mirror of
https://scm.univ-tours.fr/22107988t/rappaurio-sae501_502.git
synced 2025-09-16 22:35:01 +02:00
1766 lines
51 KiB
JavaScript
1766 lines
51 KiB
JavaScript
/**
|
||
* @name CeL data function
|
||
* @fileoverview 本檔案包含了 data 處理的 functions。
|
||
* @since
|
||
*/
|
||
|
||
'use strict';
|
||
// 'use asm';
|
||
|
||
// --------------------------------------------------------------------------------------------
|
||
|
||
typeof CeL === 'function' && CeL.run({
|
||
// module name
|
||
name : 'data',
|
||
|
||
require : 'data.code.compatibility.|data.native.to_fixed',
|
||
|
||
// 設定不匯出的子函式。
|
||
// no_extend : '*',
|
||
|
||
// 為了方便格式化程式碼,因此將 module 函式主體另外抽出。
|
||
code : module_code
|
||
});
|
||
|
||
function module_code(library_namespace) {
|
||
|
||
// requiring
|
||
var to_fixed = this.r('to_fixed'),
|
||
/** {Number}未發現之index。 const: 基本上與程式碼設計合一,僅表示名義,不可更改。(=== -1) */
|
||
NOT_FOUND = ''.indexOf('_');
|
||
|
||
/**
|
||
* null module constructor
|
||
*
|
||
* @class data 處理的 functions
|
||
*/
|
||
var _// JSDT:_module_
|
||
= function() {
|
||
// null module constructor
|
||
};
|
||
|
||
/**
|
||
* for JSDT: 有 prototype 才會將之當作 Class
|
||
*/
|
||
_// JSDT:_module_
|
||
.prototype = {};
|
||
|
||
/**
|
||
* <code>
|
||
eval(uneval(o)): IE 沒有 uneval
|
||
http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone
|
||
|
||
way1:
|
||
return YAHOO.lang.JSON.parse( YAHOO.lang.JSON.stringify( obj ) );
|
||
|
||
TODO:
|
||
1.
|
||
防止交叉參照版: try
|
||
var a=function(){this.a=1,this.b={a:this.a},this.a={b:this.b};},b=cloneObject(a);
|
||
.or.
|
||
var a={},b;
|
||
a.a={a:1};
|
||
a.b={a:a.a};
|
||
a.a={b:a.b};
|
||
b=cloneObject(a);
|
||
|
||
恐須改成
|
||
=new cloneObject();
|
||
|
||
|
||
2.
|
||
equal()
|
||
|
||
</code>
|
||
*/
|
||
/**
|
||
* clone object
|
||
*
|
||
* @param object
|
||
* @param {Boolean}deep
|
||
* deep / with trivial
|
||
* @return
|
||
*
|
||
* @see Object.clone() @ data.native
|
||
* @since 2008/7/19 11:13:10, 2012/10/16 22:01:12, 2014/5/30 19:35:59.
|
||
*/
|
||
function clone(object, deep) {
|
||
if (!object || typeof object !== 'object')
|
||
// assert: is 純量 / function
|
||
return object;
|
||
|
||
if (Array.isArray(object))
|
||
if (deep) {
|
||
var target = [];
|
||
object.forEach(function(o, index) {
|
||
target[index] = clone(o, deep);
|
||
});
|
||
return target;
|
||
} else
|
||
// Array.clone: data.native.clone_Array()
|
||
return Array.clone(object);
|
||
|
||
var key = ('clone' in object)
|
||
// test 物件自帶的 clone().
|
||
&& typeof object.clone === 'function';
|
||
|
||
if (key)
|
||
// object.clone(deep);
|
||
return object.clone();
|
||
|
||
key = library_namespace.is_type(object);
|
||
if (key === 'Date')
|
||
return new Date(object.getTime());
|
||
|
||
if (key === 'RegExp')
|
||
// new object.constructor(object)
|
||
return new RegExp(object);
|
||
|
||
var value, target = {};
|
||
for (key in object)
|
||
// 不加入非本 instance,為 prototype 的東西。
|
||
if (Object.hasOwn(object, key)) {
|
||
value = object[key];
|
||
// TODO: 預防 loop, 防止交叉參照/循環參照。
|
||
target[key] = deep ? clone(value, deep) : value;
|
||
}
|
||
return target;
|
||
}
|
||
|
||
_// JSDT:_module_
|
||
.clone = clone;
|
||
|
||
// merge `new_data` to `old_data`, and return merged old_data
|
||
// merge 時,各屬性值以 `old_data` 為基礎,`new_data` 後設定者為準。
|
||
// Will modify old_data!
|
||
function deep_merge_object(old_data, options) {
|
||
var new_data;
|
||
if (Array.isArray(old_data)) {
|
||
// deep_merge_object([old_data, new_data, newer_data, ...,
|
||
// latest data ], options);
|
||
old_data.forEach(function(data, index) {
|
||
if (index === 0) {
|
||
old_data = data;
|
||
} else if (index === this.length - 1) {
|
||
new_data = data;
|
||
} else {
|
||
old_data = deep_merge_object([ old_data, data ], options);
|
||
}
|
||
});
|
||
} else {
|
||
// deep_merge_object(old_data, new_data);
|
||
new_data = options;
|
||
}
|
||
|
||
// ----------------------------
|
||
|
||
if (typeof old_data !== 'object' || old_data === null || !new_data) {
|
||
return new_data || old_data;
|
||
}
|
||
|
||
// ----------------------------
|
||
|
||
function merge_property_of_object(sub_new_data, sub_old_data) {
|
||
for ( var property in sub_new_data) {
|
||
// 將 property in sub_new_data 設定至
|
||
// old_value=sub_old_data[property];
|
||
merge_property(property, sub_new_data, sub_old_data);
|
||
}
|
||
return sub_old_data;
|
||
}
|
||
|
||
// 以 sub_old_data[property] 為基礎,將 sub_new_data[property] merge/overwrite
|
||
// 到 sub_old_data[property]
|
||
// 最終指定 sub_new_data[property] = old_value;
|
||
function merge_property(property, sub_new_data, sub_old_data) {
|
||
// assert: property in sub_new_data
|
||
var new_value = sub_new_data[property];
|
||
|
||
// assert: typeof old_value === 'object'
|
||
if (typeof new_value !== 'object' || !(property in sub_old_data)) {
|
||
// using new_value, overwrite old value
|
||
sub_old_data[property] = new_value;
|
||
return;
|
||
}
|
||
|
||
// assert: property in sub_old_data
|
||
var old_value = sub_old_data[property];
|
||
|
||
if (Array.isArray(new_value)) {
|
||
if (Array.isArray(old_value)) {
|
||
// 對於 {Object}old_value 先複製到 `old_value`
|
||
Object.keys(new_value).forEach(
|
||
// merge 像 new_value.a=1
|
||
function(sub_property) {
|
||
if (!library_namespace.is_digits(sub_property)) {
|
||
merge_property(sub_property, new_value, old_value);
|
||
}
|
||
});
|
||
|
||
old_value.append(new_value);
|
||
} else if (typeof old_value === 'object') {
|
||
// assert: library_namespace.is_Object(old_value)
|
||
merge_property_of_object(new_value, old_value);
|
||
} else {
|
||
// new_value.push(old_value);
|
||
sub_old_data[property] = new_value;
|
||
}
|
||
return;
|
||
}
|
||
|
||
// assert: library_namespace.is_Object(old_value) &&
|
||
// library_namespace.is_Object(new_value)
|
||
|
||
if (typeof old_value !== 'object') {
|
||
// 考慮 new_value 與 old_value 型態不同的情況。
|
||
sub_old_data[property] = new_value;
|
||
} else {
|
||
merge_property_of_object(new_value, old_value);
|
||
}
|
||
}
|
||
|
||
// assert: library_namespace.is_Object(old_data)
|
||
return merge_property_of_object(new_data, old_data);
|
||
}
|
||
|
||
_.deep_merge_object = deep_merge_object;
|
||
|
||
/**
|
||
* get the quote index of specified string.<br />
|
||
* 輸入('"','dh"fdgfg')得到2:指向"的位置.
|
||
*
|
||
* @param {String}string
|
||
* @param {String}quote
|
||
* ['"/],[/]可能不太適用,除非將/[/]/→/[\/]/
|
||
* @returns
|
||
* @since 2004/5/5
|
||
*/
|
||
function index_of_quote(string, quote) {
|
||
var i, l = 0, m;
|
||
if (!quote)
|
||
quote = '"';
|
||
while ((i = string.indexOf(quote, l)) > 0
|
||
&& string.charAt(i - 1) === '\\') {
|
||
m = string.slice(l, i - 2).match(/(\\+)$/);
|
||
if (m && m[1].length % 2)
|
||
break;
|
||
else
|
||
l = i + 1;
|
||
}
|
||
return i;
|
||
}
|
||
|
||
/**
|
||
* <code>
|
||
{var a=[],b,t='',i;a[20]=4,a[12]=8,a[27]=4,a[29]=4,a[5]=6,a.e=60,a.d=17,a.c=1;alert(a);b=sortValue(a);alert(a+'\n'+b);for(i in b)t+='\n'+b[i]+' '+a[b[i]];alert(t);}
|
||
</code>
|
||
*/
|
||
// 依值排出key array…起碼到現在,我還看不出此函數有啥大功用。
|
||
// array,否則會出現error! mode=1:相同value的以','合併,mode=2:相同value的以array填入
|
||
function sortValue(a, mode) {
|
||
var s = [], r = [], i, j, b, k = [];
|
||
// 使用(i in n)的方法,僅有數字的i會自動排序;這樣雖不必用sort(),但數字亦會轉成字串。
|
||
for (i in a)
|
||
if ((b = isNaN(i) ? i : parseFloat(i)),
|
||
//
|
||
typeof s[j = isNaN(j = a[i]) ? j : parseFloat(j)] === 'undefined')
|
||
k.push(j), s[j] = b;
|
||
else if (typeof s[j] === 'object')
|
||
s[j].push(b);
|
||
else
|
||
s[j] = [ s[j], b ];
|
||
// 注意:sort 方法會在原地排序 Array 物件。
|
||
for (i = 0, k.sort(library_namespace.ascending); i < k.length; i++)
|
||
if (typeof (b = s[k[i]]) === 'object')
|
||
if (mode === 1)
|
||
// b.join(',')與''+b效能相同
|
||
r.push(b.join(','));
|
||
else if (mode === 2)
|
||
r.push(b);
|
||
else
|
||
for (j in b)
|
||
r.push(b[j]);
|
||
else
|
||
r.push(b);
|
||
return r;
|
||
}
|
||
|
||
/**
|
||
* <code>
|
||
2005/7/18 21:26
|
||
依照所要求的index(sortByIndex_I)對array排序。
|
||
sortByIndex_Datatype表某index為數字/字串或function
|
||
先設定sortByIndex_I,sortByIndex_Datatype再使用array.sort(sortByIndex);
|
||
|
||
example:
|
||
var array=[
|
||
'123 avcf 334',
|
||
'131 hj 562',
|
||
'657 gfhj 435',
|
||
'131 ajy 52',
|
||
'345 fds 562',
|
||
'52 gh 435',
|
||
];
|
||
sortByIndex_I=[0,1],sortByIndex_Datatype={0:1,2:1};
|
||
for(i in array)array[i]=array[i].split(' ');
|
||
array.sort(sortByIndex);
|
||
alert(array.join('\n'));
|
||
</code>
|
||
*/
|
||
var sortByIndex_I, sortByIndex_Datatype;
|
||
function sortByIndex(a, b) {
|
||
// alert(a+'\n'+b);
|
||
for (var i = 0, n; i < sortByIndex_I.length; i++)
|
||
if (sortByIndex_Datatype[n = sortByIndex_I[i]]) {
|
||
if (typeof sortByIndex_Datatype[n] === 'function') {
|
||
if (n = sortByIndex_Datatype[n](a[n], b[n]))
|
||
return n;
|
||
} else if (n = a[n] - b[n])
|
||
return n;
|
||
} else if (a[n] != b[n])
|
||
return a[n] > b[n] ? 1 : -1;
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* <code>
|
||
2005/7/18 21:26
|
||
依照所要求的 index 對 array 排序,傳回排序後的 index array。
|
||
**假如設定了 separator,array 的元素會先被 separator 分割!
|
||
|
||
example:
|
||
var array=[
|
||
'123 avcf 334',
|
||
'131 hj 562',
|
||
'657 gfhj 435',
|
||
'131 ajy 52',
|
||
'345 fds 562',
|
||
'52 gh 435',
|
||
];
|
||
alert(getIndexSortByIndex(array,' ',[0,1],[0,2]));
|
||
alert(array.join('\n')); // 已被 separator 分割!
|
||
|
||
</code>
|
||
*/
|
||
function getIndexSortByIndex(array, separator, indexArray, isNumberIndex) {
|
||
// 判定與事前準備(設定sortByIndex_I,sortByIndex_Datatype)
|
||
if (typeof indexArray === 'number')
|
||
sortByIndex_I = [ indexArray ];
|
||
else if (typeof indexArray !== 'object'
|
||
|| indexArray.constructor !== Array)
|
||
sortByIndex_I = [ 0 ];
|
||
else
|
||
sortByIndex_I = indexArray;
|
||
var i, sortByIndex_A = [];
|
||
sortByIndex_Datatype = Object.create(null);
|
||
if (typeof isNumberIndex === 'object') {
|
||
if (isNumberIndex.constructor === Array) {
|
||
sortByIndex_Datatype = Object.create(null);
|
||
for (i = 0; i < isNumberIndex.length; i++)
|
||
sortByIndex_Datatype[isNumberIndex[i]] = 1;
|
||
} else
|
||
sortByIndex_Datatype = isNumberIndex;
|
||
for (i in sortByIndex_Datatype)
|
||
if (isNaN(sortByIndex_Datatype[i])
|
||
|| parseInt(sortByIndex_Datatype[i]) !== sortByIndex_Datatype[i])
|
||
delete sortByIndex_Datatype[i];
|
||
}
|
||
if (typeof array !== 'object')
|
||
return;
|
||
|
||
// main work: 可以不用重造 array 資料的話..
|
||
for (i in array) {
|
||
if (separator)
|
||
array[i] = array[i].split(separator);
|
||
sortByIndex_A.push(i);
|
||
}
|
||
sortByIndex_A.sort(function(a, b) {
|
||
return sortByIndex(array[a], array[b]);
|
||
});
|
||
|
||
/**
|
||
* <code>
|
||
for: 重造array資料
|
||
var getIndexSortByIndexArray=array;
|
||
for(i in getIndexSortByIndexArray){
|
||
if(separator)getIndexSortByIndexArray[i]=getIndexSortByIndexArray[i].split(separator);
|
||
sortByIndex_A.push(i);
|
||
}
|
||
sortByIndex_A.sort(function (a,b){return sortByIndex(getIndexSortByIndexArray[a],getIndexSortByIndexArray[b]);});
|
||
</code>
|
||
*/
|
||
|
||
return sortByIndex_A;
|
||
}
|
||
|
||
/**
|
||
* <code>
|
||
simpleWrite('char_frequency report3.txt',char_frequency(simpleRead('function.js')+simpleRead('accounts.js')));
|
||
{var t=reduceCode(simpleRead('function.js')+simpleRead('accounts.js'));simpleWrite('char_frequency source.js',t),simpleWrite('char_frequency report.txt',char_frequency(t));} // 所費時間:十數秒(…太扯了吧!)
|
||
</code>
|
||
*/
|
||
_// JSDT:_module_
|
||
.
|
||
/**
|
||
* 測出各字元的出現率。 普通使用字元@0-127:9-10,13,32-126,reduce後常用:9,32-95,97-125
|
||
*
|
||
* @param {String}
|
||
* text 文檔
|
||
* @return
|
||
* @_memberOf _module_
|
||
*/
|
||
char_frequency = function char_frequency(text) {
|
||
var i, a, c = [], d, t = '' + text, l = t.length, used = '', unused = '', u1 = -1, u2 = u1;
|
||
for (i = 0; i < l; i++)
|
||
if (c[a = t.charCodeAt(i)])
|
||
c[a]++;
|
||
else
|
||
c[a] = 1;
|
||
for (i = u1; i < 256; i++)
|
||
if (c[i]) {
|
||
if (u2 + 1 === i)
|
||
used += ',' + i, unused += (u2 < 0 ? '' : '-' + u2);
|
||
u1 = i;
|
||
} else {
|
||
if (u1 + 1 === i)
|
||
unused += ',' + i, used += (u1 < 0 ? '' : '-' + u1);
|
||
u2 = i;
|
||
}
|
||
// 若是reduceCode()的程式,通常在120項左右。
|
||
for (i = 0, t = 'used:' + used.substr(1) + '\nunused:'
|
||
+ unused.substr(1) + '\n', d = sortValue(c, 2).reverse(); i < d.length; i++) {
|
||
t += NewLine
|
||
+ (a = d[i])
|
||
+ '['
|
||
+ fromCharCode(a).replace(/\0/g, '\\0').replace(/\r/g,
|
||
'\\r').replace(/\n/g, '\\n').replace(/\t/g, '\\t')
|
||
+ ']' + ': ' + (a = c[typeof a === 'object' ? a[0] : a])
|
||
+ ' ' + (100 * a / l);
|
||
// .5%以上者←選購
|
||
// if(200*v<l)break;
|
||
}
|
||
alert(t);
|
||
return t;
|
||
};
|
||
|
||
/**
|
||
* <code>
|
||
flag:
|
||
(flag&1)==0 HTML tag, 表情符號等不算一個字
|
||
(flag&1)==1 將 HTML tag 全部消掉
|
||
(flag&2)==1 連表情符號等也算一個字
|
||
|
||
可讀性/適讀性
|
||
http://en.wikipedia.org/wiki/Flesch-Kincaid_Readability_Test
|
||
http://en.wikipedia.org/wiki/Gunning_fog_index
|
||
Gunning-Fog Index:簡單的來說就是幾年的學校教育才看的懂你的文章,數字越低代表越容易閱讀,若是高於17那表示你的文章太難囉,需要研究生才看的懂,我是6.08,所以要受過6.08年的學校教育就看的懂囉。
|
||
Flesch Reading Ease:這個指數的分數越高,表示越容易了解,一般標準的文件大約介於60~70分之間。
|
||
Flesch-Kincaid grade level:和Gunning-Fog Index相似,分數越低可讀性越高,越容易使閱讀者了解,至於此指數和Gunning-Fog Index有何不同,網站上有列出計算的演算法,有興趣的人可以比較比較。
|
||
|
||
DO.normalize(): 合併所有child成一String, may crash IE6 Win! http://www.quirksmode.org/dom/tests/splittext.html
|
||
</code>
|
||
*/
|
||
_// JSDT:_module_
|
||
.
|
||
/**
|
||
* 計算字數 count words. word_count
|
||
*
|
||
* @param {String}
|
||
* text 文檔
|
||
* @param {Number}
|
||
* flag 文檔格式/處理方法
|
||
*
|
||
* @return {Number} 字數
|
||
*
|
||
* @see https://zh.wikipedia.org/wiki/User:%E5%B0%8F%E8%BA%8D/Wordcount.js
|
||
*
|
||
* @_memberOf _module_
|
||
*/
|
||
count_word = function count_word(text, flag) {
|
||
var is_HTML = flag & 1;
|
||
|
||
// is HTML object
|
||
if (typeof text === 'object')
|
||
if (text.innerText)
|
||
text = text.innerText, is_HTML = false;
|
||
else if (text.innerHTML)
|
||
text = text.innerHTML, is_HTML = true;
|
||
|
||
if (typeof text !== 'string')
|
||
return 0;
|
||
|
||
// 和perl不同,JScript常抓不到(.*?)之後還接特定字串的東西,大概因為沒有s。(.*?)得改作([\s\S]*?)?
|
||
// 或者該加/img?
|
||
if (is_HTML)
|
||
text = text.replace(/<!--([\s\S]*?)-->/g, '').replace(
|
||
/<[\s\n]*\/?[\s\n]*[a-z][^<>]*>/gi, '');
|
||
|
||
if (flag & 2)
|
||
text = text.replace(
|
||
// 連表情符號或 '(~。),' / 破折號 '——' / 刪節號 '……' 等標點符號也算一個字
|
||
/[\+\-–*\\\/?!,;.<>{}\[\]@#$%^&_|"'~`—…、,;。!?:()()「」『』“”‘’]{2,}/g,
|
||
';');
|
||
|
||
return text
|
||
// 去掉注解用的括弧、書名號、專名號、印刷符號等
|
||
.replace(/[()()《》〈〉*#]+/g, '')
|
||
|
||
// 將數字改成單一字母。
|
||
.replace(/\d*\.?\d+([^.]|$)/g, '0$1')
|
||
/**
|
||
* 將整組物理量值加計量單位略縮成單一字母。<br />
|
||
* The general rule of the International Bureau of Weights and Measures
|
||
* (BIPM) is that the numerical value always precedes the unit, and a
|
||
* space is always used to separate the unit from the number, e.g., "23
|
||
* °C" (not "23°C" or "23° C").
|
||
*
|
||
* @see <a href="http://en.wikipedia.org/wiki/ISO_31-0#Expressions"
|
||
* accessdate="2012/7/28 0:42">ISO 31-0</a>, <a
|
||
* href="http://lamar.colostate.edu/~hillger/faq.html#spacing"
|
||
* accessdate="2012/7/28 0:42">FAQ: Frequently Asked Questions
|
||
* about the metric system</a>.
|
||
*/
|
||
.replace(/\d+\s*[a-zA-Z°]+(\s*\/\s*(\d+\s*)?[a-zA-Z°]+)?/g, '0')
|
||
// 長度過長時,極耗時間。e.g., ...\d{500000}...
|
||
// .replace(/\d*\.?\d+\s*[a-zA-Z°]+(\s*\/\s*(\d*\.?\d+\s*)?[a-zA-Z°]+)?/g,'0')
|
||
|
||
// https://en.wikipedia.org/wiki/Punctuation_of_English
|
||
// Do not count punctuations of English.
|
||
.replace(/[,;:.?!\-–"'()⟨⟩«»\[\]{}<>$%#@~`^&*\\\/⁄+=|]+/g, '')
|
||
// 將英文、數字、單位等改成單一字母。[.]: 縮寫。[\/]: m/s 之類。
|
||
// a's: 1
|
||
// http://en.wikibooks.org/wiki/Unicode/Character_reference/0000-0FFF
|
||
// http://zh.wikipedia.org/wiki/Unicode%E5%AD%97%E7%AC%A6%E5%88%97%E8%A1%A8
|
||
.replace(/[\wÀ-ÖØ-öø-ȳ]{2,}/g, 'w')
|
||
|
||
// date/time or number
|
||
.replace(/[\d:+\-–\.\/,]{2,}/g, '0')
|
||
// 再去掉*全部*空白
|
||
.replace(/[\s\n]+/g, '')
|
||
// return text.length;
|
||
.length;
|
||
};
|
||
|
||
_// JSDT:_module_
|
||
.
|
||
/**
|
||
* 運算式值的二進位表示法
|
||
* 已最佳化:5.82s/100000次dec_to_bin(20,8)@300(?)MHz,2.63s/100000次dec_to_bin(20)@300(?)MHz
|
||
*
|
||
* @param {Number}
|
||
* number number
|
||
* @param places
|
||
* places,字元數,使用前置0來填補回覆值
|
||
* @return
|
||
* @example<code>
|
||
{var d=new Date,i,b;for(i=0;i<100000;i++)b=dec_to_bin(20);alert(gDate(new Date-d));}
|
||
</code>
|
||
* @_memberOf _module_
|
||
*/
|
||
dec_to_bin = function dec_to_bin(number, places) {
|
||
if (places && number + 1 < (1 << places)) {
|
||
var h = '', b = number.toString(2), i = b.length;
|
||
for (; i < places; i++)
|
||
h += '0';
|
||
return h + b;
|
||
}
|
||
// native code 還是最快!
|
||
return number.toString(2);
|
||
|
||
if (false) {
|
||
// 上兩代:慢
|
||
// 不用'1:0',型別轉換比較慢.不用i,多一個變數會慢很多
|
||
var b = '', c = 1;
|
||
for (p = p && n < (p = 1 << p) ? p : n + 1; c < p; c <<= 1)
|
||
b = (c & n ? '1' : '0') + b;
|
||
return b;
|
||
|
||
// 上一代:慢
|
||
if (p && n + 1 < (1 << p)) {
|
||
var h = '', c = 1, b = n.toString(2);
|
||
while (c <= n)
|
||
c <<= 1;
|
||
while (c < p)
|
||
c <<= 1, h += '0';
|
||
return h + (n ? n.toString(2) : '');
|
||
}
|
||
}
|
||
};
|
||
|
||
/**
|
||
* <code>
|
||
value (Array)=value,(Object)value=
|
||
[null]=value 累加=value
|
||
value=[null] value=''
|
||
|
||
type: value type ['=','][int|float|_num_]
|
||
: 前段
|
||
以[']或["]作分隔重定義指定號[=]與分隔號[,]
|
||
: 後段
|
||
數字表累加
|
||
'int'表整數int,累加1
|
||
'float'表示浮點數float,累加.1 bug:應該用.to_fixed()
|
||
不輸入或非數字表示string
|
||
|
||
mode
|
||
_.set_Object_value.F.object
|
||
_.set_Object_value.F.array(10進位/當做數字)
|
||
number: key部分之base(10進位,16進位等)
|
||
|
||
example:
|
||
set_Object_value('UTCDay','Sun,Mon,Tue,Wed,Thu,Fri,Sat','int'); // 自動從0開始設,UTCDay.Tue=2
|
||
set_Object_value('UTCDay','Sun,Mon,Tue,Wed,Thu,Fri,Sat'); // UTCDay.Sun=UTCDay.Fri=''
|
||
set_Object_value('add','a=3,b,c,d',2); // 累加2。add.b=5
|
||
set_Object_value('add','a,b,c,d',1,_.set_Object_value.F.array); // add[2]='c'
|
||
set_Object_value('add','4=a,b,c,d',2,_.set_Object_value.F.array); // 累加2。add[8]='c'
|
||
|
||
</code>
|
||
*/
|
||
_// JSDT:_module_
|
||
.
|
||
/**
|
||
* 設定object之值,輸入item=[value][,item=[value]..]。<br />
|
||
* value未設定會自動累加。<br />
|
||
* 使用前不必需先宣告…起碼在現在的JS版本中
|
||
*
|
||
* @param obj
|
||
* object name that need to operate at
|
||
* @param value
|
||
* valueto set
|
||
* @param type
|
||
* 累加 / value type
|
||
* @param mode
|
||
* mode / value type
|
||
* @return
|
||
* @_memberOf _module_
|
||
*/
|
||
set_Object_value = function set_Object_value(obj, value, type, mode) {
|
||
if (!value || typeof obj !== 'string')
|
||
return;
|
||
|
||
var a, b, i = 0, p = '=', sp = ',', e = "if(typeof " + obj
|
||
+ "!='object')" + obj + "=new " + (mode ?
|
||
// "[]":"{}"
|
||
// Array之另一種表示法:[value1,value2,..],
|
||
// Object之另一種表示法:{key1:value1,key2:value2,..}
|
||
"Array" : "Object") + ";",
|
||
// l: item, n: value to 累加
|
||
n, Tint = false, cmC = '\\u002c', eqC = '\\u003d';
|
||
if (type) {
|
||
if (typeof a === 'string') {
|
||
a = type.charAt(0);
|
||
if (a === '"' || a === "'") {
|
||
a = type.split(a);
|
||
p = a[1], sp = a[2], type = a[3];
|
||
}
|
||
}
|
||
if (type === 'int')
|
||
type = 1, Tint = true;
|
||
else if (type === 'float')
|
||
type = .1;
|
||
else if (isNaN(type))
|
||
type = 0;
|
||
else if (type === parseInt(type))
|
||
type = parseInt(type), Tint = true;
|
||
else
|
||
// t被設成累加數
|
||
type = parseFloat(type);
|
||
}
|
||
// else t = 1;
|
||
|
||
if (typeof value === 'string')
|
||
value = value.split(sp);
|
||
// escape regex characters from jQuery
|
||
cmC = new RegExp(cmC.replace(
|
||
/([\.\\\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, "\\$1"), 'g'),
|
||
eqC = new RegExp(eqC.replace(
|
||
/([\.\\\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, "\\$1"),
|
||
'g');
|
||
|
||
if (type)
|
||
// n: 現在count到..
|
||
n = -type;
|
||
|
||
for (; i < value.length; i++) {
|
||
if (value[i].indexOf(p) === NOT_FOUND)
|
||
// if(v[i].indexOf(p)==NOT_FOUND&&m)v[i]=p+v[i];
|
||
value[i] = mode ? p + value[i] : value[i] + p;
|
||
if (mode && value[i] === p) {
|
||
n += type;
|
||
continue;
|
||
}
|
||
a = value[i].split(p);
|
||
if (!mode && !a[0])
|
||
// 去掉不合理的(Array可能有NaN index,所以不設條件。)
|
||
continue;
|
||
a[0] = a[0].replace(cmC, ',').replace(eqC, '='), a[1] = a[1]
|
||
.replace(cmC, ',').replace(eqC, '=');
|
||
if (type)
|
||
if (mode) {
|
||
if (!a[0])
|
||
a[0] = (n += type);
|
||
else if (!isNaN(b = mode > 0 ? parseInt(a[0], mode) : a[0]))
|
||
n = Tint ? (a[0] = parseInt(b)) : parseFloat(b);
|
||
} else if (!a[1])
|
||
a[1] = (n += type);
|
||
else if (!isNaN(a[1]))
|
||
n = Tint ? parseInt(a[1]) : parseFloat(a[1]);
|
||
if (!type || Tint && isNaN(b = parseInt(a[1]))
|
||
|| isNaN(b = parseFloat(a[1])))
|
||
b = a[1];
|
||
a = a[0];
|
||
e += obj + '['
|
||
+ (!type || isNaN(a) ? library_namespace.dQuote(a) : a)
|
||
+ ']='
|
||
+ (!type || isNaN(b) ? library_namespace.dQuote(b) : b)
|
||
+ ';';
|
||
}
|
||
|
||
try {
|
||
// if(o=='kk')alert(e.slice(0,500));
|
||
// 因為沒想到其他方法可存取Global的object,只好使用eval...
|
||
// 可以試試obj=set_Object_value(0,..){this=new Aaaray/Object}
|
||
return library_namespace.eval_code(e);
|
||
} catch (e) {
|
||
library_namespace.error('Error @ ' + obj);
|
||
library_namespace.error(e);
|
||
return;
|
||
}
|
||
};
|
||
|
||
_.set_Object_value.F = {
|
||
// object is default
|
||
'object' : 0,
|
||
'array' : -1
|
||
};
|
||
|
||
_// JSDT:_module_
|
||
.
|
||
/**
|
||
* 將字串組分作 Object
|
||
*
|
||
* @param {String}
|
||
* value_set 字串組, e.g., 'a=12,b=34'
|
||
* @param assignment_char
|
||
* char to assign values, e.g., '='
|
||
* @param end_char
|
||
* end char of assignment
|
||
* @return
|
||
* @since 2006/9/6 20:55, 2010/4/12 23:06:04
|
||
* @_memberOf _module_
|
||
*/
|
||
split_String_to_Object = function split_String_to_Object(value_set,
|
||
assignment_char, end_char) {
|
||
if (typeof value_set !== 'string' || !value_set)
|
||
return {};
|
||
|
||
value_set = value_set.split(end_char || /[,;]/);
|
||
|
||
if (!assignment_char)
|
||
assignment_char = /[=:]/;
|
||
|
||
var a, o = {}, _e = 0, l = value_set.length;
|
||
for (; _e < l; _e++) {
|
||
// http://msdn.microsoft.com/library/en-us/jscript7/html/jsmthsplit.asp
|
||
a = value_set[_e].split(assignment_char, 2);
|
||
if (false)
|
||
library_namespace.debug(value_set[_e] + '\n' + a[0] + ' '
|
||
+ a[1], 2);
|
||
if (a[0] !== '')
|
||
o[a[0]] = a[1];
|
||
}
|
||
return o;
|
||
};
|
||
|
||
/**
|
||
* <code>
|
||
2003/10/1 15:46
|
||
比較string:m,n從起頭開始相同字元數
|
||
return null: 格式錯誤,-1: !m||!n
|
||
若一開始就不同:0
|
||
|
||
|
||
TODO:
|
||
|
||
test starting with
|
||
|
||
2009/2/7 7:51:58
|
||
看來測試 string 的包含,以 .indexOf() 最快。
|
||
即使是比較 s.length 為極小常數的情況亦復如此
|
||
|
||
下面是快到慢:
|
||
|
||
// long, short
|
||
var contain_substring = [ function(l, s) {
|
||
var a = 0 == l.indexOf(s);
|
||
return a;
|
||
}, function(l, s) {
|
||
return 0 == l.indexOf(s);
|
||
}, function(l, s) {
|
||
return s == l.slice(0, s.length);
|
||
}, function(l, s) {
|
||
return l.match(s);
|
||
}, function(l, s) {
|
||
for (var i = 0; i < s.length; i++)
|
||
if (s.charAt(i) != l.charAt(i))
|
||
return 0;
|
||
return 1;
|
||
} ];
|
||
|
||
function test_contain_substring() {
|
||
for (var i = 0; i < contain_substring.length; i++) {
|
||
var t = new Date;
|
||
for (var j = 0; j < 50000; j++) {
|
||
contain_substring[i]('sdfgjk;sh*dn\\fj;kgsamnd nwgu!eoh;nfgsj;g',
|
||
'sdfgjk;sh*dn\\fj;kgsamnd nwgu!');
|
||
contain_substring[i]('sdbf6a89* /23hsauru', 'sdbf6a89* /23');
|
||
}
|
||
sl(i + ': ' + (new Date - t));
|
||
}
|
||
}
|
||
|
||
|
||
// 極小常數的情況:
|
||
// long,short
|
||
var contain_substring = [ function(l, s) {
|
||
var a = 0 == l.indexOf(s);
|
||
return a;
|
||
}, function(l, s) {
|
||
return 0 == l.indexOf(s);
|
||
}, function(l, s) {
|
||
return s == l.slice(0, 1);
|
||
}, function(l, s) {
|
||
return s.charAt(0) == l.charAt(0);
|
||
}, function(l, s) {
|
||
return l.match(/^\//);
|
||
} ];
|
||
|
||
function test_contain_substring() {
|
||
for (var i = 0; i < contain_substring.length; i++) {
|
||
var t = new Date;
|
||
for (var j = 0; j < 50000; j++) {
|
||
contain_substring[i]('a:\\sdfg.dfg\\dsfg\\dsfg', '/');
|
||
contain_substring[i]('/dsfg/adfg/sadfsdf', '/');
|
||
}
|
||
sl(i + ': ' + (new Date - t));
|
||
}
|
||
}
|
||
|
||
</code>
|
||
*/
|
||
|
||
_// JSDT:_module_
|
||
.
|
||
/**
|
||
* test if 2 string is at the same length.<br />
|
||
* strcmp: String.prototype.localeCompare
|
||
*
|
||
* @param s1
|
||
* string 1
|
||
* @param s2
|
||
* string 2
|
||
* @return
|
||
* @_memberOf _module_
|
||
*/
|
||
same_length = function same_length(s1, s2) {
|
||
if (typeof m !== 'string' || typeof n !== 'string')
|
||
return;
|
||
if (!s1 || !s2)
|
||
return 0;
|
||
|
||
var i = s1.length, b = 0, s = s2.length;
|
||
if (i < s) {
|
||
if (
|
||
// m === n.slice(0, i = m.length)
|
||
0 === s2.indexOf(s1))
|
||
return i;
|
||
} else if (
|
||
// s2==s1.slice(0,i=s2.length)
|
||
i = s, 0 === s1.indexOf(s2))
|
||
return i;
|
||
|
||
// sl('*same_length: start length: '+i);
|
||
while ((i = (i + 1) >> 1) > 1 && (s = s2.substr(b, i))) {
|
||
if (false)
|
||
sl('same_length: ' + i + ',' + b + '; [' + m.substr(b) + '], ['
|
||
+ s + '] of [' + n + ']');
|
||
if (s1.indexOf(s, b) === b)
|
||
b += i;
|
||
if (false) {
|
||
sl('*same_length: ' + i + ',' + b + '; [' + m.charAt(b)
|
||
+ '], [' + n.charAt(b) + '] of [' + n + ']');
|
||
var s_l = i && m.charAt(b) == n.charAt(b) ? b + 1 : b;
|
||
sl('*same_length: ' + s_l + ':' + m.slice(0, s_l) + ',<em>'
|
||
+ m.slice(s_l) + '</em>; ' + n.slice(0, s_l) + ',<em>'
|
||
+ n.slice(s_l) + '</em>');
|
||
}
|
||
}
|
||
return i && s1.charAt(b) === s2.charAt(b) ? b + 1 : b;
|
||
};
|
||
|
||
/**
|
||
* 去除指定字串中重複字元。 remove duplicate characters in a string.
|
||
*
|
||
* @param {String}string
|
||
* 指定字串。
|
||
*
|
||
* @returns 去除重複字元後之字串。
|
||
*/
|
||
function remove_duplicate_characters(string) {
|
||
string = String(string);
|
||
if (!string)
|
||
return '';
|
||
|
||
string = string.split('');
|
||
var i = 0, length = string.length, code_array = [], code;
|
||
for (; i < length; i++) {
|
||
code = string[i].charCodeAt(0);
|
||
if (code in code_array) {
|
||
string[i] = '';
|
||
} else {
|
||
code_array[code] = 1;
|
||
}
|
||
}
|
||
|
||
return string.join('');
|
||
}
|
||
|
||
_// JSDT:_module_
|
||
.remove_duplicate_characters = remove_duplicate_characters;
|
||
|
||
// -----------------------------------------------------------------------------
|
||
|
||
/**
|
||
* 產生將數字轉為 K, M, G 等數量級(order of magnitude)表示方式的轉換器。<br />
|
||
* 原理:先設好各 symbol 使用之下限,比完大小後使用此 symbol。
|
||
*
|
||
* TODO: full test
|
||
*
|
||
* @param {Array}symbol
|
||
* array of {String}symbol
|
||
* @param {Integer}[base]
|
||
* define what power
|
||
* @param {Integer}[index_of_1]
|
||
* 純量的 index。no prefix. 這之前的算做小數。
|
||
* @param {String}intervening
|
||
* intervening string, 將插入於數字與 symbol 間。e.g.,
|
||
* @return {Function} 改變表示方式之轉換器。
|
||
* @return {undefined} 輸入有問題。
|
||
* @requires to_fixed
|
||
* @see <a href="http://www.merlyn.demon.co.uk/js-maths.htm#DTS"
|
||
* accessdate="2012/8/18 12:17">JRS - JavaScript Maths - J R Stockton</a>
|
||
* @_memberOf _module_
|
||
*/
|
||
function set_order_prefix(symbol, base, index_of_1, intervening) {
|
||
if (!Array.isArray(symbol) || !symbol.length)
|
||
return;
|
||
|
||
if (!base)
|
||
base = 10;
|
||
if (!index_of_1) {
|
||
index_of_1 = 0;
|
||
if (symbol[0])
|
||
symbol.unshift('');
|
||
}
|
||
|
||
var magnitude = 1, length = symbol.length, value = new Array(length), index = index_of_1;
|
||
|
||
// 先設定好各數量級(order of magnitude)之大小。
|
||
while (++index < length) {
|
||
magnitude *= base;
|
||
value[index] = magnitude;
|
||
}
|
||
if (index_of_1) {
|
||
index = index_of_1;
|
||
magnitude = 1;
|
||
while (index--) {
|
||
magnitude /= base;
|
||
value[index] = magnitude;
|
||
}
|
||
}
|
||
value[index_of_1] = 1;
|
||
|
||
if (intervening) {
|
||
for (index = 0; index < length; index++) {
|
||
symbol[index] = intervening + symbol[index];
|
||
}
|
||
}
|
||
|
||
library_namespace.debug('magnitude array of base ' + base + ': ['
|
||
+ value + ']', 1, 'set_order_prefix');
|
||
library_namespace.debug('prefixes of base ' + base + ': [' + symbol
|
||
+ ']', 1, 'set_order_prefix');
|
||
|
||
// cache 引入: symbol, value, length.
|
||
|
||
return (
|
||
/**
|
||
* 將數字轉為 K, M, G 等數量級(order of magnitude)表示方式。
|
||
*
|
||
* @param {Number}number
|
||
* 數字純量
|
||
* @param {Number}digits
|
||
* to fixed digit
|
||
* @type {String}
|
||
* @return {String} 指定數量級(order of magnitude)表示方式
|
||
* @requires to_fixed
|
||
*/
|
||
function(number, digits) {
|
||
if (typeof number === 'string')
|
||
number = parseFloat(number.replace(/[, ]/g, ''));
|
||
if (!number || isNaN(number))
|
||
return 0;
|
||
|
||
var l = 0, r = length, i;
|
||
// 直接用比的。 inline binary search.
|
||
// while (l < (i = Math.floor((l + r) / 2))) {
|
||
while (l < (i = (l + r) >> 1)) {
|
||
library_namespace.debug('compare: ' + number + ', [' + i + ']'
|
||
+ value[i], 3, 'set_order_prefix');
|
||
if (number < value[i]) {
|
||
r = i;
|
||
} else {
|
||
l = i;
|
||
}
|
||
}
|
||
library_namespace.debug('index: [' + i + '] ' + value[i] + ', '
|
||
+ symbol[i], 2, 'set_order_prefix');
|
||
|
||
l = number / value[i];
|
||
return to_fixed.call(l, isNaN(digits) || digits < 0 ? (r = Math
|
||
.floor(l)) < 10 ? 2 : r < 100 ? 1 : 0 : digits)
|
||
+ symbol[i];
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 將數字轉為 K, M, G 等 metric prefix / SI prefix 表示方式,例如 6458 轉成 6.31 K。
|
||
*
|
||
* @example <code>
|
||
|
||
CeL.to_1000_prefix(12343454345);
|
||
CeL.to_1000_prefix('12,343,454,345');
|
||
|
||
</code>
|
||
*
|
||
* @returns
|
||
* @see <a href="http://en.wikipedia.org/wiki/Metric_prefix"
|
||
* accessdate="2012/8/18 12:9">Metric prefix</a>, <a
|
||
* href="http://en.wikipedia.org/wiki/International_System_of_Units"
|
||
* accessdate="2012/8/18 12:11">International System of Units</a>, <a
|
||
* href="http://www.bipm.org/en/si/si_brochure/chapter3/prefixes.html"
|
||
* accessdate="2012/8/18 12:10">BIPM - SI prefixes</a>, <a
|
||
* href="http://bmanolov.free.fr/numbers_names.php"
|
||
* accessdate="2012/8/18 12:19">Names of LARGE and small Numbers</a>
|
||
*/
|
||
function to_1000_prefix() {
|
||
var s = 'yzafpnμm kMGTPEZY', i = s.indexOf(' ');
|
||
s = s.split('');
|
||
s[i] = '';
|
||
return set_order_prefix(s, 1000, i, ' ');
|
||
}
|
||
|
||
/**
|
||
* 將數字轉為 Ki, Mi, Gi 等 binary prefix 表示方式,例如 1024 轉成 1Ki。
|
||
*
|
||
* @example <code>
|
||
|
||
CeL.to_1024_prefix(12343454345);
|
||
|
||
</code>
|
||
*
|
||
* @returns
|
||
* @see <a
|
||
* href="http://en.wikipedia.org/wiki/Binary_prefix#IEC_standard_prefixes"
|
||
* accessdate="2012/8/18 11:53">Binary prefix</a>
|
||
*/
|
||
function to_1024_prefix() {
|
||
return set_order_prefix(',Ki,Mi,Gi,Ti,Pi,Ei,Zi,Yi'.split(','), 1024, 0,
|
||
' ');
|
||
}
|
||
|
||
// 不可以 `byte` 為變數名: JsDoc 會失會失效。
|
||
function to_KiB(bytes, type, use_KB) {
|
||
var expression = use_KB ? library_namespace.to_1000_prefix
|
||
: library_namespace.to_1024_prefix, b = bytes + ' byte'
|
||
+ (bytes > 1 ? 's' : '');
|
||
expression = expression(bytes) + 'B';
|
||
|
||
if (type && type.toLowerCase() === 'html') {
|
||
expression = '<span title="' + b + '">' + expression + '</span>';
|
||
} else if (library_namespace.is_Object(type)) {
|
||
expression = {
|
||
span : expression,
|
||
title : b
|
||
};
|
||
} else if (type === '()') {
|
||
expression += ' (' + b + ')';
|
||
}
|
||
|
||
return expression;
|
||
}
|
||
function to_KB(bytes, type) {
|
||
return to_KiB(bytes, type, true);
|
||
}
|
||
|
||
// old alias: CeL.show_KiB(), CeL.show_KB()
|
||
_.to_KiB = to_KiB;
|
||
_.to_KB = to_KB;
|
||
|
||
// TODO: accept '300K' as 300 KiB
|
||
|
||
// 設定 lazy evaluation。
|
||
library_namespace.set_initializor(to_1000_prefix);
|
||
library_namespace.set_initializor(to_1024_prefix);
|
||
|
||
// -----------------------------------------------------------------------------
|
||
|
||
/**
|
||
* for IE3: mode=1:不取空字串<br />
|
||
* .split() appears from Internet Explorer 4.0
|
||
*
|
||
* @see <a
|
||
* href="http://msdn.microsoft.com/en-us/library/s4esdbwz%28v=VS.85%29.aspx"
|
||
* accessdate="2010/4/16 20:4">Version Information (Windows Scripting -
|
||
* JScript)</a>
|
||
*/
|
||
function StringToArray(s, mode) {
|
||
var a = [], last = 0, i;
|
||
while ((i = s.indexOf(sp, last)) !== NOT_FOUND) {
|
||
if (mode === 0 || last !== i)
|
||
a[a.length] = s.slice(last, i);
|
||
last = i + 1;
|
||
}
|
||
if (mode === 0 || last !== s.length)
|
||
a[a.length] = s.slice(last);
|
||
return a;
|
||
}
|
||
|
||
// for IE3: 去除s之空白,包括字與字之間的
|
||
function disposeSpace(s) {
|
||
if (!s)
|
||
return s;
|
||
var r = "", i, last;
|
||
while ((i = s.indexOf(' ', last)) !== NOT_FOUND)
|
||
r += s.slice(last, i), last = i + 1;
|
||
r += s.slice(last);
|
||
return r;
|
||
}
|
||
|
||
// for IE3: 以label,mode:m置換s,先找到先贏
|
||
// 輸入t['$k']=..會有問題,需用t['\\$k']=..
|
||
function changeV(s, l, m) {
|
||
var i, r, re, t; // var I='';
|
||
if (!m)
|
||
m = 'g';
|
||
if (s && (t = l ? l : label))
|
||
for (i in t) {
|
||
// I+=', '+i+'='+t[i];
|
||
re = new RegExp(i, m);
|
||
// r=s.replace(re,t[i]);s=r;
|
||
s = s.replace(re, t[i]);
|
||
}
|
||
// pLog(I.substr(2));
|
||
// pLog('changeV:'+s);
|
||
return s;
|
||
}
|
||
|
||
/**
|
||
* <code>
|
||
// 以label置換s,先找到先贏
|
||
function changeV(s) {
|
||
for (var i, j = 0; j < labelN.length; j++)
|
||
if ((i = s.indexOf(labelN[j])) != NOT_FOUND)
|
||
s = s.slice(0, i) + labelV[j] + s.slice(i + labelN[j].length)
|
||
, j = 0; // search again from beginning
|
||
return s;
|
||
}
|
||
</code>
|
||
*/
|
||
|
||
_// JSDT:_module_
|
||
.get_Object_value = function(o) {
|
||
// if (Array.isArray(o)) return o;
|
||
|
||
// if (!library_namespace.is_Object(o)) return;
|
||
var i, l = [];
|
||
for (i in o)
|
||
l.push(o[i]);
|
||
return l;
|
||
};
|
||
|
||
_// JSDT:_module_
|
||
.
|
||
/**
|
||
* 互換/reverse key/value pairs.
|
||
*
|
||
* @example <code>
|
||
swap_key_value({A:1,B:2,s:4,t:[]}, [], /^[A-Z_\-\d]+$/) === [,'A','B']
|
||
</code>
|
||
* @param {Object|object}pairs
|
||
* key/value pairs
|
||
* @param {Object|Array}[base]
|
||
* 把互換結果放在 base
|
||
* @param {RegExp}[key_filter]
|
||
* 僅放入符合的 key
|
||
* @returns
|
||
*/
|
||
swap_key_value = function(pairs, base, key_filter) {
|
||
if (!base)
|
||
base = {};
|
||
|
||
var k;
|
||
if (library_namespace.is_type(key_filter, 'RegExp')) {
|
||
for (k in pairs)
|
||
if (key_filter.test(k))
|
||
base[pairs[k]] = k;
|
||
} else
|
||
for (k in pairs)
|
||
base[pairs[k]] = k;
|
||
|
||
return base;
|
||
};
|
||
|
||
if (library_namespace.dependency_chain)
|
||
_.dependency_chain = library_namespace.dependency_chain;
|
||
|
||
/**
|
||
* new_Array=[,,]: 可以使用 Array 常值中的空白元素來建立零星稀疏的陣列。
|
||
*/
|
||
|
||
// ---------------------------------------------------------------------//
|
||
// UTF-8 char and bytes.
|
||
/**
|
||
* 計算指定 UTF-8 char code 之 bytes。
|
||
*
|
||
* TODO:<br />
|
||
* 加快速度。
|
||
*
|
||
* @param {Number}code
|
||
* 指定之 UTF-8 char code。
|
||
* @returns {Number} 指定 UTF-8 char code 之 bytes。
|
||
* @see https://en.wikipedia.org/wiki/UTF-8#Description
|
||
*/
|
||
function bytes_of_UTF8_char_code(code) {
|
||
return code < 0x0080 ? 1 : code < 0x0800 ? 2 : code < 0x10000 ? 3
|
||
: code < 0x200000 ? 4 : code < 0x4000000 ? 5
|
||
: code < 0x80000000 ? 6 : 7;
|
||
}
|
||
|
||
/**
|
||
* 計算指定 UTF-8 text 之 bytes。
|
||
*
|
||
* TODO:<br />
|
||
* use Buffer.byteLength
|
||
*
|
||
* @param {String}text
|
||
* 指定之 UTF-8 text。
|
||
* @returns {Number} 指定 UTF-8 text 之 bytes。
|
||
*/
|
||
function byte_count_of_UTF8(text) {
|
||
var i = 0, length = text.length, bytes = 0;
|
||
for (; i < length; i++)
|
||
bytes += bytes_of_UTF8_char_code(text.charCodeAt(i));
|
||
return bytes;
|
||
}
|
||
|
||
/**
|
||
* 將 UTF-8 text 截成指定 byte 長度。
|
||
*
|
||
* @param {String}text
|
||
* 指定之 UTF-8 text。
|
||
* @param {Number}byte_length
|
||
* 指定之 byte 長度。
|
||
* @returns {String} UTF-8 text, length <= byte_length.
|
||
*/
|
||
function cut_UTF8_by_bytes(text, byte_length) {
|
||
var i = 0, length = text.length;
|
||
for (; byte_length > 0 && i < length; i++) {
|
||
byte_length -= bytes_of_UTF8_char_code(text.charCodeAt(i));
|
||
if (byte_length < 0)
|
||
i--;
|
||
}
|
||
return i === length ? text : text.slice(0, i);
|
||
}
|
||
|
||
_.bytes_of_char_code = bytes_of_UTF8_char_code;
|
||
_.byte_count = byte_count_of_UTF8;
|
||
_.cut_by_bytes = cut_UTF8_by_bytes;
|
||
|
||
// ---------------------------------------------------------------------//
|
||
// for bencode & torrent file data.
|
||
|
||
/**
|
||
* [ key_1, value_1, key_2, value_2, key_3, value_3 ]<br /> →<br /> {
|
||
* key_1: value_1, key_2: value_2, key_3: value_3 }
|
||
*
|
||
* @param {Array}list
|
||
* list to convert
|
||
*
|
||
* @returns {Object} pair Object converted
|
||
* @since 2014/7/21 23:17:32
|
||
*/
|
||
function list_to_Object(list) {
|
||
var i = 0, length = list.length, pair = Object.create(null);
|
||
if (length % 2 !== 0)
|
||
library_namespace.warn('list_to_Object: The length (' + length
|
||
+ ') of list is not an even number!');
|
||
|
||
for (; i < length; i += 2) {
|
||
if (typeof list[i] !== 'string')
|
||
library_namespace.warn(
|
||
// Set key to non-string type. e.g., integer
|
||
'Set (' + (typeof list[i]) + ') [' + list[i] + '] as key.');
|
||
if (list[i] in pair)
|
||
library_namespace.warn('Duplicated key: [' + list[i] + ']');
|
||
|
||
library_namespace.debug('pair[' + list[i] + '] = [' + list[i + 1]
|
||
+ ']', 3);
|
||
pair[list[i]] = list[i + 1];
|
||
}
|
||
|
||
return pair;
|
||
}
|
||
|
||
/**
|
||
* parse bencode data
|
||
*
|
||
* @param {String}data
|
||
* bencode data
|
||
* @param {Object}[status]
|
||
* get the parse status
|
||
* @param {Boolean}[is_ASCII]
|
||
* 若設定為真,則當作 ASCII 處理。若設定為假,則當作 UTF-8 處理。
|
||
*
|
||
* @returns
|
||
*
|
||
* @see https://zh.wikipedia.org/wiki/Bencode
|
||
*
|
||
* @since 2014/7/21 23:17:32
|
||
*/
|
||
function parse_bencode(data, status, is_ASCII) {
|
||
|
||
function make_end() {
|
||
// assert: object_now === queue.pop()
|
||
if ((tmp = object_now) !== queue.pop()) {
|
||
library_namespace.error('Bad data structure!');
|
||
// assert: queue.length === 0
|
||
if (queue !== object_now)
|
||
// 回存。
|
||
queue.push(object_now);
|
||
} else {
|
||
if (tmp.d)
|
||
tmp = list_to_Object(tmp);
|
||
// assert: queue.length > 0
|
||
(object_now = queue.at(-1)).push(tmp);
|
||
}
|
||
}
|
||
|
||
// 盡可能不動到 data,因為 data 可能很大。
|
||
var index = 0, tmp, queue = [],
|
||
// 即使在 data 有缺陷的情況下,也盡可能解析出資料。
|
||
// 因此先將 data 設定成 list。
|
||
object_now = queue,
|
||
// 為了多行程,因此這些 pattern 應該放在函數內,不可為 global 為其他行程存取。
|
||
PATTERN_controller = /(.*?)([ldei\d])/g,
|
||
// 為了盡快 match,所以盡可能選擇可能 match 的 pattern,之後再來檢查是否相符。
|
||
PATTERN_integer = /(-?\d*)(\D)/g, PATTERN_string_length = /(\d*)(\D)/g;
|
||
|
||
for (;;) {
|
||
PATTERN_controller.lastIndex = index;
|
||
if (!(tmp = PATTERN_controller.exec(data))) {
|
||
if (index < data.length)
|
||
library_namespace.error('Last data skipped! ('
|
||
+ data.slice(index) + ')');
|
||
break;
|
||
}
|
||
|
||
if (tmp[1]) {
|
||
index += tmp[1].length;
|
||
// control char should be next char.
|
||
library_namespace.error('Some data skipped! (' + tmp[1] + ')');
|
||
}
|
||
|
||
switch (tmp[2]) {
|
||
case 'l':
|
||
// list 列表
|
||
case 'd':
|
||
// dictionary 關聯數組
|
||
++index;
|
||
queue.push(object_now = []);
|
||
if (tmp[2] === 'd')
|
||
object_now.d = true;
|
||
break;
|
||
case 'e':
|
||
// ending
|
||
++index;
|
||
make_end();
|
||
break;
|
||
|
||
case 'i':
|
||
// integer 整數
|
||
PATTERN_integer.lastIndex = ++index;
|
||
tmp = PATTERN_integer.exec(data);
|
||
if (tmp && tmp[2] === 'e') {
|
||
// 確定為 /i\d+e/
|
||
if (!tmp[1])
|
||
library_namespace
|
||
.error('No integer specified ("ie" instead of /i\d+e/)!');
|
||
else if (PATTERN_integer.lastIndex !== index
|
||
+ tmp[0].length)
|
||
library_namespace.error('Some integer data skipped! ('
|
||
+ data.slice(index, PATTERN_integer.lastIndex
|
||
- tmp[0].length) + ')');
|
||
object_now.push(parseInt(tmp[1]));
|
||
index = PATTERN_integer.lastIndex;
|
||
} else {
|
||
// fatal error
|
||
library_namespace.error('Bad integer format! Exit parse!');
|
||
index = data.length;
|
||
}
|
||
break;
|
||
|
||
default:
|
||
// assert: 接下來是 string (\d+:.+) 字串
|
||
PATTERN_string_length.lastIndex = index;
|
||
tmp = PATTERN_string_length.exec(data);
|
||
if (tmp && tmp[2] === ':') {
|
||
// 確定為 /\d+:/
|
||
if (!tmp[1] || !(tmp[1] | 0))
|
||
library_namespace.error('No string length specified! ('
|
||
+ tmp[1] + ')');
|
||
else if (PATTERN_string_length.lastIndex !== index
|
||
+ tmp[0].length)
|
||
library_namespace.error('Some string data skipped! ('
|
||
+ data.slice(index,
|
||
PATTERN_string_length.lastIndex
|
||
- tmp[0].length) + ')');
|
||
if ((index = PATTERN_string_length.lastIndex)
|
||
+ (tmp = tmp[1] | 0) > data.length)
|
||
library_namespace.error(
|
||
//
|
||
'The end of string is beyond the end of data! (ask '
|
||
// remaining
|
||
+ (index + tmp) + ' - data left ' + data.length
|
||
+ ' = lost ' + (index + tmp - data.length)
|
||
+ ')');
|
||
// tmp: length of string.
|
||
library_namespace.debug(index + '+' + tmp, 3);
|
||
if (is_ASCII) {
|
||
object_now.push(data.substr(index, tmp));
|
||
index += tmp;
|
||
} else {
|
||
// 對 UTF-8 (non-ASCII string) 特別處理:
|
||
// 此時因取得 Unicode,所指定之 length >= 實際 length。
|
||
tmp = cut_UTF8_by_bytes(data.substr(index, tmp), tmp);
|
||
object_now.push(tmp);
|
||
index += tmp.length;
|
||
}
|
||
} else {
|
||
// fatal error
|
||
library_namespace.error('Bad string format! Exit parse!');
|
||
index = data.length;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (queue.length > 1)
|
||
library_namespace.warn('Illegal data: 有錯誤或缺陷!');
|
||
while (queue.length > 1 && Array.isArray(queue.at(-1)))
|
||
make_end();
|
||
|
||
if (status)
|
||
if (queue.length !== 1)
|
||
status.error = true;
|
||
|
||
return queue.length === 1 ? queue[0] : queue;
|
||
}
|
||
|
||
/**
|
||
* parse torrent file data
|
||
*
|
||
* @example <code>
|
||
|
||
// @ JScript
|
||
CeL.run('data', function () {
|
||
// http://www.ubuntu.com/download/alternative-downloads
|
||
CeL.log(CeL.parse_torrent('http://releases.ubuntu.com/14.04/ubuntu-14.04-desktop-i386.iso.torrent', true));
|
||
});
|
||
|
||
* </code>
|
||
*
|
||
* @param {String}path
|
||
* torrent file path.
|
||
* @param {Boolean}name_only
|
||
* get the torrent name only.
|
||
*
|
||
* @returns {Object} torrent file data
|
||
* @since 2014/7/21 23:17:32
|
||
*/
|
||
function parse_torrent(path, name_only) {
|
||
// 注意:此方法不可跨 domain!
|
||
// JScript 下,XMLHttpRequest 會將檔案當作 UTF-8 讀取。
|
||
var data = library_namespace.get_file(path);
|
||
var status = Object.create(null);
|
||
if (!data || data.charAt(0) !== 'd') {
|
||
library_namespace.error((data ? 'Illegal' : 'Cannot get')
|
||
+ ' torrent data of [' + path + ']!');
|
||
return;
|
||
}
|
||
library_namespace.debug(data, 4);
|
||
|
||
if (name_only) {
|
||
// a fast way to get torrent name.
|
||
var PATTERN_name = /4:name(\d{1,4}):/, matched = data
|
||
.match(PATTERN_name), index;
|
||
if (matched && (matched = matched[1] | 0) > 0) {
|
||
library_namespace.debug('[' + path + '] length: ' + matched, 3);
|
||
// fix for non-ASCII chars, it will be change to Unicode,
|
||
// and the real length <= length specified.
|
||
if (false) {
|
||
// assert: 'piece length' 恰好在 PATTERN_name 之後。
|
||
index = data.indexOf('12:piece lengthi')
|
||
- PATTERN_name.lastIndex;
|
||
return data.substr(PATTERN_name.lastIndex, index > 0 ? Math
|
||
.min(index, matched) : matched);
|
||
}
|
||
return cut_UTF8_by_bytes(data.substr(PATTERN_name.lastIndex,
|
||
matched), matched);
|
||
}
|
||
return;
|
||
}
|
||
|
||
data = parse_bencode(data, status);
|
||
|
||
return data;
|
||
}
|
||
|
||
_.list_to_Object = list_to_Object;
|
||
_.parse_bencode = parse_bencode;
|
||
_.parse_torrent = parse_torrent;
|
||
|
||
// ---------------------------------------------------------------------//
|
||
|
||
function is_natural(value) {
|
||
return value >= 1 && Math.floor(value) === value;
|
||
}
|
||
// is_non_negative
|
||
function is_natural_or_0(value) {
|
||
return value >= 0 && Math.floor(value) === value;
|
||
}
|
||
function is_integer(value) {
|
||
return Math.floor(value) === value;
|
||
}
|
||
import_options.filters = {
|
||
number : {
|
||
range : function in_range(value) {
|
||
return (typeof this[0] !== 'number' || this[0] <= value)
|
||
&& (typeof this[1] !== 'number' || value <= this[1]);
|
||
},
|
||
|
||
// 自然數 natural numbers ℕ*, ℕ+, ℕ>0, ℤ+
|
||
natural : is_natural,
|
||
'ℕ' : is_natural,
|
||
'natural+0' : is_natural_or_0,
|
||
// 'ℕ0'
|
||
'ℕ+0' : is_natural_or_0,
|
||
// 整數
|
||
integer : is_integer,
|
||
'ℤ' : is_integer
|
||
},
|
||
string : {
|
||
IPv4 : /^[12]?\d{1,2}\.[12]?\d{1,2}\.[12]?\d{1,2}\.[12]?\d{1,2}$/,
|
||
|
||
// RFC2822 :
|
||
// http://regexlib.com/DisplayPatterns.aspx
|
||
// /^(?:[a-z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+\/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])$/,
|
||
email :
|
||
// http://www.regular-expressions.info/email.html
|
||
// /^[a-z0-9!#$%&'*+\/=?^_`{|}~-]+(\.[a-z0-9!#$%&'*+\/=?^_`{|}~-]+)*@([a-z0-9]([a-z0-9-]*[a-z0-9])?\.)+([a-z]{2}|com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum)\b$/i,
|
||
/^[a-z0-9+_~-]+(\.[a-z0-9+_~-]+)*@([a-z\d]([a-z\d-]*[a-z\d])?\.)+([a-z]{2}|com|org|net)\b$/i,
|
||
|
||
// 十進位小數
|
||
decimal : /^[+\-]?(?:\d+|\d*\.\d+)$/,
|
||
// 數字
|
||
digits : library_namespace.is_digits
|
||
}
|
||
};
|
||
|
||
/**
|
||
* @inner
|
||
*/
|
||
function string_options_to_normalizer(string_normalizer) {
|
||
var normalizer = Object.create(null);
|
||
|
||
string_normalizer.split('|').forEach(function(type) {
|
||
var matched = type.match(/^([a-z]+):([\s\S]+)$/);
|
||
if (!matched) {
|
||
normalizer[type] = true;
|
||
return;
|
||
}
|
||
|
||
type = matched[1];
|
||
// condition
|
||
var _normalizer = matched[2];
|
||
if (_normalizer in import_options.filters[type]) {
|
||
_normalizer = import_options.filters[type][_normalizer];
|
||
|
||
} else if (type === 'number' && (matched = _normalizer.match(
|
||
// @see CeL.date.parse_period.PATTERN
|
||
// [ all, min, max ]
|
||
/([+\-]?\d+(?:\.\d+)?)?\s*[–~-—─~〜﹣至]\s*([+\-]?\d+(?:\.\d+)?)?/))) {
|
||
_normalizer = import_options.filters[type]
|
||
// this: [ min, max ]
|
||
.range.bind([ matched[1] && +matched[1],
|
||
//
|
||
matched[2] && +matched[2] ]);
|
||
|
||
} else if (type === 'string') {
|
||
matched = _normalizer.match(library_namespace.PATTERN_RegExp);
|
||
if (matched) {
|
||
_normalizer = new RegExp(matched[1], matched[2]);
|
||
} else {
|
||
_normalizer = _normalizer.split(';');
|
||
}
|
||
|
||
} else {
|
||
library_namespace.warn('import_options: Invalid normalizer: '
|
||
//
|
||
+ _normalizer);
|
||
}
|
||
|
||
normalizer[type] = _normalizer;
|
||
});
|
||
|
||
return normalizer;
|
||
}
|
||
|
||
/**
|
||
* 將 options 裡面可使用之選項(依照 options_normalizer 之定義),篩選、正規化並提取至 target。
|
||
*
|
||
* @param {Object}options
|
||
* @param {Object|String|Function}options_normalizer
|
||
* options allowed
|
||
* @param {Object}[target]
|
||
* target options to modify
|
||
* @returns
|
||
*
|
||
* @see function verify_arg(key, value)
|
||
* @see function generate_argument_condition(condition) @ CeL.application.net.work_crawler.arguments
|
||
* @see _.default_verify_pattern @ CeL.interact.form.select_input
|
||
*/
|
||
function import_options(options, options_normalizer, target) {
|
||
options = library_namespace.setup_options(options);
|
||
if (!target) {
|
||
target = Object.create(null);
|
||
}
|
||
|
||
for ( var key in options) {
|
||
var value = options[key];
|
||
var normalizer = options_normalizer && options_normalizer[key];
|
||
if (typeof normalizer === 'string') {
|
||
// e.g., 'boolean|string:changed;multi_parts_changed'
|
||
normalizer = string_options_to_normalizer(normalizer);
|
||
// console.trace(normalizer);
|
||
}
|
||
|
||
if (typeof normalizer === 'object') {
|
||
var type = Array.isArray(value) ? 'Array'
|
||
//
|
||
: library_namespace.is_RegExp(value) ? 'RegExp'
|
||
//
|
||
: library_namespace.is_Date(value) ? 'Date' : null;
|
||
if (!type || !(type in normalizer)
|
||
&& !((type = type.toLowerCase()) in normalizer)) {
|
||
type = typeof value;
|
||
}
|
||
|
||
if (type in normalizer) {
|
||
normalizer = normalizer[type];
|
||
} else {
|
||
// invalid value type
|
||
continue;
|
||
}
|
||
}
|
||
|
||
if (normalizer === undefined && options_normalizer) {
|
||
// invalid key / parameter name
|
||
continue;
|
||
}
|
||
|
||
if (typeof normalizer === 'function') {
|
||
value = normalizer(value);
|
||
} else if (!fit_filter(normalizer, value)) {
|
||
// treat normalizer as filter
|
||
// invalid value
|
||
continue;
|
||
}
|
||
|
||
if (value !== import_options.INVALID
|
||
// && value !== undefined
|
||
) {
|
||
target[key] = value;
|
||
}
|
||
}
|
||
|
||
// console.trace(target);
|
||
return target;
|
||
}
|
||
|
||
import_options.INVALID = {
|
||
invalid_value : true
|
||
};
|
||
|
||
_.import_options = import_options;
|
||
|
||
/**
|
||
* validity value
|
||
*
|
||
* generate_filter: fit_filter.bind(null, filter)
|
||
*
|
||
* @param {Function|RegExp|Array}filter
|
||
* @param value
|
||
* value to test
|
||
*
|
||
* @returns value fits the filter
|
||
*
|
||
* @see function receive() @ CeL.application.net.wiki.page
|
||
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/text#pattern
|
||
*/
|
||
function fit_filter(filter, value) {
|
||
// if (filter === true) return true;
|
||
if (typeof filter === 'boolean' || filter === null
|
||
|| typeof filter === 'undefined')
|
||
return filter;
|
||
|
||
// 驗證 pattern
|
||
if (library_namespace.is_RegExp(filter))
|
||
return filter.test(value);
|
||
|
||
if (typeof filter === 'function')
|
||
return filter(value);
|
||
|
||
if (typeof filter === 'string') {
|
||
return filter === value;
|
||
// return filter.includes(value);
|
||
// return String(value).includes(filter);
|
||
}
|
||
|
||
// e.g., ['A','B','C']
|
||
if (Array.isArray(filter))
|
||
return filter.includes(value);
|
||
|
||
if (false && library_namespace.is_Object(filter))
|
||
return value in filter;
|
||
|
||
throw new TypeError('Invalid filter');
|
||
}
|
||
|
||
_.fit_filter = fit_filter;
|
||
|
||
// ---------------------------------------------------------------------//
|
||
|
||
return (_// JSDT:_module_
|
||
);
|
||
}
|