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風味。こんな感じ。
$('#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
いちお先と同じメソッドチェーン方式でもできます。ただ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..
プラグイン本体
実行には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とか気を遣わなくてもメソッドチェーンできるとか、いろいろ楽ですね。
- パラメータの取得と変更
- 引数を与える
- メソッドチェーンで繋ぐ
...と、これくらいまではできたので... 後はカスタムイベントとか、$(':ui-gcaEncode')
みたいな呼び出しとかマスターすれば「使えるようになった」と言えるのかな?
JQuery使い始めてから$.fnすっ飛ばして$.widgetに入ったから、イマイチ違いとか利点とか判ってない。先日のzudolog($.fn+$.widgetな設計とか)見てむむむ、と思っているところ。Sugamoで$.widget講座受けて、前に書いた記事(JavaScriptの巧い書き方)っぽく書けるなーと感じて、できちゃった感じです。良いっすね!
添削求むっ(ぇ
参考
- jQuery UI の プラグイン定義関数 $.widget を使ってみる, Cykodog :: Diary
- UI/Developer Guide, JQuery Documentation
- Google Chart APIで作るグラフ画像のサンプル, Archiva
Note: スパム対策が面倒なので、コメント投稿を廃止しました。以前のコメントは残します。
ご意見・ご要望はtwitter@sigwygかはてブコメントにて。