ラベル アップデート の投稿を表示しています。 すべての投稿を表示
ラベル アップデート の投稿を表示しています。 すべての投稿を表示

2014年11月29日

encoding.js 1.0.12 リリースしました

JavaScript 文字コード変換ライブラリ encoding.js 1.0.12 リリース しました

アップデート内容

  • UTF-8 <=> JIS 変換テーブルのバグ修正 (文字化けが直りました)
  • JIS X 0208 を対象にした変換テストが通るようになりました
  • ファイルサイズが約60% 減りました
  • 実行速度が向上しました
  • convert で引数にオブジェクトが渡せるようになりました

UTF-8 <=> JIS 変換テーブルのバグ

UTF-8 から JIS, JIS から UTF-8 への変換テーブルが変なことになってました。
変換テーブルは MS 基本コードポイント (Unicode) を参照していましたが、 UTF-8 に変換する際に無効なコードポイントになってしまい文字化けが発生していました。
MS 基本コードポイントと IBM 基本コードポイントの差は以下のようになっています。

コードポイント:
文字名 IBM CP (Unicode) MS CP (Unicode) IBM CP (UTF-8) MS CP (UTF-8)
― (全角ダッシュ) U+2014 U+2015 E28094 E28095
~ (波形ダッシュ) U+301C U+FF5E E3809C EFBD9E
∥ (二重縦線) U+2016 U+2225 E28096 E288A5
- (負符号) U+2212 U+FF0D E28892 EFBC8D
U+00A2 U+FFE0 C2A2 EFBFA0
U+00A3 U+FFE1 C2A3 EFBFA1
U+00AC U+FFE2 C2AC EFBFA2

encoding.js の場合、MS コードポイントのまま変換しようとしていまい、対応する文字がなく '?' などになってしまっていました。
これは元々直したつもりでいたのですが、 encoding.js 1.0.10 Line 2973 あたりを見てみると以下のようになっていて
0xEFBC8F:0x213F,0xEFBCBC:0x2140,0xE3809C:0x2141,0xEFBD9E:0x2141,0xE288A5:0x2142,
そもそも 0x2141 が消しきれてなくて重複していたり、対応する JIS テーブルに文字がなくて変換すると 1文字分消えてしまったりと、 バグが多かったのですが
今回、IBM 基本コードポイントの差分によって変換してあげることで、ぶじ文字化けが解消されました。

JIS X 0208 を対象にした変換テストが通るようになりました

この変換テーブルの修正により、JIS X 0208-1997 (JIS規格 第一・第二水準漢字) を対象にした変換テスト (UTF-16, UTF-8, Shift_JIS, EUC-JP, ISO-2022-JP(JIS)) が通るようになりました。 また、Unicode サロゲートペア の処理も修正しています。

ファイルサイズが約60% 減りました

encoding.js では、変換テーブルだけでかなりのサイズを占めています。
前バージョンまでは、JIS→UTF-8 テーブルと UTF-8→JIS テーブルの 2つを保持していたのですが、
今回、UTF-8→JIS のみを静的に持ち、JIS→UTF-8 変換が必要になった際、動的にもう一つのテーブルを生成するようにしました。
生成されるのは呼ばれた最初の 1回のみです。
その際の速度が気になり、ベンチとってみましたが 約 2~5ms 程度で問題ないと思われます。
ファイルサイズは非minifyで 261KB (min: 219KB) だったのが 152KB (min: 116KB) になり、約60% 減らすことができました。

実行速度が向上しました

前までは、変換テーブルに文字があるかないかを、if (TABLE[c] === void 0) { ... } みたいな感じで判断していましたが、
これは非常に遅く、また in を使うより hasOwnProperty が速く、実行環境にもよりますが
var hasOwn = Object.prototype.hasOwnProperty.call.bind(Object.prototype.hasOwnProperty);
if (hasOwn(obj, key)) { ... }
のように bind して使うよりも hasOwnProperty.call(obj, key) のほうが若干速かったため、後者を採用しています。

変換速度は、1MB の日本語テキスト (UTF-8) に対して SJIS に文字コード変換した場合、平均 約 50ms. (EUC-JP, JIS 等も同程度)、
UNICODE (UTF-16) から UTF-8 とその逆は約 5ms ほどで、前よりだいぶ速くなりました。

convert で引数にオブジェクトが渡せるようになりました

convert の第二引数に Object を渡せるようになりました。
// 以前の指定の仕方 (SJISに変換)
var sjisArray = Encoding.convert(unicodeArray, 'SJIS', 'UNICODE');

// Object で指定
var sjisArray = Encoding.convert(unicodeArray, {
  to: 'SJIS',
  from: 'UNICODE'
});
前のように 第二引数を to, 第三引数を from として文字列で渡しても動きます。


文字化けという大きなバグを修正しているので、アップデートをおすすめします。

Download


Node.js では、encoding-japanese というモジュール名になっています
npm install encoding-japanese
var encoding = require('encoding-japanese');

上のようにして利用できます。

encoding.convert() は Buffer に対しても使えます(配列で返ります)

Demo

Repository




2012年4月24日

JavaScriptで文字コード変換ライブラリ作ってみた

文字コード変換ライブラリを JavaScript で作りました。
文字列ではなく配列 or TypedArray で処理します。

追記

↓動作サンプルを作りました
文字コード変換 動作サンプル

Unicode の変換が可能になりました。
文字コード配列から URLエンコード/デコード が可能になりました。
あと説明とサンプルも少し載せました。。(説明不足でごめんなさい)

こないだの 「JavaScriptだけでzipファイルの解凍 - Unzipper.js」が
SJIS ファイルとかだと表示で文字化けするので、ついつい。。

