node-ninjaとiconv
久々の投稿。 node-ninjaでiconvを使おうとしたら、npmがこけてしまった。 node-iconvのv2系は使えないのかな? v1.2.4なら使えた。 致命的な不具合がないことを祈りつつw

@theartofmadeline
Cosmic Funnies
Peter Solarz
art blog(derogatory)
Show & Tell
Sade Olutola
Acquired Stardust

roma★
Keni
Misplaced Lens Cap

Kiana Khansmith
occasionally subtle
ojovivo
cherry valley forever
let's talk about Bridgerton tea, my ask is open

Andulka
Jules of Nature

oozey mess
hello vonnie
Lint Roller? I Barely Know Her
seen from Australia
seen from Kazakhstan
seen from United States

seen from Indonesia
seen from Guatemala
seen from United States

seen from Malaysia
seen from Malaysia

seen from United States
seen from United States
seen from United States
seen from United States

seen from United States
seen from United States
seen from United States
seen from United States
seen from United States
seen from United States

seen from Malaysia
seen from Australia
@weekendcarpentry-blog
node-ninjaとiconv
久々の投稿。 node-ninjaでiconvを使おうとしたら、npmがこけてしまった。 node-iconvのv2系は使えないのかな? v1.2.4なら使えた。 致命的な不具合がないことを祈りつつw

