SilverlightでJavaScriptのライブラリを書く
メモ。
クラスにScriptableTypeAttributeを設定する
ScriptableTypeAttribute Class (System.Windows.Browser) | Microsoft Docs
<Browser.ScriptableType()> _ Public Class Library1 Dim _aaa As Integer = 1 Public Property aaa() As Integer Get Return Me._aaa End Get Set(ByVal value As Integer) _aaa = value End Set End Property Public Function bytesFromBase64(ByVal s As String) As Byte() Return Convert.FromBase64String(s) End Function Public Function fromBase64(ByVal s As String) As String Dim bytes() As Byte = Me.bytesFromBase64(s) Return Text.Encoding.UTF8.GetString(bytes, 0, bytes.Length) End Function Public Function toBase64(ByVal s As String) Return Convert.ToBase64String(Text.Encoding.UTF8.GetBytes(s)) End Function End Class
Dim lib1 As New Library1 window.SetProperty("Lib1", lib1)
Firebugからはフィールドが見えない。
ScriptObjectに追加していく
ScriptObjectにメソッドをそのまま追加するのは無理そうだけど、プロパティにラムダ式で書けばできそう。
Public Class Library2 Public bytesFromBase64 As Func(Of String, Byte()) = Function(s As String) Convert.FromBase64String(s) Public toBase64 As Func(Of String, String) = Function(s As String) Convert.ToBase64String(Text.Encoding.UTF8.GetBytes(s)) Public fromBase64 As Func(Of String, String) = Function(s As String) Me._fromBase64(s) Public Function _fromBase64(ByVal s As String) As String Dim bytes() As Byte = Me.bytesFromBase64(s) Return Text.Encoding.UTF8.GetString(bytes, 0, bytes.Length) End Function End Class
Dim lib2 As New Library2 Dim instance As Browser.ScriptObject = window.CreateInstance("Object") Dim fields() As Reflection.FieldInfo = lib2.GetType.GetFields For Each field As Reflection.FieldInfo In fields instance.SetProperty(field.Name, field.GetValue(lib2)) Next window.SetProperty("Lib2", instance)
Firebugからも見える。
Canvasでバーコードを読む
JANの仕様
ここが分かりやすい。→ バーコードの作り方
プログラミングでの話
まずは、
- バーコードの部分だけ写ってる写真を用意する
- 高さ1px分どこかを切り取る
- コントラスト調整
- 2値化
- ビットの文字列にする
- モジュールの幅を調節
- 左右のガードバーからスケールを推測
- ガードバー部分を削除
みたいな準備をします。結構写真写りに左右されるかも。
13桁の数字のうち、画像として表現されているのは2-13文字目までの12文字で、最初の1文字目は2-7文字目によって判別します。なので、とりあえず2文字目以降を判別します。対応表は下みたいな感じ。
{ left: { odd: { "0001101": 0, "0011001": 1, "0010011": 2, "0111101": 3, "0100011": 4, "0110001": 5, "0101111": 6, "0111011": 7, "0110111": 8, "0001011": 9 }, even: { "0100111": 0, "0110011": 1, "0011011": 2, "0100001": 3, "0011101": 4, "0111001": 5, "0000101": 6, "0010001": 7, "0001001": 8, "0010111": 9 } }, right: { "1110010": 0, "1100110": 1, "1101100": 2, "1000010": 3, "1011100": 4, "1001110": 5, "1010000": 6, "1000100": 7, "1001000": 8, "1110100": 9 } }
leftは2-7文字目を, rightは8-13文字目に対応しています。left部分のodd, evenの組み合わせで1文字目が決まってきます。対応表は下みたいなの。
{ "oooooo": 0, "ooeoee": 1, "ooeeoe": 2, "ooeeeo": 3, "oeooee": 4, "oeeooe": 5, "oeeeoo": 6, "oeoeoe": 7, "oeoeeo": 8, "oeeoeo": 9 }
これで全部の数字が分かるようになりました。不安なときは13文字目でチェックできます。多分こんな感じで。
function checkDigit(nums){ var n = 0; for(var i = 0; i < 12; ++i) n += nums[i] * ((i % 2 == 0)? 1 : 3); return 10 - (n % 10) == nums[12]; }
デモなど
http://arikui.github.com/ip/barcode.html
Firefox3でのみ確認。何回かボタン押せば出る。
ImageProcessing.jsのモジュールはここに。
GitHub - arikui/image_processing.js: JavaScript library for canvas
Shellなプロトコル
中身がまったく別物になってきたので別のGistにしました。ちゃんと動いてる気がする。
http://gist.github.com/69913
普通の実行
cmd:command
例「cmd:ruby -e "puts 1"」
ファイルにコードを保存して実行
cmd:command;code;args
例「cmd:vbs; WSH.Echo(WSH.Arguments(0) + WSH.Arguments(1)); "1" "2"」
JavaScriptのevalを通して実行
cmd:-e command;code;args
command, code, argsをJavaScriptのコードとして評価してから実行します。
例「cmd:-e "echo " + (100 * 100)」
コード
Firefox3でWSHプロトコル
Firefox3のXPCOMUtilsを使うと、短いコードで独自プロトコルを登録できるんですね。
XPCOMUtilsを使わない方法との比較
使わない方法は以下のリンクを参考に。
QueryInterfaceをあんまり書かなくてよくなった
XPCOMUtils.generateQIが作ってくれます。
ProtocolFactoryを書かなくてよくなった
勝手に作ってくれます。自分で定義したい場合は、_xpcom_factoryに書きます。
TestModuleを書かなくてよくなった
XPCOMUtils.generateModuleが勝手にやってくれます。
オレオレプロトコルを書く
以下の必須っぽいものを実装していきます。
classDescription, classID, contractID
XPCOMUtils.generateModuleの中で参照されるものです。全部揃ってないと登録されません。contractIDは、"@mozilla.org/network/protocol;1?name=" + scheme みたいなものになります。
QueryInterface
XPCOMUtils.generateQI([Ci.nsIProtocolHandler])
scheme
登録するスキーム。
newChannel
実際の動作の中身です。ちゃんとnsIChannelを返すようにしないと、Firefoxが落ちるようです。
その他
defaultPort, protocolFlags, allowPort, newURIはコピー・アンド・ペーストで。
コード
Oretocolの中身を変えればいいようにしています。WSHを実行するのにTomblooを使用。「wsh:return 1」と書かないといけないのがちょっとダサい。
const Ci = Components.interfaces; const Cc = Components.classes; Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); var Oretocol = { scheme : "wsh", description: "WSH Protocol", id : Components.ID("F32C80B3-404B-4ce0-8590-B9DC25BDFA9A"), newChannel : function(aURI){ var T = Components.classes['@brasil.to/tombloo-service;1'].getService().wrappedJSObject; var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); var que = decodeURIComponent(aURI.spec).substr(Oretocol.scheme.length + 1); eval("var fun = function(){" + que + "}"); T.log(T.executeWSH(fun)); return ios.newChannel("javascript:alert((" + fun.toSource() + ")())", null, null); } }; function Protocol(){} Protocol.prototype = { classDescription: Oretocol.description, contractID : Oretocol.contractID, classID : Oretocol.id, contractID : "@mozilla.org/network/protocol;1?name=" + Oretocol.scheme, QueryInterface : XPCOMUtils.generateQI([Ci.nsIProtocolHandler]), scheme : Oretocol.scheme, defaultPort : -1, protocolFlags: Ci.nsIProtocolHandler.URI_NORELATIVE | Ci.nsIProtocolHandler.URI_NOAUTH, allowPort: function(port, scheme){ return false; }, newURI: function(spec, charset, baseURI){ var uri = Cc["@mozilla.org/network/simple-uri;1"].createInstance(Ci.nsIURI); uri.spec = spec; return uri; }, newChannel: Oretocol.newChannel } function NSGetModule(compMgr, fileSpec){ return XPCOMUtils.generateModule([Protocol]); }
そういえば、ios.newChannel("javascript:なんとか", null, null)が全然動かなかったような。
インストール
「Latest topics > Firefoxで独自プロトコルを定義する方法 - outsider reflex」の一番下の記述のとおり。ただし、Profilesディレクトリ(WindowsならC\Documents and Settings\ユーザ名\Application Data\Mozilla\Firefox\Profiles\なんたらかんたら)にあるcompreg.datというファイルを削除してから再起動しないと読み込まれないかもしれません。
「about:何とか」も簡単に作れる
Firefoxをインストールしたところのcomponentsディレクトリ(C:\Program Files\Mozilla Firefox\components)にある「aboutなんとか.js」を参考に。
追記
このままだと、どこからでもオレオレプロトコルを実行できてしまい、外部からのヤバイコードも実行してしまいますね。以下のように変更すれば多分大丈夫です。
protocolFlags: Ci.nsIProtocolHandler.URI_NORELATIVE | Ci.nsIProtocolHandler.URI_NOAUTH | Ci.nsIProtocolHandler.URI_DANGEROUS_TO_LOAD,
nsIProtocolHandler.URI_DANGEROUS_TO_LOAD を設定すると特権的な場所からのみ実行できるようになります。実行制限は、
URI_DANGEROUS_TO_LOAD < URI_IS_UI_RESOURCE < URI_LOADABLE_BY_ANYONE
って感じだそうで。
参考: nsIProtocolHandler - Mozilla | MDN
まだ聴いてない曲を意識したい
友達や家族からCDを借りたり違法ダウンロードしたりしてると、あっという間にプレイリストの中の曲が膨らんできて、一体どの曲を聴いてないか困る、なんて事例がよくあります。自分もそうです。(違法ダウンロードしているという意味ではない) なので、
みたいなスマートプレイリストを作って聴いてるんですが、聴いてない曲が減ってる気がしない。いい加減ムカついてきたので、Twitterで知れるようにします。
var user = "username"; var pass = "password"; // iTunesが起動してるかチェック var WMI = GetObject("winmgmts:\\\\.\\root\\cimv2"); var processes = WMI.ExecQuery("SELECT * FROM Win32_Process WHERE Name = 'iTunes.exe'"); if(processes.Count != 1) WSH.Quit(); var iTunes = WSH.CreateObject("iTunes.Application"); // ライブラリのdont_listenが聴いてない曲のプレイリスト var playlists = iTunes.Sources.ItemByName("\u30E9\u30A4\u30D6\u30E9\u30EA").Playlists; var playlist = playlists.ItemByName("dont_listen"); var count = playlist.Tracks.Count; post(count); //WSH.Echo(count); function post(text){ var XmlHttp = WScript.CreateObject('MSXML2.XMLHTTP'); XmlHttp.open('POST', "http://twitter.com/statuses/update.json", false, user, pass); XmlHttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); XmlHttp.send('status=' + encodeURIComponent(text)); }
音楽聴いてないのにクソ重いiTunesがいちいち起動するのも腹立たしいので、起動してるかチェックしてます。これをタスクスケジューラで毎日実行するようにします。拡張子jsをcscriptなどに関連付けておく必要があるかもしれないので、それが嫌な人はwsfで書いた方がいいかも。
専用のアカウントでも作ってXPathGraphなどでグラフにすれば音楽を聴く意欲が増しますね。
vb.jsの日記 : For Next
人気のプログラミング言語、VisualBasicっぽく書けるJavaScriptライブラリを開発する日記です。
最初はFor Nextステートメントを実装したいと思います。
function For(start){ For.isExit = false; return { To: function(end){ var step = 1; var loop = function(f){ return { Next: function(){ for(var i = start; i <= end; i += step){ if(For.isExit) break; f(i); } } }; }; loop.Step = function(n){ step = n; return loop; }; return loop; } }; }; function ExitFor(){ For.isExit = true; }
For(5).To(10).Step(3)(function(i){ console.log(i); }).Next(); // 5, 8 For(5).To(10)(function(i){ if(i == 8) ExitFor(); console.log(i); }).Next(); // 5, 6, 7, 8
ExitForがダメです。ここは妥協してreturn ExitForとします。ヤケクソです。
function For(start){ return { To: function(end){ var step = 1; var loop = function(f){ return { Next: function(){ for(var i = start; i <= end; i += step){ if(f(i) == ExitFor) break; } } }; }; loop.Step = function(n){ step = n; return loop; }; return loop; } }; };
For(5).To(10)(function(i){ if(i == 8) return ExitFor; console.log(i); }).Next();
これで多少はJavaScriptにもVBのよさが出たでしょうか。Nextのような飾りが可愛らしい。
vb.jsの日記の1回目から妥協してしまったので多分続かない。
追記
monjudohさんのコメントを参考にして。
function ExitFor(){ throw arguments.callee; }
Next: function(){ for(var i = start; i <= end; i += step){ try{ f(i); } catch(e){ if(e == ExitFor) break; } } }
これでreturnしなくてもループが抜けられるんですね。
For(5).To(10)(function(i){ try{ if(i == 8) ExitFor(); } catch(e){} console.log(i); }).Next();
みたいな使い方をしなければ大丈夫。