動作確認は、zip ファイル解凍のデモページでわかると思います。
zip の中に SJIS や EUC-JP のファイル (ファイル名) がある場合でも
UTF-8 表示で化けなければ問題なしです。


↑のデモページを開いて、デスクトップなどから zip ファイルをドロップすると
解凍して結果のテキストを表示します。

※ JavaScript だけで動いていて、どっかのサーバなどに送信したり保存したりしてません


今回はぜんぶ同期で書いちゃったのですが、
バイナリファイルとかが対象だとちょっと重たくなりがち。。
気が向いたら非同期も可能にしてみたいです。

あと、Unzipper.js と同じレポジトリにしちゃったけど
これもできれば別にしたいところ。
追記→ 作りました

使い方:
// <script src="encoding.js"></script>
//
// Encoding というオブジェクトがグローバルに定義されます
// 配列に対して変換または判別します

// 文字コード変換
var utf8Array = new Uint8Array(...) or [...] or Array(...);
var sjisArray = Encoding.convert(utf8Array, 'SJIS', 'UTF8');

// 文字コード自動判別で変換
var sjisArray = Encoding.convert(utf8Array, 'SJIS');
// or  
var sjisArray = Encoding.convert(utf8Array, 'SJIS', 'AUTO');


// 文字コード判別 (戻り値は下の「共通の文字コード値」のいずれか)
var encoding = Encoding.detect(utf8Array);
if (encoding === 'UTF8') {
    alert('UTF8です');
}

// 特定の文字コードかどうか判別
var isSJIS = Encoding.detect(sjisArray, 'SJIS');
if (isSJIS) {
    alert('SJISです');
}

// convert, detect 共通の文字コード値:
//  - 'UTF32'   (detect only)
//  - 'UTF16'   (detect only)
//  - 'BINARY'  (detect only)
//  - 'ASCII'   (detect only)
//  - 'JIS'
//  - 'UTF8'
//  - 'EUCJP'
//  - 'SJIS'
//  - 'UNICODE' (JavaScript Unicode String/Array)
//
// ※ (detect only) は Encoding.detect() でのみ有効 (変換はできない)
// ※ 'UNICODE' は JavaScript の Unicode コード値 (0xFF 以上の数値になりえる)
//

サンプル:
// EUCJPの文字コード配列 (中身は 'こんにちは、ほげ☆ぴよ')
var eucjpArray = [
    164, 179, 164, 243, 164, 203, 164, 193, 164, 207, 161,
    162, 164, 219, 164, 178, 161, 249, 164, 212, 164, 232
];
// UTF-8に変換
var utf8Array = Encoding.convert(eucjpArray, 'UTF8', 'EUCJP');
console.log( utf8Array );
// output: [
//   227, 129, 147, 227, 130, 147, 227, 129, 171,
//   227, 129, 161, 227, 129, 175, 227, 128, 129,
//   227, 129, 187, 227, 129, 146, 226, 152, 134,
//   227, 129, 180, 227, 130, 136
// ]
//   => 'こんにちは、ほげ☆ぴよ'

// ---------------------------------------------
// 文字コード自動判別で変換
//

// SJISの文字コード配列 (中身は 'こんにちは、ほげ☆ぴよ')
var sjisArray = [
    130, 177, 130, 241, 130, 201, 130, 191, 130, 205, 129,
     65, 130, 217, 130, 176, 129, 153, 130, 210, 130, 230
];
// Unicodeに変換
var unicodeArray = Encoding.convert(sjisArray, 'UNICODE', 'AUTO');
// 文字列にして表示
// codeToStringは、文字コード配列を文字列に変換(連結)して返す関数
console.log( Encoding.codeToString(unicodeArray) );
//
// output: 'こんにちは、ほげ☆ぴよ'
//

// ---------------------------------------------
// 文字コードの配列をURLエンコード/デコード
var sjisArray = [
  130, 177, 130, 241, 130, 201, 130, 191, 130, 205, 129,
   65, 130, 217, 130, 176, 129, 153, 130, 210, 130, 230
];
var encoded = Encoding.urlEncode(sjisArray);
console.log(encoded);
// output:
//   '%82%B1%82%F1%82%C9%82%BF%82%CD%81A%82%D9%82%B0%81%99%82%D2%82%E6'

var decoded = Encoding.urlDecode(encoded);
console.log(decoded);
// output: [
//   130, 177, 130, 241, 130, 201, 130, 191, 130, 205, 129,
//    65, 130, 217, 130, 176, 129, 153, 130, 210, 130, 230
// ]

ダウンロード



レポジトリ



その他、なにか問題等あれば
コメントやメールまたは @polygon_planet まで送っていただけるとうれしいです。



2012年3月12日

jsFiddleだらけ-JavaScriptライブラリPot.js+PotLite.jsリリースノート



Pot.js 1.15 1.16PotLite.js 1.32 1.33 リリースしました。

2012-03-13 追記:
Pot.js 1.15 と PotLite.js 1.33 はバギーなためアップデートしました。詳細
2012-03-13 時点の最新は Pot.js 1.16PotLite.js 1.34 です。最新にアップデートお願いします。。


このバージョンでは、
  • 文字列処理の高速化
  • 重くなりそうな処理を非同期化
などを適応しました。

文字列処理の高速化

先日、ふと思って String.fromCharCode を呼ばずに
U+0000 - U+FFFF の配列をあらかじめ作成して インデックスに対応させたらどうなのかなって
ベンチマークとってみました。


結果として、String.fromCharCode(c) のような
apply で配列を使わない場合、かなり高速化できました。
メモリ消費も文字列などは気にするほどじゃなかった。
String.fromCharCode.apply(null, [...]); のような場合は逆に遅くなるので
従来通り String.fromCharCode を使用しています。

