/** * @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 = {}; /** * 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() */ /** * 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.
* 輸入('"','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; } /** * {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);} */ // 依值排出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; } /** * 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')); */ 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; } /** * 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 分割! */ 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]); }); /** * 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]);}); */ return sortByIndex_A; } /** * 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));} // 所費時間:十數秒(…太扯了吧!) */ _// 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 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 */ _// 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(//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') /** * 將整組物理量值加計量單位略縮成單一字母。
* 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 ISO 31-0, FAQ: Frequently Asked Questions * about the metric system. */ .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 {var d=new Date,i,b;for(i=0;i<100000;i++)b=dec_to_bin(20);alert(gDate(new Date-d));} * @_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) : ''); } } }; /** * 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' */ _// JSDT:_module_ . /** * 設定object之值,輸入item=[value][,item=[value]..]。
* value未設定會自動累加。
* 使用前不必需先宣告…起碼在現在的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; }; /** * 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)); } } */ _// JSDT:_module_ . /** * test if 2 string is at the same length.
* 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) + ',' + m.slice(s_l) + '; ' + n.slice(0, s_l) + ',' + n.slice(s_l) + ''); } } 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)表示方式的轉換器。
* 原理:先設好各 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 JRS - JavaScript Maths - J R Stockton * @_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 CeL.to_1000_prefix(12343454345); CeL.to_1000_prefix('12,343,454,345'); * * @returns * @see Metric prefix, International System of Units, BIPM - SI prefixes, Names of LARGE and small Numbers */ 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 CeL.to_1024_prefix(12343454345); * * @returns * @see Binary prefix */ 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 = '' + expression + ''; } 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:不取空字串
* .split() appears from Internet Explorer 4.0 * * @see Version Information (Windows Scripting - * JScript) */ 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; } /** * // 以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; } */ _// 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 swap_key_value({A:1,B:2,s:4,t:[]}, [], /^[A-Z_\-\d]+$/) === [,'A','B'] * @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:
* 加快速度。 * * @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:
* 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 ]

{ * 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 // @ 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)); }); * * * @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_ ); }