Anya is live and ready to show you everything. Watch her strip, dance, and perform exclusive shows just for you. Interact in real-time and make your fantasies come true.
Free to watch • No registration required • HD streaming
textareaにペーストした文字列を取得する
いきなりコード。
<![CDATA[// ペーストを監視したいtextareaを取得 var textarea = document.getElementsByTagName()[0]; textarea.onpaste=function (e){ // onpaste実行。この時はまだペーストされていない。 // textareaと同じ var t=e.target; // onpaste発生時のカーソルの位置を取得。 // これからペーストされる位置にカーソルがあるはず var start=t.selectionStart; // ちょっと(22行目の10msec)遅れて、関数を実行。 // 実行時にはペーストが完了している。 setTimeout(function(){ // ペースト完了直後のカーソルの位置を取得。 // ペースト文字列の末尾にカーソルがあるはず var end=t.selectionStart; // ペースト前後のカーソルの位置の間がペースト文字列のはず var val=t.value.substring(start,end); // あとは煮るなり焼くなりいかようにも console.log(val); },10); };]]>
ほとんどコメントに書いたけど、 10msecという時間がペースト処理完了までに十分か、という懸念はある。 自分の環境ではコレでうまくいっているので、正直わからない あといくつか「はず」がある。
IEと他のブラウザで異なる挙動の一つに、 Document.getElementsByNameがある。
これは、
<![CDATA[document.getElementsByName()[0];]]>
と
<![CDATA[document.getElementById();]]>
が同じ結果になる、という謎仕様。
先日、社内システムが何故IEでしか動作しないか調べていたら、 コレにぶちあたった。
なので今回はこの違いをUserscriptで吸収してみた。
という訳でコード。
<![CDATA[ document.getElementsByNameOrig=document.getElementsByName; document.getElementsByName=function(name){ var a=document.getElementsByNameOrig(name); if(a.length==0){ a.push(document.getElementById(name)); } return a; }; ]]>
やっているのは、Document.getElementsByNameを上書き。 上書き後のDocument.getElementsByNameの挙動は、
まずは通常のDocument.getElementsByName通り
1.の結果が空なら、Document.getElementByIdを実行して結果を追加
になる。 ただし、 <![CDATA[ // ==UserScript== // @name ほげほげ〜 // @namespace https://fuga.fuga/ : // @run-at document-start // @grant none // ==/UserScript==]]>
みたいに@run-at属性に、document-startを付けて、 他のスクリプトが実行される前に、上記上書き処理が走らないといけない。
確認している回避環境は Firefox+Greasemonkey Chromium+Tampermonkey # Safari+NinjaKitは未検証
Chromium(v37以降)のポリシー変更
Chromiumがv37に上がってshowModalDialog APIがdeprecatedになったが、 Mac OSでの対応例が、
<array> <string>ShowModalDialog_EffectiveUntil20150430</string> </array>
と何の事かわからない記述だったが、イロイロ探った結果、 こいつの事だったらしいので、まとめておく。
Appleのサイトから、Workgroup Managerをダウンロードしインストールする
インストールしたWorkgroup Managerを起動し、 「Address」に「localhost」、 「User Name」に「administrator」、 「Password」に管理者パスワード を入れて、「Connect」ボタンを押す ※一瞬フリーズしかけるが、我慢して待つ
起動した後、左側のペインでポリシーを変更したいユーザを選択して、ツールバーの「Preferences」を押し、『Details』タブを選ぶ
左下の「+」を押してポリシーを変更したいアプリ(今回はChromium)を選ぶ
「Always」を選択した状態で「New Key」ボタンを押す
「New Item」という項目ができるので、プルダウンで「Enable deprecated web platform features」を選択する
選択したまま再度「New Key」 ボタンを押し、valueに「ShowModalDialog_EffectiveUntil20150430」を入れる
「Done」ボタンを押して設定内容を保存したあと、 一旦ログアウトして再ログインする
多分コレでいける
久々の投稿。
最近、社内でもブラウザで動くシステムが増えてきている。 でも、使い勝手が悪い、クロスブラウザが考慮されてない、など、 もの申したいことも度々。
そんな時、まず最初に 『それ、BookmarkletとかUserScriptでなんとかなるんじゃない?』 と考えてしまうほど、UserScript厨化が進行している。
まぁ、実際できるんだから、いいじゃん。
で、実際UserScriptを書くときは、 Chromeの拡張機能で、UserScript実行環境でありエディタでもあるTampermonkeyを使っている。
これでデバッグ用コードを書いて動作検証した後に、 GoogleのClosure Compilerでコード圧縮してから配布/公開、 というのが、最近の基本的な流れ。
で、作業がルーチン化すると、人間どうしても、 その作業を全自動/半自動化したくなるもので。
今回は、今までTampermonkeyで書いてたコードをclosurecompilerにコピペして圧縮してたルーチン作業を、 Tampermonkeyから直接実行できるようにしてみた。 これはBookmarkletで。 ホントは、UserScriptで圧縮用ボタンでも追加したかったが、 さすがに「chrome-extension://」で始まる画面でUserScriptは実行できないらしいので
以下、コード。
<![CDATA[ (function(){ var mask; var compile=function(js_code){ var metas=js_code.match(/\/\/ ==UserScript==[\w\W]*\/\/ ==\/UserScript==\n/g); var xhr=new XMLHttpRequest(); xhr.open("POST", "http://closure-compiler.appspot.com/compile"); xhr.onreadystatechange=function(){ if (xhr.readyState==4&&xhr.status==200){ var textarea=mask.getElementsByTagName("textarea")[0]; textarea.innerHTML=(metas!=null?metas[0]+"\n":"")+JSON.parse(this.responseText).compiledCode; mask.getElementsByTagName("div")[1].style.display="inline"; mask.getElementsByTagName("input")[0].onclick=function(){ d.body.removeChild(mask); }; textarea.focus(); textarea.select(); } }; xhr.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded;charset=UTF-8'); var encoded=encodeURIComponent(js_code); xhr.send("compilation_level=SIMPLE_OPTIMIZATIONS&output_info=compiled_code&output_info=warnings&output_info=errors&output_info=statistics&compilation_level=SIMPLE_OPTIMIZATIONS&warning_level=default&output_format=json&output_info=compiled_code&js_code="+encoded); } var d=document; try{ mask=d.createElement("div"); mask.innerHTML='<div style="width: 100%; height: 100%; position: fixed; left: 0px; top: 0px; opacity: 0.7; background-color: black;"><div style="width: 80%; height: 80%; position: fixed; top: 0px; right: 0px; bottom: 0px; left: 0px; margin: auto;display:none" align="right"><textarea style="font-size:12px;width: 100%; height: 100%;background-color: white;" readonly=""><input type="button" value="閉じる">'; mask.id="mask"; mask.style.cssText="width:100%;height:100%;position:fixed;left:0;top:0;z-index:9999;"; d.body.appendChild(mask); if(location.href.indexOf('chrome-extension://dhdgffkkebhmkfjojejmpbldmpobfkfo/')==0) { var at=(function(){var tabs=d.getElementsByClassName("tv_content");for(var i=0;i<tabs.length;i++){if(tabs[i].id.indexOf("_details_c")>=0&&tabs[i].style.display!="none")return tabs[i]}return null;})(); var a=at.getElementsByClassName("CodeMirror-scroll")[0]; var b=$(at.getElementsByClassName("CodeMirror-editor")[0]); var c=0,cl=0; var allsrc=[]; var pos=a.scrollTop; if(pos==0)a.scrollTop=1; var onscroll=function(){ var s=at.getElementsByClassName("CodeMirror-scroll")[0].innerText; var lines=s.match(/(?:\n|^)[1-9]{1}\d*/g); if(cl==parseInt(lines[lines.length-1])){ a.removeEventListener("scroll",onscroll); a.scrollTop=pos; compile(allsrc.join('\n')) return; } cl=parseInt(lines[lines.length-1]); c=a.scrollTop; var m=0; var n=at.getElementsByClassName("CodeMirror-scroll")[0].innerText.replace( /(?:\n|^)([1-9]\d*)\n([^\n]*)/g, function(a,b,c){ if(m==0)m=parseInt(b); return "@"+b+","+c+""; }); var src=n.split(/\@[1-9]\d*,/); src.forEach(function(e,i,a){ if(allsrc[i+m-1]==null)allsrc[i+m-1]=e; }); var before=a.scrollTop; a.scrollTop+=b.height(); var after=a.scrollTop; if(before==after)onscroll(); } a.addEventListener("scroll", onscroll); if(a.scrollTop==0)onscroll(); else a.scrollTop=0; } }catch(e){ if((mask=d.getElementById("mask"))!=null){ d.body.removeChild(mask); } } })();]]>
前半は、Closure Compilerに投げる関数の宣言で、 これはREST APIがあるので、XMLHttpRequestでPOSTするだけ。
Tampermonkey固有のコードは30行目付近から。 面倒だったのは、Tampermonkeyのエディタから、表示中のソースコードを取得する部分。
どうもこのエディタ、どこかの要素に全てのソースコードを保持してる訳ではなく、 表示されている部分と前後±αしか保持してなく、 残りはスクロールする度にロードされていた。
なので、上記のコードでは、一旦一番上までスクロールして、 自動でスクロールしながら、上の行から順番に配列に確保し、 最後にArray.joinで(改行コードを挟みながら)連結している。
ソースコードが出来上がれば、後はClosure Compilerに投げるだけ。
このコード自体をClosure Compilerで圧縮して、 Bookmarkletとして使っている。
一応(何がなんだかわからない)圧縮版も。 <![CDATA[javascript:(function(){var c,m=function(b){var h=b.match(/\/\/ ==UserScript==[\w\W]*\/\/ ==\/UserScript==\n/g),a=new XMLHttpRequest;a.open("POST","http://closure-compiler.appspot.com/compile");a.onreadystatechange=function(){if(4==a.readyState&&200==a.status){var b=c.getElementsByTagName("textarea")[0];b.innerHTML=(null!=h?h[0]+"\n":"")+JSON.parse(this.responseText).compiledCode;c.getElementsByTagName("div")[1].style.display="inline";c.getElementsByTagName("input")[0].onclick=function(){d.body.removeChild(c)}; b.focus();b.select()}};a.setRequestHeader("Content-Type","application/x-www-form-urlencoded;charset=UTF-8");b=encodeURIComponent(b);a.send("compilation_level=SIMPLE_OPTIMIZATIONS&output_info=compiled_code&output_info=warnings&output_info=errors&output_info=statistics&compilation_level=SIMPLE_OPTIMIZATIONS&warning_level=default&output_format=json&output_info=compiled_code&js_code="+b)},d=document;try{if(c=d.createElement("div"),c.innerHTML='<div style="width: 100%; height: 100%; position: fixed; left: 0px; top: 0px; opacity: 0.7; background-color: black;"><div style="width: 80%; height: 80%; position: fixed; top: 0px; right: 0px; bottom: 0px; left: 0px; margin: auto;display:none" align="right"><textarea style="font-size:12px;width: 100%; height: 100%;background-color: white;" readonly=""><input type="button" value="\u9589\u3058\u308b">', c.id="mask",c.style.cssText="width:100%;height:100%;position:fixed;left:0;top:0;z-index:9999;",d.body.appendChild(c),0==location.href.indexOf("chrome-extension://dhdgffkkebhmkfjojejmpbldmpobfkfo/")){var e=function(){for(var b=d.getElementsByClassName("tv_content"),a=0;a<b.length;a++)if(0<=b[a].id.indexOf("_details_c")&&"none"!=b[a].style.display)return b[a];return null}(),a=e.getElementsByClassName("CodeMirror-scroll")[0],n=$(e.getElementsByClassName("CodeMirror-editor")[0]),k=0,g=[],l=a.scrollTop; 0==l&&(a.scrollTop=1);var f=function(){var b=e.getElementsByClassName("CodeMirror-scroll")[0].innerText.match(/(?:\n|^)[1-9]{1}\d*/g);if(k==parseInt(b[b.length-1]))a.removeEventListener("scroll",f),a.scrollTop=l,m(g.join("\n"));else{k=parseInt(b[b.length-1]);var c=0;e.getElementsByClassName("CodeMirror-scroll")[0].innerText.replace(/(?:\n|^)([1-9]\d*)\n([^\n]*)/g,function(a,b,d){0==c&&(c=parseInt(b));return"@"+b+","+d+""}).split(/\@[1-9]\d*,/).forEach(function(a,b,d){null==g[b+c-1]&&(g[b+c-1]=a)}); b=a.scrollTop;a.scrollTop+=n.height();b==a.scrollTop&&f()}};a.addEventListener("scroll",f);0==a.scrollTop?f():a.scrollTop=0}}catch(p){null!=(c=d.getElementById("mask"))&&d.body.removeChild(c)}})();]]>

Anya is live and ready to show you everything. Watch her strip, dance, and perform exclusive shows just for you. Interact in real-time and make your fantasies come true.
Free to watch • No registration required • HD streaming
VPNの接続状態を検知する
ふとAndroid(ICS以降)でVPN接続状態を検知したくなったので、そのやり方を調べた。 Stack Overflowによると、 DNSサーバの設定を監視してろよ、だったんだが、 もしかしたらもうちょっと簡単にできるかも。 まずはコード。 <![CDATA[ new Thread(new Runnable(){ @Override public void run() { while(true) { try { Thread.sleep(1000); ArrayList<String> nics = new ArrayList<String>(); Process process = Runtime.getRuntime().exec("ls /sys/class/net | grep tun"); BufferedReader in = new BufferedReader( new InputStreamReader(process.getInputStream()) ); String nic = null; while((nic=in.readLine()) != null){ nics.add(nic); nic = null; } if(nics.size()>0) { // vpn connected } } catch (IOException e) { e.printStackTrace(); nics = null; } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); ]]> ようするに、定期的にlsコマンドでインターフェース一覧を取得して、 その中から「tun」が頭につくインターフェースを取ってきているだけ。 コレが本当に正しいのか、どの機種でもできるのかは不明。 Nexus7とGalaxyNexusの2機種では動いた。
android update project --path .
Eclipseでプロジェクトを作ったあとに、 やっぱりAntでビルドしたくなった場合のコマンド。
Tumblrでjavascriptを実行する
基本的には、HTMLモードで<script>タグを書くだけで、 いろんなスクリプト(全部かは知らん)が実行可能。
スクリプトの実行に外部ライブラリが必要な場合は、 「カスタマイズ」の「説明」欄に読み込む為の<script>タグを書く。
こういう風にスクリプトを読み込めば、 公開ライブラリを使えるようになる。
例えばThree.jsを使った例。
var elms=$(".stlviewer");for(var i=0;i<elms.length;i++){var elm = elms[i];new STLViewer(elm, {droppable:true, showGround:true, navigatorStyle:"position:absolute; width:100px; height:100px; top:0px; right:0px;"}).load($(elm).attr("stlUrl"));$(elm).removeClass("stlviewer")}
XMLHTTPRequestの実装差
二つ目の投稿にして、いきなりニッチなんだがw
ちょっと前の話、
デスクトップからブラウザにドラッグ&ドロップしたSTLファイルを サーバにアップせずに、Three.jsを使ってブラウザで表示する
なんてことしていた。
その時にぶちあたったのが、
Chrome/Safariではすんなり開けるが、 Firefoxでは開けない
と言うありがちなブラウザ互換性問題。
「IEではできない」はよく見るけど、「Firefoxではできない」? なにやらめんどくさそう。
という訳で、どこでFirefoxが処理を投げ出しているのか、追いかけてみた。
最初にChromeで動いて、てんぐになっていたコードはこんな感じ。
var loader = new THREE.STLLoader(); var droppable=document.getElementById("target");//ドロップ先 droppable.bind("drop", function(event){ var file = event.originalEvent.dataTransfer.files[0]; loader.addEventListener("load", function(e) { // STL読み込み完了時の処理 // e.contentからMeshを作ってSceneに貼る、とか。 }); loader.load(window.URL.createObjectURL(file)); event.preventDefault(); event.stopPropagation(); return false; });
Chrome/Safariだと呼ばれるloadイベントのリスナーが、Firefoxだと見事にに呼ばれない。
どうやら、STLLoaderのloadメソッドの中では、jQueryのajaxメソッドが使われていて、 さらにコレがXMLHTTPRequestを呼んでいる。
一方で、FileAPIから取れるオブジェクトURLは、”blob:”スキーム。
このblobスキームのURLをXMLHTTPRequestが処理できるかどうか、が今回の分かれ道の様子。
Chrome(V8)/Safari(JavaScriptCore?)のXMLHTTPRequestの実装はblobスキームを処理できるが、 Firefox(SpiderMonkey?)のXMLHTTPRequestはblobスキームを処理できない、のようす。 (困る人は少ないだろうが)
なのでこの時はSTLLoaderのloadメソッドを使わずに読み出すよう、ドロップ後の処理を変更。 直したコードはこんな感じ。
var loader = new THREE.STLLoader(); var droppable=document.getElementById("target");//ドロップ先 droppable.bind("drop", function(event){ var file = event.originalEvent.dataTransfer.files[0]; var reader = new FileReader(); reader.onload = (function(theFile) { return function(e) { // "data:application/sla;base64,...."というdata-scheme形式でファイルが取れる var data=reader.result; // base64エンコードされた部分だけ取り出す var contentBase64=data.split("base64,")[1]; // base64デコードする var contentBin=window.atob(contentBase64); // STLLoaderのparse関数だけを使う var content=loader.parse(contentBin); // ここまできてSTLの読み込みが完了 // あとはcontentからMeshを作ってSceneに貼る、とか。 }; })(file); reader.readAsDataURL(file); event.preventDefault(); event.stopPropagation(); return false; });
巷に公開されているライブラリは大変便利。 でも実開発者が想定していた使い方と自分の使い方が全く同じとは限らない。 注意、注意。 (今回は、ネット越しのデータではなく、ドロップされたデータからロード)

Anya is live and ready to show you everything. Watch her strip, dance, and perform exclusive shows just for you. Interact in real-time and make your fantasies come true.
Free to watch • No registration required • HD streaming
javascriptコードが。。。
先のjavascriptコードを書いたポスト、 公式Androidアプリから見ると、 コードが一行になって見にくい。
Tween.jsでオブジェクトを移動させる
Tween.jsの使い方。 まずはサンプルをそのまま。
var tween = new TWEEN.Tween( { x: 50, y: 0 } ) .to( { x: 400 }, 2000 ) .easing( TWEEN.Easing.Elastic.InOut ) .onUpdate( function () { output.innerHTML = 'x == ' + Math.round( this.x ); var transform = 'translateX(' + this.x + 'px)'; output.style.webkitTransform = transform; output.style.transform = transform; } ) .start();
Tweenのコンストラクタで、操作したいプロパティを持ったオブジェクトを渡して、 toメソッドで、操作したいプロパティの名前とゴールの値、 あとアニメーションにかかる時間を指定して、 easingメソッドで、アニメーションのタイプを指定して、 onUpdateメソッドで、値が更新されるたびに呼ばれるコールバック関数を指定して、 startで実行。
で、実際に使ってみた。 やってみたのは、Three.jsのカメラをTweenを使って、 ボタンクリックで初期位置に移動させること。
こんな状況です。
最初は直接カメラの座標を操作してみた。その時のコードはコレ。 # コードを簡単にする為に2次元にした。カメラ初期位置は(20,0)
var tween = new TWEEN.Tween( camera.position ) .to( { x: 20, y:0 }, 500 ) .easing( TWEEN.Easing.Linear.None ) .start();
コレだと、アニメーションの最中に物体に一度ズームしてしまう。
そりゃそうだ。
円周上の一点から別の点に移動するとき、 その点を結んだ直線上を移動しているので、 物体とカメラの間の距離が、一度近づいているから。
なので、ちょっと工夫。 # 工夫、つーか、当然の処理なんですが。
カメラと物体の間を一定に保てば、ズームせずに回転だけするはず。 実際には、カメラのpositionプロパティ(直交座標系)を直接操作するのはやめて、 いったん極座標系に直してアニメーションを実行する。 こうすることで、物体とカメラの距離は一定に保てる。
実際やってみらうまくいった。 コードはこんな感じ。
var rectp=camera.position; var polarp = { r:Math.sqrt(rectp.x*rectp.x+rectp.y*rectp.y), theta:rectp.x!=0?Math.atan(rectp.y/rectp.x):(rectp.y>0?Math.PI/2:-Math.PI/2) }; var tween = new TWEEN.Tween( polarp ) .to( { r: 20, theta:0 }, 500 ) .easing( TWEEN.Easing.Linear.None ) .onUpdate(function(){ camera.position.x = this.r*Math.cos(this.theta); camera.position.y = this.r*Math.sin(this.theta); }) .start();
onUpdateの中では、 thisがアニメーションの対象オブジェクト(polarp)になっているので、 thisから現状の極座標系の座標を取り出し、 それを直交座標系の座標を計算しなおし、 カメラの座標を更新している。
今回は距離が変わらないケースだったが、 カメラをズームイン/アウトさせた後に初期位置に戻るケースでも、 直線で移動させるより、回転させつつ戻った方が、自然に見える。
自己紹介
はじめまして。 Tumblrを(今更ながら)始めました。
これは、某メーカーにつとめる達観系アラフォー男子が、 趣味レベルのソフト開発メモを書くミニブログです。
kあた仕事は、(メーカー勤めなのに)商品開発とは直結してなくて、 人間観察したり、アイデア出したり、それを即興で形にしたり、と なんでもござれ、な仕事ですが、 形にするところでソフトの知識を使ったりしてます。
メインはJava(Android)、Javascript(HTML5/Node.js)ですが、 昔はC++もVB6(.NETではない)もいじってました。
以後お見知り置きを。
(コレが最後の投稿にならないといいですが。。。)