(この件はあほなミスをしてて@gochoさんにつっこまれて助かりました)

重くなりそうな処理を非同期化

重くなりそうな処理とは、巨大な文字列が渡される可能性のある関数や、
ループ回数が未知の処理などです。

途中で「応答のないスクリプト」警告なんぞ でてしまったらめんどうです。
そういった対処や負荷軽減も含めて 関数オブジェクトに deferred というメソッドを持たせました。

例えば Pot.md5(string); が同期実行に対して
Pot.md5.deferred(string); は、非同期で実行します。


圧縮・解凍

文字列を LZ77 アルゴリズムをベースに圧縮・解凍する
Pot.Archive.AlphamericString も同様に非同期化しています。

その他のサンプル

他にもいくつかテスト用にサンプルがあったので紹介します。



Pot.js / PotLite.js

Pot.js は CPU に負荷をかけることなく JavaScript の実行を可能とするユーティリティライブラリです。

PotLite.js は Pot.js の非同期な部分だけを抽出したライトバージョンです。

ダウンロード

マニュアル

その他の情報についてはマニュアル/マニュアルからのリンク から参照ください。

レポジトリ




その他、なにか問題・バグ・感想・指摘などあれば、
コメントやメールまたは @polygon_planet まで送っていただけるとうれしいです。


2012年2月24日

同期処理を非同期に変換-JavaScriptライブラリPot.js+PotLite.jsリリースノート






Pot.js 1.14PotLite.js 1.31 リリースしました。



その前に Pot.js + PotLite.js リファレンス が完成しました (ということにしたい) ので、
お時間あるときにでも覗いてみてください。

結局 /test-for-new-design/ に置いてあるのはファイル消すのも嫌だし
全部リダイレクトさせるようにしました。



Pot.js 1.14 と PotLite.js 1.31 では、Pot.deferreed() ていう関数つくりました。

deferreed は、関数の中の for, for-in, for-of, do, while 等の同期ループを
Pot.Deferred.forEach() などの非同期イテレータに置き換えて関数を再定義します。

前の記事「JavaScript whileループとPot.Deferred.forEverイテレータでCPU使用率を比較」 でも書いてますが、
Pot.js ライブラリのイテレータは CPU の負荷なく実行できます。

ループを置き換えることで、巨大な文字列を扱ったり、ループ回数が未知な処理をする際に
重さについて考えなくて済むようにと、作ってみまみた。

もともとは deferrize() という関数から派生しています。

関数内のコードをトークン単位に分解して、
単純にトークンの数が多いループブロックをメインループとして解析して、
その文に見合った Pot.Deferred イテレータを割り当ててるんですが
ぜんぜん思ったよりたいへんで まだ複雑なものは対応できてないです。
とくに三項演算子 (?:) と function のコンボとか、E4X とかまでは今後の課題。。 にしたいけど、
そこまでしてたらライブラリの方向性違うんじゃないのって思ってしかたないです。

deferreed の 1 コールで zip.deflate 圧縮とかファイル規模への md5 とか非同期化できたら楽そう。

例えば以下のような関数が

// 文字列を charCode からなる配列にして返す関数
var toCharCode = function(string) {
    var result = [];
    for (var i = 0; i < string.length; i++) {
        result.push(string.charCodeAt(i));
    }
    return result;
};

// deferreed により for 文を非同期イテレータにした関数を生成
var toCharCodeDefer = Pot.deferreed(toCharCode);

// 例えば toCharCodeDefer は内部で以下のような感じに変換される
//
//  function(string) {
//      var result = [];
//      return Pot.Deferred.repeat(string.length, function(i) {
//          result.push(string.charCodeAt(i));
//      }).then(function() {
//          return result;
//      });
//  };
//

// Pot.Deferred インスタンスが返るため then() などで繋げられる
toCharCodeDefer('abc').then(function(res) {
    Pot.debug(res); // [97, 98, 99]
});

// この変化により、巨大な文字列でも負荷を分散させ実行できる
var largeString = new Array(100000).join('abcdef');

// 'slow' などの指定が可能
toCharCodeDefer.slow(largeString).then(function(res) {
    Pot.debug(res.length); // 599994
});

あと、プラグインみたいなことしたくて Pot.Plugin ての作ったんですが
なにか違うような気がしてならないです。

プラグイン = ただのオブジェクト
になってしまった。

Pot.addPlugin() という関数で任意の関数とかオブジェクトを登録。
Pot.removePlugin() で削除。
Pot.hasPlugin() で確認、Pot.listPlugin() で列挙。

addPlugin した関数は Pot.xxx でアクセスできる。
・・・それって Pot.myFunc = function() {}; でいいじゃん! てなったので、
以下のようにしてみました。
var string = '\t abc\n \t ';

// オリジナルの Pot.trim()
debug(Pot.trim(string)); // 'abc'

// プラグイン関数の登録により Pot.trim() を上書き
Pot.addPlugin('trim', function(s) {
    return s.replace(/^ +| +$/g, ''); // スペース (U+0020) だけ削除するようにする
});

// 登録した Pot.trim()
debug(Pot.trim(string)); // '\t abc\n \t'

// プラグインの trim を削除
Pot.removePlugin('trim');

// 元に戻る
debug(Pot.trim(string)); // 'abc'
元に戻せる (むしろ消えない) ので、
一時的に挙動を変えたい時とか便利かもしれないです。
あと、Pot.globalize() したときに プラグイン関数もグローバル化します。
Pot.globalize() しなくても with (Pot) {...} でもいい気がしてきました。

プラグインの詳細は
Pot.Plugin リファレンス あたりから参照ください。

マニュアルは英語版も含めてちょっとずつ進めています。


以下はレポジトリとかのリンクなど。

