[JQuery習作] Google Chart API のデータ値をエンコードする

/web/javascript

Note: この記事は、3年以上前に書かれています。Webの進化は速い!情報の正確性は自己責任で判断してください。

Note: Chart APIは現在ではGoogle Chartとして整理されており、この記事で利用されているImage Chart APIは旧仕様として削除対象になっています。実際に削除はされていないので今のところ利用できていますが、2015年中には消えてしまいそうです。(2015-08-31)

JQueryプラグイン書いた。そう、最近ようやくJQuery使ってるんですよ。
まあ仕事でJS触る機会ってのもなかなかないのですが、そろそろ慣れとかないとSugamoでぼっちになりそ(ry

さて、GoogleChartAPIでは<img>のsrc属性にパラメータ入れてリクエストする訳ですが、パラメータが長くなると無視られたりする。そうした場合には、データ部分をエンコードして短くしてやれば通る。今回つくったのは、データ値をエンコードしてsrc属性に追加するスクリプトです。構造はちょっと前のSugamoでずどさんに教えてもらった$.widget風味。こんな感じ。

demo1 - extendEncode + method-chain

$('#contents img#googleChartApi_1')
    .gcaEncoding({ maxVal: 100 })
    .gcaEncoding('extendEncode', [50,75,100,100,100, ... ,100] )
    .gcaEncoding('extendEncode', [50,75,100,100,100, ... ,0] )
    .gcaEncoding('extendEncode', [25,50,75,100,75, ... ,0] )
    .gcaEncoding('extendEncode', [0,0,0,0,0,0, ... ,0] )
    ;

まあもともと、GoogleにてsimpleEncode/extendedEncodeという関数が公開されてます。こいつをJQuery環境でJQueryっぽく使えるようにしてみただけなので、エンコード部分そのものは殆どいじってないです。あとはマルチデータ表示したい場合、カンマ区切りでエンコード文字列を渡す必要があるわけですが、これを簡単にしたいってのが主な動機。

simpleEncodeの場合

いわゆる「&chd=s:」からなるデータ部分。マルチデータ表示できないけど、より短い文字列になる。そもそも単一データでエンコードが必要になるほど複雑で長い図って、あんまないんですけど。機密保持?みたいな?

$('#contents img#googleChartApi_2').gcaEncoding({
                          type: 'simple',
                          arrVals: [10,12,12,24,23, ... ,88,94],
                          maxVal: 94
                       });
// &chd=s:GIIQPNTVXgifkqrrrzt59

demo2 - simpleEncode

いちお先と同じメソッドチェーン方式でもできます。ただchd=s:だとマルチデータは扱えないので、使うとしたらfind()/eq()/end()とかで複数の図に適用させていく時とかかな? たぶんできるけど、真面目にチェックしてない。

$('#contents img#googleChartApi_2').gcaEncoding({ maxVal: 94 })
        .gcaEncoding('simpleEncode', [10,12,12,24,23, ... ,88,94] )
        ;
// &chd=s:GIIQPNTVXgifkqrrrzt59

extendEncodeの場合

chd=e:」からなるデータ文字列。こっちがメインですね。冒頭に出したメソッドチェーン方式が主ですが、いちおsimpleEncodeっぽい書き方でも行けるようにしてます。

$('#contents img#googleChartApi_3').gcaEncoding({
                          type: 'extend',
                          arrVals: [10,12,12,24,23, ... ,88,94],
                          maxVal: 94
                       });
// &chd=e:GzIKIKQVPqNnUbWdYgiCjZgrlcrks7s7s71Gvq76..

demo3 - extendEncode / draw once

プラグイン本体

実行にはJQueryとJQuery UIのWidget(UI Core)が必要です。

$.widget('ui.gcaEncoding', {
    _create: function(){
        // This function scales the submitted values so that
        // maxVal becomes the highest value.

        this.maxVal = this.options.maxVal;

        if(this.options.type){
            this.options.type == 'simple'
                ? this.simpleEncode(this.options.arrVals)
                : this.extendEncode(this.options.arrVals)
                ;
        }
    },

    // simple Encode for Google Chart API
    // -> &chd=s:LLLLLLLLLLLLLLLLL
    _simpleEncoding: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',

    simpleEncode: function(valueArray) {
        var maxValue  = this.maxVal;
        var chartData = ['&chd=s:'];

        for (var i = 0; i < valueArray.length; i++) {
            var currentValue = valueArray[i];
            if (!isNaN(currentValue) && currentValue >= 0) {
                chartData.push(this._simpleEncoding.charAt(Math.round((this._simpleEncoding.length-1) *
                currentValue / maxValue)));
            }
            else {
                chartData.push('_');
            }
        }
        //return chartData.join('');
        this.element.attr('src', this.element.attr('src') + chartData.join('') );
    },

    // extended Encode for Google Chart API
    // -> &chd=e:LLLLLLLLLLLLLLLLL
    _EXTENDED_MAP: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-.',
    _EXTENDED_MAP_LENGTH: function(){ return this._EXTENDED_MAP.length },

    extendEncode: function(arrVals) {
        var prefix = this.prefix;
        if (!this.prefix) {
            prefix = '&chd=e:';
            this.prefix = ','
        }

        var EXTENDED_MAP        = this._EXTENDED_MAP;
        var EXTENDED_MAP_LENGTH = this._EXTENDED_MAP_LENGTH();
        var maxVal              = this.maxVal;
        var chartData           = prefix;

        for(i = 0, len = arrVals.length; i < len; i++) {
            // In case the array vals were translated to strings.
            var numericVal = new Number(arrVals[i]);
            // Scale the value to maxVal.
            var scaledVal = Math.floor(EXTENDED_MAP_LENGTH *
            EXTENDED_MAP_LENGTH * numericVal / maxVal);

            if(scaledVal > (EXTENDED_MAP_LENGTH * EXTENDED_MAP_LENGTH) - 1) {
                chartData += "..";
            }
            else if (scaledVal < 0) {
                chartData += '__';
            }
            else {
                // Calculate first and second digits and add them to the output.
                var quotient = Math.floor(scaledVal / EXTENDED_MAP_LENGTH);
                var remainder = scaledVal - EXTENDED_MAP_LENGTH * quotient;
                chartData += EXTENDED_MAP.charAt(quotient) + EXTENDED_MAP.charAt(remainder);
            }
        }

        //return chartData;
        this.element.attr('src', this.element.attr('src') + chartData);

    }
});

_createが、gcaEncoding()を実行したときに一度だけ実行されるインスタンス生成部分。今回は直接メソッドを叩けば良いので、無くても良いくらいですね。どのみちメソッドチェーンする前には、一度宣言して読み込む必要があるので、ついでに入れてみた感じ。

感想

$.widgetを利用した量産は、ひとまとめの処理を似たような構成でカプセル化できるので、非常に見やすいです。特にreturnとか気を遣わなくてもメソッドチェーンできるとか、いろいろ楽ですね。

  1. パラメータの取得と変更
  2. 引数を与える
  3. メソッドチェーンで繋ぐ

...と、これくらいまではできたので... 後はカスタムイベントとか、$(':ui-gcaEncode')みたいな呼び出しとかマスターすれば「使えるようになった」と言えるのかな?

JQuery使い始めてから$.fnすっ飛ばして$.widgetに入ったから、イマイチ違いとか利点とか判ってない。先日のzudolog($.fn+$.widgetな設計とか)見てむむむ、と思っているところ。Sugamoで$.widget講座受けて、前に書いた記事(JavaScriptの巧い書き方)っぽく書けるなーと感じて、できちゃった感じです。良いっすね!

添削求むっ(ぇ

参考

Note: スパム対策が面倒なので、コメント投稿を廃止しました。以前のコメントは残します。
ご意見・ご要望はtwitter@sigwygかはてブコメントにて。