tmlib の tm.util.Ajaxは、「取り敢えず、Webサーバーから情報を取得したい」という実装なのが現状。でも、DOMエンジンなんかで見られるAjaxはフォームの内容をPOSTしたりと、「サーバーへの送信」ができる。この違いは純粋に物足りなさを感じてしまうので、この機に書き換えてしまう。
(function() {
/**
* @enum
* @TODO 現状はWebリソースの読み込み限定の実装。サーバーにデータを送信したい場合は大幅な変更を要する。
* @private
*/
var AJAX_DEFAULT_SETTINGS = {
/* @property type */
type :"GET",
/* @property async */
async: true, // 同期モードにも対応したい考えが見られるが、返却値として XHRオブジェクトが無い。
/* @property data */
data: null, // POSTしたいなら xhr.send(params.data) とすべき。
/* @property contentType */
contentType: 'application/x-www-form-urlencoded',
/* @property dataType */
dataType: 'text',
/* @property username */
username: null,
/* @property password */
password: null,
/* @property success */
success : function(data){ alert("success!!\n"+data); },
/* @property error */
error : function(err){ alert("error!!"); },
/* @property beforeSend */
/* 却下
beforeSend: null, // せっかくラッパーオブジェクトとしているのに onreadystatechange() も書き換える機会を与えてしまう実装
*/
/* @property headers */
headers: {}, // DOMエンジンのような指定方法にすべき。
/* @property received */
received: function(headers, data){ console.log('recieved', headers, data ) },// 通信内容によっては レスポンスヘッダを参照したい場合もある。
};
/**
* @class tm.util.Ajax
* @TODO DOMエンジンで採用される一般的な実装にする。
*/
tm.util.Ajax = {
/**
* @property load
*/
load: function(params) {
var key, httpRequest, ajax_param, conv_func;
for (key in AJAX_DEFAULT_SETTINGS) {
params[key] = params[key] || AJAX_DEFAULT_SETTINGS[key];
}
httpRequest = new XMLHttpRequest();
ajax_params = ""; // これは不要かも。
conv_func = (typeof params.proc==='function')
? params.proc
: tm.util.Ajax.DATA_CONVERTE_TABLE[params.dataType||"undefined"];
// 通信成功時のコールバック
httpRequest.onreadystatechange = function() {
var headers, data;
switch( httpRequest.readyState ){
// REQUEST-METHOD 'HEAD' の時の成功コールバック
case 2:
headers = httpRequest.getAllResponseHeaders();
break;
// REQUEST-METHOD 'HEAD' 以外の成功コールバック
case 4:
headers = httpRequest.getAllResponseHeaders();
// 成功
if (httpRequest.status === 200) {
data = conv_func(httpRequest.responseText);
params.received(headers, data);
params.success(data);
}
// status === 0 はローカルファイル用
else if (httpRequest.status === 0) {
data = conv_func(httpRequest.responseText);
params.received(headers, data);
params.success(data);
}
break;
default:
console.log("通信中");
}
};
// XHRの通信エラー navigator.onLine == false の時や XHR2が利用出来ない時など
httpRequest.onerror = function( err ) {
params.error( err )
}
httpRequest.open(params.type, params.url, params.async, params.username, params.password); // オープン
httpRequest.setRequestHeader('Content-Type', params.contentType); // ヘッダをセット
/* beforeSend は却下
* 引数に XMLHttpRequest のリファレンスを与える実装の為、
* onreadystatechange(), onerror() など、APIの機能を喪失する機会を提供してしまう。
if (params.beforeSend) {
params.beforeSend(httpRequest);
}
*/
// 一般的なDOMエンジンのような実装にする
if(params.headers && typeof params.headers === 'object'){
for(var header in params.headers){
if(header.toLowerCace() !== 'content-type') {
httpRequest.setRequestHeader(header, params.headers[header]);
}
}
}
// サーバーへ送信するデータのボディ(default は null)
httpRequest.send(params.data);
// 同期モードに対応するため、XHRを正しく返却する。
if(!params.async) return httpRequest;
},
/**
* @property loadJSONP
*/
loadJSONP: function(url, callback) {
var g = tm.global;
g.tmlib_js_dummy_func_count = tm.global.tmlib_js_dummy_func || 0;
var dummy_func_name = "tmlib_js_dummy_func" + (g.tmlib_js_dummy_func_count++);
g[dummy_func_name] = callback;
var elm = document.createElement("script");
elm.type = "text/javascript";
elm.charset = "UTF-8";
elm.src = url + "&callback=" + dummy_func_name;
elm.setAttribute("defer", true);
document.getElementsByTagName("head")[0].appendChild(elm);
}
};
/**
* @enum tm.util.Ajax.DATA_CONVERTE_TABLE
* データコンバータテーブル
*/
tm.util.Ajax.DATA_CONVERTE_TABLE = {
/** @property */
undefined: function(data) {
return data;
},
/** @property */
text: function(data) {
return data;
},
/** @property */
xml: function(data) {
var div = document.createElement("div");
div.innerHTML = data;
return div;
},
/** @property */
dom: function(data) {
var div = document.createElement("div");
div.innerHTML = data;
return tm.dom.Element(div);
},
/** @property */
json: function(data) {
try {
return JSON.parse(data);
}
catch(e) {
console.dir(e);
console.dir(data);
}
},
/** @property
* 外部JavaScript をeval するときは tm.global なスコープとなるように call() を使う。
*/
script: function(data) {
eval.call(tm.global, data);
return data;
},
/**
* @property
* ### Reference
* -
* @param {Object} data
*/
bin: function(data) {
var bytearray = [];
for (var i=0, len=data.length; i<len; ++i) {
bytearray[i] = data.charCodeAt(i) & 0xff;
}
return bytearray;
},
};
})();
tm.util.Ajax.DATA_CONVERTE_TABLE.script は開発者にとって、実行されるスコープがXHRのコールバック関数となる致命的な実装だったので、晒しておきます。
コメント