Pot.js / PotLite.js

Pot.js は CPU に負荷をかけることなく JavaScript の実行を可能とするユーティリティライブラリです。
PotLite.js は Pot.js の非同期な部分だけを抽出したライトバージョンです。

ダウンロード

マニュアル

その他の情報についてはマニュアル/マニュアルからのリンク から参照ください。



レポジトリ



その他、なにか問題・バグ・感想・指摘などあれば、
コメントやメールまたは @polygon_planet まで送っていただけるとうれしいです。


2012年2月11日

JavaScript whileループとPot.Deferred.forEverイテレータでCPU使用率を比較-Pot.js+PotLite.jsリリースノート

Pot.js 1.13PotLite.js 1.30 リリースしました。

Pot.js 1.13 と PotLite.js 1.30 では、主に内部ループ処理を大幅に高速化しました。
(そろそろ ChangeLog 作らないとまずいかも…)

あとは、変数宣言とか 例の (function(){}()) とか (結局直してる)
細かい修正などです。

先日、Hacker NewsEcho JS で Pot.js が紹介されました (ありがとうございます)。
でもアクセスが今までの 1000 倍くらいになって、すごいことになってちょっとビビった。
(API サーバのほうは適当に調節しておいたので大丈夫だった。よかった。。)


それで Pot.js の本来の存在意義ですが、

実行環境の CPU に負荷をかけることなく JavaScript が実行できる。

といったことが本来の目的であり、常に追求している目標でもあります。
Deferred が重点にとらわれがちですが、Pot.Deferred はそれの足がかりであり、
Pot.Deferred だけがライブラリの中核ではないので、いろいろ使ってみてください。

新しいデザインのリファレンス

現在、新しいデザインでリファレンスを書き直しています。


前は 全部で 1 ファイルになっちゃってて、どんどん重くなるし更新もやり難くて
どうしようもなかったのですが、今回は 各ページを非同期読み込みにして
英語版と日本語版で見れるようにして、戻るボタンとかも再現したり、がんばってます。

でもまだ作成途中です (2012-02-11 現在)。

ある程度完成したら 本来の /index.html に移動させようと思ってるんですが、
先ほどの Echo JS などが /test-for-new-design/ にリンクしちゃってるもんだから
どうしようかと考え中。(たぶん リダイレクトか、もうこのままでいいか…)。

CPU 比較を実行

jsFiddle のほうで
JavaScript の while ループと、Pot.js の非同期イテレータとで CPU 使用率の比較を書いてみました。
前に書いた Pot.js イテレータと jQuery.each と for ループの CPU 使用率をグラフで比較
と同じ事ですが、実際に実行できます (ただし負荷テストなのでブラウザクラッシュに気をつけてください)。
もっとも最近のブラウザはループでクラッシュしないと思いますが。。

CPU 使用率は、Web ブラウザからの JavaScript では取得できないと思うので、
CPU モニタリングするアプリとか、
なければ Win ならタスクマネージャから「パフォーマンス」→ 「CPU 使用率」
を見ながら実行してみるとわかりやすいと思います。


今回は while 文と Pot.Deferred.forEver の比較ですが、
Pot.js 非同期イテレータは CPU 負荷が一定量に抑えられたループが可能になっていると思います。
ただし、この例のような処理だと実行時間はある程度伸びてしまいます。

実行時間に関しては 速度調整 が可能なので、処理に応じたスピードを選ぶこともできます。
(このへんは今後改善したいです)。

サンプルのソースコード

jsFiddle でも確認できますが、今回使用したサンプルのソースコードです。
ループ処理の部分だけ抽出しています。

JavaScript while ループを使ったソースコード:
// while で同期ループして圧縮
compressSync : function(s) {
    var a = 53300, b, c, d, e, f, g = -1,
        h, i, r = [], x = String.fromCharCode;

    s = new Array(a--).join(' ') + s;
    while ((b = s.substr(a, 256))) {
        for (c = 2, i = b.length; c <= i; ++c) {
            d = s.substring(
                a - 52275,
                a + c - 1
            ).lastIndexOf(b.substring(0, c));
            if (!~d) {
                break;
            }
            e = d;
        }
        if (c === 2 || c === 3 && f === g) {
            f = g;
            h = s.charCodeAt(a++);
            r.push(
                x(h >> 8 & 255),
                x(h & 255)
            );
        } else {
            r.push(
                x((e >> 8 & 255) | 65280),
                x(e & 255),
                x(c - 3)
            );
            a += c - 1;
        }
    }
    return r.join('');
}

Pot.js 非同期イテレータ (今回は Pot.Deferred.forEver) を使ったソースコード:
// Pot.js 非同期イテレータで圧縮
compressAsync : function(s) {
    var a = 53300, b, c, d, e, f, g = -1,
        h, i, r = [], x = String.fromCharCode;

    var deferred = new Pot.Deferred();

    s = new Array(a--).join(' ') + s;

    // whileループを forEver に置き換え
    Pot.Deferred.forEver[SPEED](function() {

        b = s.substr(a, 256);
        if (!b) {
            throw Pot.StopIteration;
        }

        for (c = 2, i = b.length; c <= i; ++c) {
            d = s.substring(
                a - 52275,
                a + c - 1
            ).lastIndexOf(b.substring(0, c));
            if (!~d) {
                break;
            }
            e = d;
        }
        if (c === 2 || c === 3 && f === g) {
            f = g;
            h = s.charCodeAt(a++);
            r.push(
                x(h >> 8 & 255),
                x(h & 255)
            );
        } else {
            r.push(
                x((e >> 8 & 255) | 65280),
                x(e & 255),
                x(c - 3)
            );
            a += c - 1;
        }
    }).then(function() {
        deferred.begin(r.join(''));
    }, function(err) {
        deferred.raise(err);
    });
    return deferred;
}

