////////////////////////////////////////////////////////////////////////////////
/**
*
*
* 使用方式 JSONP.send(options);
*
*
* <options 主要對外參數>
*
* timeout(int) => 設定過時
* url(string, not null) => 要連往後端的網址
* data(string, {}) => 要送出的參數
* jsonp(string) => url參數(request('callBack') = function_name)
* jsonpCallBack(string) => 手動設定callBack 函式的名稱(尚無此功能,主要是自動給予)
*
* error(function) => 若執行錯誤要執行的
* beforeSend(function) => 在整個執行前要執行的函式
* complete(function) => success完,數據都讀完處理完要做的事
* success(function) => 執行成功要執行的函式
*
*/
////////////////////////////////////////////////////////////////////////////////
function $JSONP_(option) {
'use strict';
// debugger;
var self = this;
// 物件的建構函式
this.fn = this.constructor;
/* --------------------------------------------- */
this.haveCall = false; // 是否在傳輸階段
this.haveLoad = false; // script是否正在load
/* --------------------------------------------- */
this.errorMsg_array = [];
// head-tag
this.headTag;
/* --------------------------------------------- */
/**
* timeout: 設定過時
* url: 要連往後端的網址
* data: 要送出的參數(string|{})
* jsonp: url參數(傳遞給後端的key => 此 key 內容主要的功能在告訴回呼函式的名稱)
* jsonpCallBack: 手動設定callBack 函式的名稱(尚無此功能,主要是自動給予)
*
* async: 網頁運作是否要等 script loading完
* defer:是否要等整網頁都 load完才執行
*
* error: 若執行錯誤要執行的函式
* beforeSend: 在整個執行前要執行的函式
* complete:
* success: 執行成功要執行的函式
*/
this.options = {
timeout: 6000,
url: '',
data: null,
async: true,
defer: false,
jsonp: 'callBack', // (request('callBack') = function_name)
error: null,
beforeSend: null,
complete: null,
success: null
};
/* -------------------------------------------- */
this.id;
this.fun_name = ''; // (callBack)的名稱
this.script_id = '';
/* -------------------------------------------- */
this.src = ''; // 要引入 json 數據的 script.src
this.arg = ''; // 要引入 json 數據的網址參數
/* ====================================================================== */
/**
* 初始化(建構式)
*/
this.__construct = function() {
'use strict';
// debugger;
if (!(this.headTag = this.fn.prototype.headTag)) {
this.headTag = this.fn.prototype.headTag = document.querySelector('head');
}
// 登錄所有 option
this.setOption(option);
/* ---------------------------------------------- */
// 創建所需要的(id)
this.id = this.fn.prototype.guid++;
this.script_id = '$JSONP_' + this.id;
// (callBack)的名稱
this.fun_name = 'fun_' + this.id;
/* ---------------------------------------------- */
// 處理要傳送的參數(this.arg)
this.setData();
this.setArg();
/* ---------------------------------------------- */
// (script)的網址
this.src = this.options.url + '?' + this.arg;;
};
/* ====================================================================== */
this.__construct();
}
/* ========================================================================== */
/**
* 共有參數
*/
(function() {
this.headTag;
this.guid = 1; // 編碼
}).call($JSONP_.prototype);
////////////////////////////////////////////////////////////////////////////////
/**
* prototype
*
*/
(function() {
/**
* start here
*/
this.main = function() {
var self = this;
if (typeof this.options.beforeSend == 'function') {
this.options.beforeSend();
};
/* ---------------------------------------------------- */
// 檢查(options)
this.checkOptions();
// 創建臨時需要的(callBack)
this.createCallBackFun();
// 定時檢查
setTimeout(timeOut.bind(this), this.options.timeout);
// 創建要引入資料的(script)
this.createJSONDataScript();
};
/* ===================================================================== */
/**
* 當傳輸過時
*
*/
function timeOut() {
debugger;
if (!this.haveCall) {
// 時間到了,(callBack)卻未執行過
var scriptDom = document.getElementById(this.script_id);;
/* ---------------------------------- */
// 錯誤訊息
this.errorMsg_array.push('timeout');
if (scriptDom) {
this.errorMsg_array.push(scriptDom.textContent);
}
/* ---------------------------------- */
// reset
if (this.haveLoad) {
this.reset_1();
} else {
// (script)還在(load)中
this.reset_2();
}
/* ---------------------------------- */
// 發出錯誤
this.error();
}
};
}).call($JSONP_.prototype);
/* ========================================================================== */
(function() {
/**
* 創建要被(script)呼叫的(callBack)
*
* @return {[type]} [description]
*/
this.createCallBackFun = function() {
// debugger;
var self = this;
this.fn[this.fun_name] = callBack;
/* ---------------------------------- */
/**
* 被呼叫的地方
*
*/
function callBack(data) {
alert('callBack');
debugger;
// 已執行(callBack)
self.haveCall = true;
var success = self.options.success;
self.options.success = undefined;
if (typeof success == 'function') {
success(data);
}
};
};
/* ====================================================================== */
// 創建要引入 json 數據的 script
this.createJSONDataScript = function() {
debugger;
var self = this;
var script = document.createElement('script');
script.setAttribute('src', this.src);
script.setAttribute('id', this.script_id);
/* ----------------------------------------- */
/**
* (script)的同步設定
*
* (defer)比(async)優先
*
* defer => 在網頁(load)完才執行
* async => 異步處理
*
*/
if (this.options.defer) {
this.options.async = false;
script.defer = true;
} else if (this.options.async) {
script.async = true;
}
/* ----------------------------------------- */
script.onerror = function(e) {
self.haveLoad = true; // 已嘗試建立過(script)
self.errorMsg_array.push('script 無法建立');
self.error();
};
/* ----------------------------------------- */
/**
* 最後步驟
*
* (script)已經(load)完,要執行的命令也執行完
*/
script.onload = function(e) {
self.haveLoad = true; // 已嘗試建立過(script)
var complete = self.options.complete;
self.options.complete = undefined;
(typeof complete == 'function') && (complete());
/* ---------------------------------- */
if (self.haveCall == true) {
// callBack已執行過,script已讀完
self.reset_1();
}
};
/* ----------------------------------------- */
// debugger;
this.headTag.appendChild(script);
};
/* ====================================================================== */
/**
* 立即移除<script>標籤
*
* 立即移除(callBack)在(jsonp)中的紀錄
*/
this.reset_1 = function() {
var self = this;
// 移除(callback)
if (this.fn[this.fun_name] != null) {
delete self.fn[this.fun_name];
}
/* ---------------------------------------- */
// 移除(script)
var scriptNode = document.getElementById(this.script_id);
(scriptNode) && (this.headTag.removeChild(scriptNode));
};
/**
* 移除(script)標籤
*
* 延時移除(callback)
*/
this.reset_2 = function() {
var self = this;
// 移除(callback)
if (this.fn[this.fun_name] != null) {
// 正常執行的話(this.fn[this.fun_name])應該已經刪除了
setTimeout(function() {
// body
delete self.fn[this.fun_name];
}, 100000);
}
/* ---------------------------------------- */
// 移除(script)
var scriptNode = document.getElementById(this.script_id);
(scriptNode) && (this.headTag.removeChild(scriptNode));
};
/* ====================================================================== */
/**
* 發出錯誤
*/
this.error = function() {
var error = this.options.error;
this.options.error = undefined;
if (typeof error == 'function') {
var error_msg = this.errorMsg_array.join(', ');
error(error_msg);
}
};
}).call($JSONP_.prototype);
/* ========================================================================== */
/**
* prototype
*/
(function() {
/**
* 處理傳送的參數,轉換 data格式
*/
this.setData = function() {
// debugger;
if (this.options.data == null) {
this.options.data = {};
} else if (typeof this.options.data == 'string') {
// 將文字參數轉為物件
var arg_array = this.options.data.split('&');
this.options.data = {};
for (var i in arg_array) {
var tmp_array = arg_array[i].split('=');
var key = tmp_array[0] || '';
var value = tmp_array[1] || '';
this.options.data[key] = value;
}
}
/* ------------------------------------------------------- */
// 處理(callBack)
var date = new Date();
// 預設是(callBack)
var jsonpName = this.options.jsonp;
// 告訴後端要呼叫那個(callBack)
this.options.data[jsonpName] = '$JSONP_.' + this.fun_name;
this.options.data.timeStamp = date.getTime(); // 防止網頁快取
};
/* ====================================================================== */
/**
* 設定網址參數
*/
this.setArg = function() {
var tmpArray = [];
for (var j in this.options.data) {
var str = j + '=' + this.options.data[j];
tmpArray.push(str);
}
this.arg = tmpArray.join('&');
};
/* ====================================================================== */
/**
* 處理選項
*/
this.setOption = function(option) {
// debugger;
var optionType = Object.prototype.toString.call(option);
if (!/object\]\s*$/gi.test(optionType)) {
throw new TypeError('input option must be {}');
}
for (var key in option) {
if (this.options.hasOwnProperty(key)) {
this.options[key] = option[key];
}
}
};
}).call($JSONP_.prototype);
////////////////////////////////////////////////////////////////////////////////
(function() {
this.___check = function() {};
/* ======================================================================== */
/**
* 輸入檢查
*/
this.checkOptions = function() {
// debugger;
var errorMsg_array = [];
var toString_fun = Object.prototype.toString;
if (!this.headTag || this.headTag.tagName.toLowerCase() !== 'head') {
errorMsg_array.push('no catch head tag');
}
if (typeof this.arg !== 'string') {
errorMsg_array.push('網址參數格式有問題');
}
// options.data
if (this.options.data != null) {
var type = toString_fun.call(this.options.data);
if (typeof this.options.data !== 'string' &&
!/object\]\s*$/gi.test(type)) {
errorMsg_array.push('data參數格式有問題');
}
}
// options.url
if (typeof this.options.url !== 'string' || this.options.url.length == 0) {
errorMsg_array.push('網址格式有問題');
}
// options.timeout
if (typeof this.options.timeout != 'number') {
errorMsg_array.push('timeout must be number');
}
// options.error
if (this.options.error && (typeof this.options.error !== 'function')) {
errorMsg_array.push('error must be function');
}
// options.beforeSend
if (this.options.beforeSend && (typeof this.options.beforeSend !== 'function')) {
errorMsg_array.push('beforeSend must be function');
}
// options.complete
if (this.options.complete && (typeof this.options.complete !== 'function')) {
errorMsg_array.push('complete must be function');
}
// options.success
if (this.options.success && (typeof this.options.success !== 'function')) {
errorMsg_array.push('success must be function');
}
// this.src
if (typeof this.src != 'string' || this.src.length == 0) {
errorMsg_array.push('script no set link');
}
if (errorMsg_array.length > 0) {
throw new Error(errorMsg_array.join(" | "));
}
};
/* ====================================================================== */
}).call($JSONP_.prototype);
////////////////////////////////////////////////////////////////////////////////
/**
* 類別方法
*/
(function() {
var self = this;
/* ====================================================================== */
// (API)start here
this.send = function(options) {
// debugger;
var jsonp = new $JSONP_(options);
jsonp.main();
return jsonp;
};
/* ====================================================================== */
}).call($JSONP_);