同期か非同期かの違いがありますが、
単に、メインの while 文を Pot.Deferred.forEver に変えてるだけです。
forEver は、StopIteration が throw されるまで、永久にループする関数です。

あと、この関数は LZ77アルゴリズムによる圧縮関数をJavaScript最短コードで | 圧縮電子精神音楽浮遊構造体 (見れないかも)
で作ったソースコードをちょっとだけ手直ししたものです。
文字列を圧縮解凍します。

Pot.js / PotLite.js

Pot.js は CPU に負荷をかけることなく JavaScript の実行を可能とするユーティリティライブラリです。

PotLite.js は Pot.js の非同期な部分だけを抽出したライトバージョンです。

ダウンロード

マニュアル

マニュアルは上に書いたように 2012-02-11 現在、まだすべてのオブジェクトの解説ができてません。。

その他の情報についてはマニュアル/マニュアルからのリンク から参照ください。

レポジトリ




その他、なにか問題・バグ・感想・指摘などあれば、
コメントやメールまたは @polygon_planet まで送っていただけるとうれしいです。


2012年1月11日

HTML5 File APIでドラッグ&ドロップのサンプル-Pot.js+PotLite.jsリリースノート

Pot.js 1.11PotLite.js 1.28 リリースしました。


主な新機能として、HTML5 の File API を使った
ファイルのドラッグ&ドロップ を扱う Pot.DropFile を実装しました。

Pot.DropFile は FileReader でドロップされたファイルをアップロードしたり
内容やサイズを取得してその場で処理することが可能です。


HTML5 とドラッグ&ドロップイベントをサポートしてるブラウザ (Firefox, GoogleChrome, Safari) で
このブログ内に、デスクトップなどから画像やテキストなどの適当なファイルをドロップしてみてください。

ここにドロップされたファイル内容が表示されます。





ソースコード

このサンプルのソースコードです。
DOM ノードを作成するのにあたって jQuery を使用しています。

HTML のソース:
<div id="dropfile-container"
     style="border-color: #ccc; border-style: dashed; border-width: 1px 0 1px 0; margin: 10px;">
    ここにドロップされたファイル内容が表示されます。
</div>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script src="http://api.polygonpla.net/js/pot/1.11/pot.min.js"></script>

JavaScript のソース:
$(function () {

    // ドロップされた結果を表示する div
    var targetId = '#dropfile-container';

    // ドロップ用のパネルを作成
    var panel = $('<div/>').css({
        position        : 'fixed',
        left            : '10%',
        top             : '10%',
        width           : '80%',
        height          : '80%',
        minHeight       : 200,
        background      : '#ff8cd3',
        zIndex          : 9999999,
        display         : 'table',
        textAlign       : 'center',
        WebkitBoxShadow : '1px 1px 10px #333',
        MozBoxShadow    : '1px 1px 10px #333',
        boxShadow       : '1px 1px 10px #333'
    })
    .append(
        $('<div/>')
            .css({
                display       : 'table-cell',
                color         : '#fff',
                fontWeight    : 'bold',
                verticalAlign : 'middle',
                fontSize      : '160%'
            })
            .text('ここにファイルをドロップします')
    )
    .hide()
    .appendTo('body');

    // ファイルの情報を表示する関数
    var showFileInfo = function(name, size, type) {
        $('<div/>')
            .text(name + ' (' + type + ' ' + size + 'bytes)')
            .appendTo(targetId);
    };

    // ----- ここから処理の中心 -----
    // 設定と共にインスタンスを生成
    var dropFile = new Pot.DropFile(panel, {
        // メッセージを表示すべき時
        onShow : function() { panel.show() },
        // メッセージを非表示にすべき時
        onHide : function() { panel.hide() },
        // ドロップ時の処理
        onDrop : function(files) {},
        // 画像がドロップされた時
        onLoadImage : function(data, name, size, type) {
            showFileInfo(name, size, type);
            $('<img/>')
                .attr({src : data})
                .css({maxWidth : 500, maxHeight : 500})
                .appendTo(targetId);
        },
        // テキストがドロップされた時
        onLoadText : function(data, name, size, type) {
            showFileInfo(name, size, type);
            $('<textarea/>')
                .val(data)
                .css({width : '90%', height : 300})
                .appendTo(targetId);
        },
        // 不明なファイルがドロップされた時
        onLoadUnknown : function(data, name, size, type) {
            showFileInfo(name, size, type);
            $('<textarea/>')
                .val(data)
                .css({width : '90%', height : 300})
                .appendTo(targetId);
        },
        // ドロップされたファイルが読み込めた時 (このときアップロードも可能)
        onLoadEnd : function(files) {
            // 今回は未使用
            /*
            this.upload(
                'http://www.example.com/',
                'dropfiles'
            ).then(function(res) {
                alert('finish upload.\nresponse:\n\n' + res.responseText);
            });
            */
        }
    });
});

Pot.js / PotLite.js

Pot.js は非同期イテレータと文字列処理を中心とした JavaScript ライブラリです。

Web ブラウザ上や Node.js 上、 UserScript や XUL 上で動作します。
この例のような DOM や HTML5 File API が含む処理は Node.js などの環境では意味がありませんが
テキスト処理やイテレータなどを利用するメリットがあります。
とくに非同期処理を扱う Deferred オブジェクトにより作業効率が向上すると思います。

PotLite.js は Pot.js の非同期な部分だけを抽出したライトバージョンです。

ダウンロード

マニュアル

マニュアルは 2012-01-11 現在、まだすべてのオブジェクトの解説ができてません。。

その他の情報についてはマニュアルから参照ください。


レポジトリ







2011年12月6日

JavaScript非同期ライブラリ PotLite.js 1.23 リリース

JavaScript 非同期処理ライブラリ PotLite.js 1.23 リリースしました。

Version 1.23 は、ほとんどバグフィックスです。

AOP ぽいことができる Pot.Signal がひどくて、
シグナル解除して再度登録すると重複してコールバック関数が呼ばれちゃったりしてたので
がんばって可能な限り実行テスト増やしてフィックスして
やっと想定の動きになりました。

前バージョン (1.22) を使ってる方いたら申し訳ないです。。

PotLite.js

PotLite.js は、めんどうになりがちな非同期処理をとにかく楽にコーディングできるよう
直感的に記述できる Deferred オブジェクトを中核として実装しています。
そして、ユーザー (UI) への配慮を目的として
CPU など負荷のかからないループ処理やイテレータが利用できる JavaScript ライブラリです。


経緯などについては、以前の記事 や、 CPU 使用率のベンチマーク結果の記事 など
シグナル (イベント) については PotLite.js 1.22 リリース - アスペクト指向っぽく書けるSignal実装
などの記事を参照ください。

ダウンロード

PotLite.js 1.23

HEAD (常に最新)

レポジトリ



以下は PotLite.js について情報です。
レポジトリにも同じようなこと書いてあるので
インストールや概要など、不要な場合は読み飛ばしてください。
マニュアルのリンクは下にあります。

動作環境

以下の Web ブラウザで動作確認済みです。

  • Mozilla Firefox *
  • Internet Explorer 6+
  • Safari *
  • Opera *
  • Google Chrome *

また、以下の環境でも動作するよう設計されています。

  • Greasemonkey (UserScript)
  • Mozilla Firefox Add-On (on XUL)
  • Node.js
  • Other non-browser environment

インストール

一般的な方法で動作します。

例:

<script type="text/javascript" src="potlite.min.js"></script>
<!--または-->
<script type="text/javascript" src="http://api.polygonpla.net/js/pot/potlite/1.23/potlite.min.js"></script>

Node.js の場合。

// Example to define Pot object on Node.js.
var Pot = require('./potlite.min.js');
Pot.debug(Pot.VERSION);

Pot.Deferred.begin(function() {
    Pot.debug('Hello Deferred!');
}).then(function() {
    // ...
});
// ...

Greasemonkey (userscript) の例。

// ==UserScript==
// ...
// @require  https://github.com/polygonplanet/Pot.js/raw/master/potlite.min.js
// ...
// ==/UserScript==
Pot.Deferred.begin(function() {
    return Pot.request('http://www.example.com/data.json').then(function(res) {
        return Pot.parseFromJSON(res.responseText);
    });
}).then(function(res) {
    Pot.debug(res);
    // do something...
});
//...
PotLite.js をバージョンを限定して Web から直接読み込みたい場合、
上の GitHub リンクでは常に最新になってしまうため
実装の差異による不具合が発生するかもしれません。
そのため、ホスティング用 API サーバを用意しています。
これは、1.23 の部分をリリース済みのバージョンに合わせて変更できます。
レポジトリに (例えば 1.xx と) バージョンをタグ付けした時に、
あわせて API サーバに置くようにしています。

例えば Greasemonkey で 1.23 を使いたい場合、
// ==UserScript==
// ...
// @require  http://api.polygonpla.net/js/pot/potlite/1.23/potlite.min.js
// ...
// ==/UserScript==
と記述できます。

Greasemonkey に限らず script タグからでもなんでも自由に使ってください。


jQuery プラグインとしての例:

// jQuery を読み込んだ後に実行。
Pot.deferrizejQueryAjax();

// Ajax 系の関数が Pot.Deferred を返すようになる
$.getJSON('/hoge.json').then(function(data) {
    alert(data.results[0].text);
}).rescue(function(err) {
    alert('Error! ' + err);
}).ensure(function() {
    return someNextProcess();
});

// エフェクトなどを Deferred 化する 'deferred' が追加される
$('div#hoge').deferred('hide', 'slow').then(function() {
    // .hide() が終了したあとの処理
});

Pot.deferrizejQueryAjax() は v1.23 現在、ライブラリ側からは自動で実行しません。
なので、プラグインとして利用する場合は コードの最初などで一度コールしてください。

リファレンス・マニュアル

より詳しい情報はすべてリファレンスに載っています。
基本的な導入や、各メソッド・関数についても扱っています。

動作テスト

以下のページで動作テストができます。
ページを開くと実装されている主な関数・メソッドを全てテストします。

自動生成されたドキュメント

Closure Compiler によりソースコードから自動生成されたドキュメントです。

殆どの関数ごとにサンプルコードを載せているので、ある程度は参考になると思います。
生成物をすべて確認しているわけではないので、誤認識してる箇所もあるかもしれません。
より詳細な実装などは直接ソースコードを参照ください。




不明な点、要望やバグや感想などありましたら
@polygon_planet や 下のレポジトリから、またはメールでなんでも伝えてください。

レポジトリ

2011年11月23日

JavaScript非同期ライブラリ PotLite.js 1.22 リリース。アスペクト指向っぽく書けるSignal実装

追記

PotLite.js 1.22 はバギーなため 記事内のリンクを 1.23 に変更しました。

PotLite.js 1.22 リリースしました。

PotLite.js は、非同期処理や負荷のかからないループ処理やイテレータを重視した JavaScript ライブラリです。
経緯などについては、以前の記事 や、 CPU 使用率のベンチマーク結果の記事 を参照ください。

ダウンロード

PotLite.js 最新

レポジトリ


PotLite.js バージョン 1.22 では Pot.Signal オブジェクトを実装しました。

Pot.Signal は、任意のハンドラを登録し シグナルを送信して実行に移すことが可能です。
言い方を変えると、自らイベントを作成し、任意のタイミングで呼び出せるようなものです。

「イベント」という概念で実装したわけではありませんが、ここでは便宜上イベントという言い方をします。

Pot.Signal には大まかに以下の関数があります。

  • attach : イベントを登録
  • detach : イベントを解除
  • signal : イベントを実行

Pot.Signal の使用例

Pot.Signal はいろいろな使い方があります。
例えば、
// イベントを登録
//  (第一引数に文字列を渡すと document.getElementById を使用する)
var handler = attach('#foo', 'click', function(ev) {...});

// イベント解除
detach(handler);
上のように addEventListener/removeEventListener の代わりに使用したり、
var MyObj = {};

// 独自のシグナルを登録
var handler = attach(MyObj, 'clear-data', function() {
  // プロパティの初期化など
  MyObj.data = null;
  MyObj.time = null;
});

attach(window, 'load', function() {
  // 初期化するシグナルを送信
  signal(MyObj, 'clear-data');

  // リセットボタンを押した時にもクリアするよう設定
  attach('#reset', 'click', function() {
    signal(MyObj, 'clear-data');
  });

  // 既存の処理など
  myLoadProcess();
  //...
});
上のように attach, signal を独自のシグナルとして使用したりできます。

そして、attachBefore と attachAfter を使うと処理の前後に何らかの動作を追加できます。
// 保存ボタンを押した時のイベントを設定
attach('#saveData', 'click', function() {
  // 保存する関数
  saveData(document.getElementById('inputText').value);
  // ユーザーに保存を伝える関数
  showSaveData('Saved!');
});

// これにフォーカス移動するよう後付けする
attachAfter('#saveData', 'click', function() {
  document.getElementById('inputText').focus();
});

// その前にログを取るよう設定する
attachBefore('#saveData', 'click', function() {
  MyLogger.log('Save inputText');
});
attachBefore, attachAfter はいくつでも登録できます。
登録した順に実行されます。

また、attachPropBefore, attachPropAfter という関数もあります。
これは、attachBefore, attachAfter と違い、
オブジェクトの関数にダイレクトに作用します。
オブジェクトの持つ関数が呼ばれると、自動的に Before, After のハンドラ関数が実行されます。


例えば何らかのアプリケーションを実行する時に、ログをとりたくなった場合。
var MyApp = {
  execute : function() {
    // 何らかの処理を開始する
    myAppDoit();
  }
  // ...
};

attach('#execute', 'click', function() {
  // アプリケーションを実行
  MyApp.execute();
});

// 実行する前にログを取る
attachPropBefore(MyApp, 'execute', function() {
  MyLogger.log('Begin execute');
});

// 実行した後のログを取る
attachPropAfter(MyApp, 'execute', function() {
  MyLogger.log('End execute');
});
このように設定することで、上の例の場合では
MyApp.execute が実行される前後に任意の処理を追加できます。

解除したくなったら、
var handler = attach(...);

// シグナルを解除
detach(handler);
attach*() 関数の戻り値のハンドラオブジェクトを使い、
detach (Pot.Signal.detach) で解除できます。

他にも detachAll() を使い一括で解除することもできます。


Pot.attach(), Pot.signal() などは Pot.Signal.attach() として実装されていますが、
Pot オブジェクトからも参照できます。
Pot.globalize() を実行しておくと、単に attach(obj, ...) と書けます。
今回の例では attach と表現しています。


これらの関数、とくに Before, After を使うことで、
アスペクト指向 (AOP) っぽいプログラミングが可能となります。

Pot.Signal は Node.js などのサーバでの利用も可能ですが、
Web ブラウザ上、HTML5 での Web Storage (localStorage や sessionStorage) を使う時などに便利と思います。

また、attach 系の関数 (attach*) の後に .once を付けると、一度だけ実行されます。
// 一度クリックしたら解除される
attach.once('#hoge', 'click', function() {...});

Pot.Signal の関数

Pot.Signal.attach(object, signalName, callback[, useCapture])
スロットにシグナルを登録します。 Pot.Signal が保持しているハンドラリストに任意のオブジェクトとコールバック関数を登録します。 引数 object が DOM エレメントだった場合、addEventListener と同じように働きます。 戻り値は、Pot.Signal.Handler のインスタンスです。
Pot.Signal.attachBefore(object, signalName, callback[, useCapture])
スロットに存在するハンドラが呼ばれる前に実行されるシグナルを登録します。 Pot.Signal.attach() で登録したものと同じシグナル signalName で登録した場合、 attach() のハンドラが呼ばれる前に実行されます。 同じシグナルが存在しない場合 signal() によって実行しても何も起きません。 DOM エレメントに対して attach() した場合も同様です。 戻り値は、Pot.Signal.Handler のインスタンスです。
Pot.Signal.attachAfter(object, signalName, callback[, useCapture])
スロットに存在するハンドラが呼ばれた後に実行されるシグナルを登録します。 Pot.Signal.attach() で登録したものと同じシグナル signalName で登録した場合、 attach() のハンドラが呼ばれた後に実行されます。 同じシグナルが存在しない場合 signal() によって実行しても何も起きません。 DOM エレメントに対して attach() した場合も同様です。 戻り値は、Pot.Signal.Handler のインスタンスです。
Pot.Signal.attachPropBefore(object, propName, callback)
object が持つ関数 propName が呼ばれる前に実行されるシグナル callback を登録します。 attachBefore() と違い、Pot.Signal.signal() を呼ばなくても object[propName] が呼ばれた時に自動的に実行されます。 attachPropBefore は、DOM オブジェクトに対しての動作は保証されません。 戻り値は、Pot.Signal.Handler のインスタンスです。
Pot.Signal.attachPropAfter(object, propName, callback)
object が持つ関数 propName が呼ばれた後に実行されるシグナル callback を登録します。 attachAfter() と違い、Pot.Signal.signal() を呼ばなくても object[propName] が呼ばれた時に自動的に実行されます。 attachPropAfter は、DOM オブジェクトに対しての動作は保証されません。 戻り値は、Pot.Signal.Handler のインスタンスです。
Pot.Signal.detach(object[, signalName[, callback[, useCapture]]])
Pot.Signal.attach*() によって登録したシグナルを 1 つ解除します。 attachBefore, attachAfter, attachPropBefore, attachPropAfter で登録したものも同様に解除可能です。 引数に object だけを渡した場合、関連するシグナルが 1 つ解除されます。 引数 signalName, callback を渡すと明確に解除できます。 また、attach() などの戻り値のハンドラを object として渡して解除もできます。 登録と解除の関係は、setTimeout と clearTimeout の関係と同様に扱えます。 DOM エレメントに対しては、 removeEventListener と同様に扱うことができます。 解除成功時に true、失敗時に false が返ります。
Pot.Signal.detachAll([object[, ...signals]])
Pot.Signal.attach*() によって登録したシグナルをすべて解除します。 attachBefore, attachAfter, attachPropBefore, attachPropAfter で登録したものも同様に解除可能です。 引数に object だけを渡した場合、関連するシグナルがすべて解除されます。 引数 signals を渡すと一致するシグナルがすべて解除できます。 シグナル名となる signals は文字列または配列で複数指定できます。
Pot.Signal.signal(object, signalName[, ...args])
Pot.Signal.attach() によって登録したシグナルを実行します。 引数 object と signalName に一致するシグナルがすべて実行されます。 実行される関数には第三引数~以降に任意の引数が渡せます。

PotLite.js

以下は PotLite.js について情報です。
インストールや概要など、不要な場合は読み飛ばしてください。

PotLite.js ダウンロード

最新

レポジトリ

$ git clone git://github.com/polygonplanet/Pot.js

GitHub : polygonplanet/Pot.js

動作環境

以下の Web ブラウザで動作確認済みです。

  • Mozilla Firefox *
  • Internet Explorer 6+
  • Safari *
  • Opera *
  • Google Chrome *

また、以下の環境でも動作するよう設計されています。

  • Greasemonkey (userscript)
  • Mozilla Firefox Add-On (on XUL)
  • Node.js
  • Other non-browser environment

インストール

一般的な方法で動作します。

例:

<script type="text/javascript" src="potlite.min.js"></script>
<!--または-->
<script type="text/javascript" src="http://api.polygonpla.net/js/pot/potlite/1.23/potlite.min.js"></script>

Node.js の場合。

// Example to define Pot object on Node.js.
var Pot = require('./potlite.min.js');
Pot.debug(Pot.VERSION);

Pot.Deferred.begin(function() {
  Pot.debug('Hello Deferred!');
}).then(function() {
  // ...
});
// ...

Greasemonkey (userscript) の例。

// ==UserScript==
// ...
// @require  https://github.com/polygonplanet/Pot.js/raw/master/potlite.min.js
// ...
// ==/UserScript==
Pot.Deferred.begin(function() {
  return Pot.request('http://www.example.com/data.json').then(function(res) {
    return Pot.parseFromJSON(res.responseText);
  });
}).then(function(res) {
  Pot.debug(res);
  // do something...
});
//...
PotLite.js をバージョンを限定して Web から直接読み込みたい場合、
上の GitHub リンクでは常に最新になってしまうため
実装の差異による不具合が発生するかもしれません。
そのため、API サーバを用意しました。
これは、1.23 の部分をリリース済みのバージョンに合わせて変更できます。
レポジトリに (例えば 1.xx と) バージョンをタグ付けした時に、
あわせて API サーバに置くようにしています。

例えば Greasemonkey で 1.23 を使いたい場合、
// ==UserScript==
// ...
// @require  http://api.polygonpla.net/js/pot/potlite/1.23/potlite.min.js
// ...
// ==/UserScript==
と記述できます。

Greasemonkey に限らず script タグからでもなんでも自由に使ってください。



jQuery プラグインとしての例:

// jQuery を読み込んだ後に実行。
Pot.deferrizejQueryAjax();

// Ajax 系の関数が Pot.Deferred を返すようになる
$.getJSON('/hoge.json').then(function(data) {
  alert(data.results[0].text);
}).rescue(function(err) {
  alert('Error! ' + err);
}).ensure(function() {
  return someNextProcess();
});

// エフェクトなどを Deferred 化する 'deferred' が追加される
$('div#hoge').deferred('hide', 'slow').then(function() {
  // ( hide() が終了したあとの処理)
});

Pot.deferrizejQueryAjax() は現状、
ライブラリ側で実行しません。
なので、プラグインを使用する場合は
コードの最初などで実行する必要があります。

リファレンス・マニュアル

より詳しい情報はすべてリファレンスに載っています。
基本的な導入や、各メソッド・関数についても扱っています。

動作テスト

以下のページで動作テストができます。
ページを開くと実装されている主な関数・メソッドを全てテストします。

自動生成されたドキュメント

Closure Compiler によりソースコードから自動生成されたドキュメントです。

殆どの関数ごとにサンプルコードを載せているので、ある程度は参考になると思います。
生成物をすべて確認しているわけではないので、誤認識してる箇所もあるかもしれません。
より詳細な実装などは直接ソースコードを参照ください。




不明な点、要望やバグや感想などありましたら
@polygon_planet や 下のレポジトリから、またはメールでなんでもどぞです。

レポジトリ