mirror of
				https://scm.univ-tours.fr/22107988t/rappaurio-sae501_502.git
				synced 2025-11-04 13:35:22 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			3268 lines
		
	
	
		
			101 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			3268 lines
		
	
	
		
			101 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/**
 | 
						||
 * @name CeL function for compatibility
 | 
						||
 * @fileoverview 本檔案包含了 new ECMAScript standard 標準已規定,但先前版本未具備的內建物件功能;以及相容性 test
 | 
						||
 *               專用的 functions。<br />
 | 
						||
 *               ES6 shim / polyfill<br />
 | 
						||
 *               部分標準功能已經包含於 ce.js。
 | 
						||
 * 
 | 
						||
 * @see see Set, Map @ _structure/dependency_chain.js
 | 
						||
 * 
 | 
						||
 * 注意: 本檔案可能會被省略執行,因此不應有標準之外的設定,應將之放置於 data.native。
 | 
						||
 * 
 | 
						||
 * More examples: see /_test suite/test.js
 | 
						||
 * 
 | 
						||
 * @since
 | 
						||
 * @see https://www.audero.it/blog/2016/12/05/monkey-patching-javascript/
 | 
						||
 * @see https://tc39.es/proposal-collection-methods/
 | 
						||
 * @see https://github.com/tc39/proposals
 | 
						||
 * @see https://github.com/Financial-Times/polyfill-library/tree/master/polyfills
 | 
						||
 * @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> http://espadrine.github.io/New-In-A-Spec/es2017/
 | 
						||
 */
 | 
						||
 | 
						||
'use strict';
 | 
						||
// 'use asm';
 | 
						||
 | 
						||
// --------------------------------------------------------------------------------------------
 | 
						||
 | 
						||
typeof CeL === 'function' && CeL.run({
 | 
						||
	// module name
 | 
						||
	name : 'data.code.compatibility',
 | 
						||
 | 
						||
	// This module should NOT require other modules!
 | 
						||
	// nothing required.
 | 
						||
	// 本 module 為許多 module 所用,應盡可能勿 requiring 其他 module。
 | 
						||
	// require : '',
 | 
						||
 | 
						||
	// 設定不匯出的子函式。
 | 
						||
	// no_extend : '*',
 | 
						||
 | 
						||
	// 為了方便格式化程式碼,因此將 module 函式主體另外抽出。
 | 
						||
	code : module_code
 | 
						||
});
 | 
						||
 | 
						||
function module_code(library_namespace) {
 | 
						||
 | 
						||
	/**
 | 
						||
	 * null module constructor
 | 
						||
	 * 
 | 
						||
	 * @class 標準已規定,但先前版本未具備的功能;以及相容性 test 專用的 functions。
 | 
						||
	 */
 | 
						||
	var _// JSDT:_module_
 | 
						||
	= function() {
 | 
						||
		// null module constructor
 | 
						||
	};
 | 
						||
 | 
						||
	/**
 | 
						||
	 * for JSDT: 有 prototype 才會將之當作 Class
 | 
						||
	 */
 | 
						||
	_// JSDT:_module_
 | 
						||
	.prototype = {};
 | 
						||
 | 
						||
	// cache
 | 
						||
	var Array_slice = Array.prototype.slice,
 | 
						||
	// cache
 | 
						||
	set_method = library_namespace.set_method,
 | 
						||
	/**
 | 
						||
	 * The index return when not found.<br />
 | 
						||
	 * 未發現之index。未找到時的 index。基本上與程式碼設計合一,僅表示名義,不可更改。(=== -1)
 | 
						||
	 * 
 | 
						||
	 * @type {Number}
 | 
						||
	 * @constant
 | 
						||
	 */
 | 
						||
	NOT_FOUND = ''.indexOf('_');
 | 
						||
 | 
						||
	// ----------------------------------------------------------------------------------------------------------------------------------------------------------//
 | 
						||
 | 
						||
	/**
 | 
						||
	 * TODO:
 | 
						||
	 * 
 | 
						||
	 * http://www.comsharp.com/GetKnowledge/zh-CN/It_News_K875.aspx<br />
 | 
						||
	 * 8進制數字表示被禁止, 010 代表 10 而不是 8
 | 
						||
	 * 
 | 
						||
	 * http://jquerymobile.com/gbs/
 | 
						||
	 */
 | 
						||
 | 
						||
	var main_version = 0, full_version = '';
 | 
						||
	// for IE/NS only
 | 
						||
	if (typeof window !== 'undefined' && window.ScriptEngine) {
 | 
						||
		library_namespace.debug(library_namespace
 | 
						||
				.is_type(ScriptEngineMajorVersion), 2);
 | 
						||
		main_version = window.ScriptEngineMajorVersion() + '.'
 | 
						||
				+ window.ScriptEngineMinorVersion();
 | 
						||
		full_version = window.ScriptEngine() + ' ' + main_version + '.'
 | 
						||
				+ window.ScriptEngineBuildVersion();
 | 
						||
		main_version = Number(main_version);
 | 
						||
	} else if (false
 | 
						||
	// java test: 加了下面這段在 FF3 會召喚出 java! IE中沒有java object.
 | 
						||
	// old: object, new: function (?)
 | 
						||
	// && (typeof java == 'function' || typeof java == 'object') && java
 | 
						||
	) {
 | 
						||
		library_namespace.debug("Today is "
 | 
						||
				+ java.text.SimpleDateFormat("EEEE-MMMM-dd-yyyy").format(
 | 
						||
						new java.util.Date()));
 | 
						||
		if (main_version = java.lang.System.getProperty('os.name') + ' '
 | 
						||
				+ java.lang.System.getProperty('os.version') + ' '
 | 
						||
				+ java.lang.System.getProperty('os.arch'))
 | 
						||
			full_version = main_version;
 | 
						||
		else
 | 
						||
			main_version = 0;
 | 
						||
	}
 | 
						||
 | 
						||
	if (full_version)
 | 
						||
		library_namespace.debug('Script engine: ' + full_version);
 | 
						||
 | 
						||
	/**
 | 
						||
	 * 版本檢查.
 | 
						||
	 * 
 | 
						||
	 * @param {Number}version
 | 
						||
	 *            最低 version
 | 
						||
	 */
 | 
						||
	function check_version(version) {
 | 
						||
		if (!library_namespace.is_digits(version) || version < 5)
 | 
						||
			version = 5;
 | 
						||
 | 
						||
		if (typeof WScript !== 'undefined' && WScript.Version < version) {
 | 
						||
			// WScript.FullName, WScript.Path
 | 
						||
			var Locale = library_namespace.env.locale, promptTitle = Locale == 0x411 ? 'アップグレードしませんか?'
 | 
						||
					: '請升級', promptC = Locale == 0x411 ? "今使ってる "
 | 
						||
					+ WScript.Name
 | 
						||
					+ " のバージョンは古過ぎるから、\nMicrosoft Windows スクリプト テクノロジ Web サイトより\nバージョン "
 | 
						||
					+ WScript.Version + " から " + version + " 以上にアップグレードしましょう。"
 | 
						||
					: "正使用的 " + WScript.Name
 | 
						||
							+ " 版本過舊,\n請至 Microsoft Windows 網站將版本由 "
 | 
						||
							+ WScript.Version + " 升級到 " + version + " 以上。", url = /* Locale==0x411? */"http://www.microsoft.com/japan/developer/scripting/default.htm";
 | 
						||
			if (1 == WScript.Popup(promptC, 0, promptTitle, 1 + 48))
 | 
						||
				WshShell.Run(url);
 | 
						||
			WScript.Quit(1);
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	// ----------------------------------------------------------------------------------------------------------------------------------------------------------//
 | 
						||
	// global object
 | 
						||
 | 
						||
	var globalThis = library_namespace.env.global;
 | 
						||
	if (false && (!globalThis.global || globalThis.global !== globalThis)) {
 | 
						||
		// Object.defineProperty() defined in base.js
 | 
						||
		Object.defineProperty(globalThis, 'global', {
 | 
						||
			configurable : true,
 | 
						||
			enumerable : false,
 | 
						||
			value : globalThis,
 | 
						||
			writable : false
 | 
						||
		});
 | 
						||
	}
 | 
						||
 | 
						||
	if (!globalThis.globalThis || globalThis.globalThis !== globalThis) {
 | 
						||
		// e.g., node-v10.19.0
 | 
						||
		// Object.defineProperty() defined in base.js
 | 
						||
		Object.defineProperty(globalThis, 'globalThis', {
 | 
						||
			configurable : true,
 | 
						||
			enumerable : false,
 | 
						||
			value : globalThis,
 | 
						||
			writable : false
 | 
						||
		});
 | 
						||
	}
 | 
						||
 | 
						||
	set_method(globalThis, {
 | 
						||
		encodeURI : escape,
 | 
						||
		decodeURI : unescape,
 | 
						||
		encodeURIComponent : encodeURI,
 | 
						||
		decodeURIComponent : decodeURI,
 | 
						||
		isNaN : function(value) {
 | 
						||
			// parseFloat(value)
 | 
						||
			// var a = typeof value == 'number' ? value : parseInt(value);
 | 
						||
			// alert(typeof a + ',' + a + ',' + (a === a));
 | 
						||
 | 
						||
			/**
 | 
						||
			 * 變數可以與其本身比較。如果比較結果不相等,則它會是 NaN。原因是 NaN 是唯一與其本身不相等的值。
 | 
						||
			 * 
 | 
						||
			 * A reliable way for ECMAScript code to test if a value X is a NaN
 | 
						||
			 * is an expression of the form X !== X. The result will be true if
 | 
						||
			 * and only if X is a NaN.
 | 
						||
			 */
 | 
						||
			// return /* typeof value=='number'&& */a != a;
 | 
						||
			value = Number(value);
 | 
						||
			return value !== value;
 | 
						||
		},
 | 
						||
		// isFinite(null) === true
 | 
						||
		isFinite : function isFinite(value) {
 | 
						||
			// https://tc39.es/ecma262/#sec-isfinite-number
 | 
						||
			return Number.isFinite(Number(value));
 | 
						||
		}
 | 
						||
	}, null);
 | 
						||
 | 
						||
	// ----------------------------------------------------------------------------------------------------------------------------------------------------------//
 | 
						||
	// Object.*
 | 
						||
 | 
						||
	if (typeof Object.freeze === 'function')
 | 
						||
		try {
 | 
						||
			// https://github.com/es-shims/es5-shim/blob/master/es5-sham.js
 | 
						||
			Object.freeze(function() {
 | 
						||
			});
 | 
						||
		} catch (e) {
 | 
						||
			var Object_freeze = Object.freeze;
 | 
						||
			Object.freeze = function freeze_Object(object) {
 | 
						||
				return typeof object == 'function' ? Object_freeze(object)
 | 
						||
						: object;
 | 
						||
			};
 | 
						||
		}
 | 
						||
 | 
						||
	if (!Object.setPrototypeOf) {
 | 
						||
		var Object_getPrototypeOf, Object_setPrototypeOf;
 | 
						||
 | 
						||
		// test prototype chain
 | 
						||
		// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain
 | 
						||
		if (typeof {}.__proto__ === 'object') {
 | 
						||
			// http://ejohn.org/blog/objectgetprototypeof/
 | 
						||
			// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/GetPrototypeOf
 | 
						||
			// http://perfectionkills.com/how-ecmascript-5-still-does-not-allow-to-subclass-an-array/
 | 
						||
			Object_getPrototypeOf = function(object) {
 | 
						||
				return object.__proto__;
 | 
						||
			};
 | 
						||
			Object_setPrototypeOf = function(object, prototype) {
 | 
						||
				object.__proto__ = prototype;
 | 
						||
				return object;
 | 
						||
			};
 | 
						||
 | 
						||
			set_method(Object, {
 | 
						||
				getPrototypeOf : Object_getPrototypeOf,
 | 
						||
				setPrototypeOf : Object_setPrototypeOf
 | 
						||
			});
 | 
						||
 | 
						||
		} else if ({}.constructor && {}.constructor.prototype) {
 | 
						||
			Object_getPrototypeOf = function(object) {
 | 
						||
				return object.constructor.prototype;
 | 
						||
			};
 | 
						||
			Object_setPrototypeOf = function(object, prototype) {
 | 
						||
				object.constructor.prototype = prototype;
 | 
						||
				return object;
 | 
						||
			};
 | 
						||
 | 
						||
			set_method(Object, {
 | 
						||
				getPrototypeOf : Object_getPrototypeOf,
 | 
						||
				setPrototypeOf : Object_setPrototypeOf
 | 
						||
			});
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	// IE 8, JScript 5.8.23141 中,DOM 可能沒有 .hasOwnProperty()。
 | 
						||
	var hasOwnProperty = Object.prototype.hasOwnProperty
 | 
						||
	// Object.getOwnPropertyDescriptor(object, 'property')
 | 
						||
	|| function hasOwnProperty(key) {
 | 
						||
		try {
 | 
						||
			// Object.getPrototypeOf() 返回給定對象的原型。
 | 
						||
			var prototype = Object.getPrototypeOf(this);
 | 
						||
			return (key in this)
 | 
						||
			//
 | 
						||
			&& (!(key in prototype) || this[key] !== prototype[key]);
 | 
						||
		} catch (e) {
 | 
						||
			// TODO: handle exception
 | 
						||
		}
 | 
						||
	};
 | 
						||
 | 
						||
	// Object.keys(): get Object keys, 列出對象中所有可以枚舉的屬性 (Enumerable Only)
 | 
						||
	// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/keys
 | 
						||
	// 可用來防止 .prototype 帶來之 properties。e.g., @ IE
 | 
						||
	// cf. Object.getOwnPropertyNames() 會列出對象中所有可枚舉以及不可枚舉的屬性 (enumerable or
 | 
						||
	// non-enumerable)
 | 
						||
	function Object_keys(object) {
 | 
						||
		var keys = [];
 | 
						||
		try {
 | 
						||
			for ( var key in object) {
 | 
						||
				if (Object.hasOwn(object, key))
 | 
						||
					keys.push(key);
 | 
						||
			}
 | 
						||
 | 
						||
		} catch (e) {
 | 
						||
			// TODO: handle exception
 | 
						||
		}
 | 
						||
 | 
						||
		return keys;
 | 
						||
	}
 | 
						||
 | 
						||
	/**
 | 
						||
	 * @deprecatred
 | 
						||
	 */
 | 
						||
	function getPropertyNames() {
 | 
						||
		return Object.keys(this);
 | 
						||
	}
 | 
						||
 | 
						||
	function getOwnPropertyDescriptor(object, property) {
 | 
						||
		if (Object.hasOwn(object, property)) {
 | 
						||
			return {
 | 
						||
				configurable : true,
 | 
						||
				enumerable : true,
 | 
						||
				value : object[property],
 | 
						||
				writable : true
 | 
						||
			};
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	function getOwnPropertyDescriptors(object) {
 | 
						||
		var descriptors = Object.create(null);
 | 
						||
		// for...in 循環也枚舉原型鏈中的屬性
 | 
						||
		for ( var property in object) {
 | 
						||
			var descriptor = Object.getOwnPropertyDescriptor(object, property);
 | 
						||
			if (descriptor)
 | 
						||
				descriptors[property] = descriptor;
 | 
						||
		}
 | 
						||
		return descriptors;
 | 
						||
	}
 | 
						||
 | 
						||
	// https://tc39.github.io/ecma262/#sec-object.values
 | 
						||
	// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/values
 | 
						||
	// http://www.2ality.com/2015/11/stage3-object-entries.html
 | 
						||
	// Object.values()
 | 
						||
	function Object_values(object) {
 | 
						||
		var values = [];
 | 
						||
		for (var keys = Object.keys(object), index = 0, length = keys.length; index < length; index++) {
 | 
						||
			values.push(object[keys[index]]);
 | 
						||
		}
 | 
						||
		return values;
 | 
						||
 | 
						||
		return Object.keys(object).map(function(key) {
 | 
						||
			return object[key];
 | 
						||
		});
 | 
						||
	}
 | 
						||
 | 
						||
	// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries
 | 
						||
	// Object.entries()
 | 
						||
	function Object_entries(object) {
 | 
						||
		var entries = [];
 | 
						||
		for (var keys = Object.keys(object), index = 0, length = keys.length; index < length; index++) {
 | 
						||
			var key = keys[index];
 | 
						||
			entries.push([ key, object[key] ]);
 | 
						||
		}
 | 
						||
		return entries;
 | 
						||
 | 
						||
		return Object.keys(object).map(function(key) {
 | 
						||
			// [ key, value ]
 | 
						||
			return [ key, object[key] ];
 | 
						||
		});
 | 
						||
	}
 | 
						||
 | 
						||
	// Object.fromEntries()
 | 
						||
	function fromEntries(iterable) {
 | 
						||
		var object = Object.create(null);
 | 
						||
 | 
						||
		iterable.forEach(function(pair) {
 | 
						||
			// pair = [ key, value ]
 | 
						||
			object[pair[0]] = pair[1];
 | 
						||
		});
 | 
						||
 | 
						||
		return object;
 | 
						||
	}
 | 
						||
 | 
						||
	set_method(Object, {
 | 
						||
		// 鎖定物件。
 | 
						||
		// Object.seal()
 | 
						||
		seal : function seal(object) {
 | 
						||
			// 無法以舊的語法實現。
 | 
						||
			return object;
 | 
						||
		},
 | 
						||
		// Object.isSealed()
 | 
						||
		isSealed : function isSealed(object) {
 | 
						||
			// 若欲更嚴謹些,可依照經驗法則,避開無法測試的 write-only 物件、或一改變就會產生後續影響的 object,
 | 
						||
			// 並對 object 作變更測試,確保 object 真的具有不可變更之性質。
 | 
						||
			return false;
 | 
						||
		},
 | 
						||
		// Object.preventExtensions()
 | 
						||
		preventExtensions : function preventExtensions(object) {
 | 
						||
			// 無法以舊的語法實現。
 | 
						||
			return object;
 | 
						||
		},
 | 
						||
		// Object.isExtensible()
 | 
						||
		isExtensible : function isExtensible(object) {
 | 
						||
			return true;
 | 
						||
		},
 | 
						||
 | 
						||
		// Object.freeze()
 | 
						||
		freeze : function freeze(object) {
 | 
						||
			// 無法以舊的語法實現。
 | 
						||
			return object;
 | 
						||
		},
 | 
						||
		// Object.isFrozen()
 | 
						||
		isFrozen : function isFrozen(object) {
 | 
						||
			// 無法以舊的語法實現。
 | 
						||
			return false;
 | 
						||
		},
 | 
						||
 | 
						||
		// Object.is(): Return SameValue(value_1, value_2).
 | 
						||
		// 以 SameValue Algorithm 判斷。
 | 
						||
		is : function is(value_1, value_2) {
 | 
						||
			return value_1 === value_2 ? value_1 !== 0
 | 
						||
			// check +0 and -0
 | 
						||
			|| 1 / value_1
 | 
						||
			// 為 JsDoc 換行。
 | 
						||
			=== 1 / value_2
 | 
						||
			// check NaN. May use Number.isNaN() as well.
 | 
						||
			: value_1 !== value_1 && value_2 !== value_2;
 | 
						||
		},
 | 
						||
 | 
						||
		// Object.fromEntries()
 | 
						||
		fromEntries : fromEntries,
 | 
						||
 | 
						||
		// Object.keys(): get Object keys, 列出對象中所有可以枚舉的屬性 (enumerable only)
 | 
						||
		keys : Object_keys,
 | 
						||
		values : Object_values,
 | 
						||
		entries : Object_entries,
 | 
						||
 | 
						||
		// Object.hasOwn(object, property)
 | 
						||
		// https://github.com/tc39/proposal-accessible-object-hasownproperty
 | 
						||
		hasOwn : function hasOwn(object, property) {
 | 
						||
			return hasOwnProperty.call(object, property);
 | 
						||
		},
 | 
						||
 | 
						||
		// Object.getOwnPropertyDescriptor()
 | 
						||
		getOwnPropertyDescriptor : getOwnPropertyDescriptor,
 | 
						||
		// Object.getOwnPropertyDescriptors()
 | 
						||
		getOwnPropertyDescriptors : getOwnPropertyDescriptors,
 | 
						||
		// Object.getOwnPropertyNames()
 | 
						||
		// 會列出對象中所有可枚舉以及不可枚舉的屬性 (enumerable or non-enumerable)
 | 
						||
		// 列出的比 Object.keys() 多
 | 
						||
		getOwnPropertyNames : Object_keys
 | 
						||
	});
 | 
						||
 | 
						||
	// 會造成幾乎每個使用 for(.. in Object),而不是使用 Object.keys() 的,都出現問題。
 | 
						||
	if (false)
 | 
						||
		set_method(Object.prototype, {
 | 
						||
			// getPropertyNames : getPropertyNames,
 | 
						||
			hasOwnProperty : hasOwnProperty
 | 
						||
		});
 | 
						||
 | 
						||
	// ----------------------------------------------------------------------------------------------------------------------------------------------------------//
 | 
						||
	// Array.*
 | 
						||
 | 
						||
	// Array.prototype.at(), String.prototype.at(), typed_array.at()
 | 
						||
	// https://github.com/tc39/proposal-relative-indexing-method#polyfill
 | 
						||
	function get_item_at(index) {
 | 
						||
		index = ToInteger(index);
 | 
						||
		var length = this.length;
 | 
						||
		// Allow negative indexing from the end
 | 
						||
		if (index < 0)
 | 
						||
			index += length;
 | 
						||
		// incase (a=[])[-3]=3;a.at(-3);
 | 
						||
		if (0 <= index && index < length) {
 | 
						||
			// typeof this === 'object' @ HTA (HTML Application) @ Windows 10
 | 
						||
			// return this instanceof String ? this.charAt(index) : this[index];
 | 
						||
			return this.charAt ? this.charAt(index) : this[index];
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	// 稀疏矩陣 (sparse matrix) 用的 Array.prototype.some()
 | 
						||
	// 要到 index > 1e7 比較感覺得出來。
 | 
						||
	// e.g.,
 | 
						||
	// a=[];a[1e7]=2;alert(a.some(function(v){return v===2}));
 | 
						||
	// a=[];a[1e7]=2;alert(a.sparse_some(function(v){return v===2}));
 | 
						||
	function sparse_some(callback, thisArg) {
 | 
						||
		for ( var index in this)
 | 
						||
			if (!isNaN(index))
 | 
						||
				if (thisArg ? callback.call(thisArg, this[index], index, this)
 | 
						||
				// 不採用 .call() 以加速執行。
 | 
						||
				: callback(this[index], index, this))
 | 
						||
					return true;
 | 
						||
		return false;
 | 
						||
	}
 | 
						||
 | 
						||
	// 稀疏矩陣 (sparse matrix) 用的 Array.prototype.every()
 | 
						||
	// 要到 index > 1e7 比較感覺得出來。
 | 
						||
	function sparse_every(callback, thisArg) {
 | 
						||
		for ( var index in this)
 | 
						||
			if (!isNaN(index))
 | 
						||
				if (!(thisArg ? callback
 | 
						||
						.call(thisArg, this[index], index, this)
 | 
						||
				// 不採用 .call() 以加速執行。
 | 
						||
				: callback(this[index], index, this)))
 | 
						||
					return false;
 | 
						||
		return true;
 | 
						||
	}
 | 
						||
 | 
						||
	// 測試 for ( ... in array ) 時,會依順序提供 index。
 | 
						||
	if (typeof Array.prototype.some !== 'function')
 | 
						||
		(function() {
 | 
						||
			var array = [], result = [];
 | 
						||
			array[4] = 4;
 | 
						||
			array[2] = 2;
 | 
						||
			array[0] = 0;
 | 
						||
			for ( var index in array)
 | 
						||
				if (!isNaN(index))
 | 
						||
					result.push(index);
 | 
						||
			// CeL.log(result.join());
 | 
						||
 | 
						||
			if (library_namespace.env.sequenced_Array = result.join() === '0,2,4')
 | 
						||
				set_method(Array.prototype, {
 | 
						||
					sparse_some : sparse_some,
 | 
						||
					sparse_every : sparse_every
 | 
						||
				});
 | 
						||
		})();
 | 
						||
 | 
						||
	set_method(Array, {
 | 
						||
		// Array.of()
 | 
						||
		of : function of() {
 | 
						||
			return Array_slice.call(arguments);
 | 
						||
		}
 | 
						||
	});
 | 
						||
 | 
						||
	function normalize_position(position, length) {
 | 
						||
		return position < 0 && (position += length) < 0 ? 0
 | 
						||
		//
 | 
						||
		: (position |= 0) > length ? length : position;
 | 
						||
	}
 | 
						||
 | 
						||
	// Array.prototype.copyWithin(target, start[, end = this.length])
 | 
						||
	function copyWithin(target, start, end) {
 | 
						||
		var length = this.length;
 | 
						||
		start = normalize_position(start, length);
 | 
						||
		end = normalize_position(end || length, length);
 | 
						||
		target = normalize_position(target, length);
 | 
						||
		if (target < start || end <= target)
 | 
						||
			while (start < end)
 | 
						||
				this[target++] = this[start++];
 | 
						||
		else if (target !== start) {
 | 
						||
			if (length < target + start) {
 | 
						||
				end -= target - start;
 | 
						||
				target = length;
 | 
						||
			} else
 | 
						||
				target += start;
 | 
						||
			// 反向(後→前) copy,確保 copy from 不曾被本操作汙染過。
 | 
						||
			while (start < end)
 | 
						||
				this[--target] = this[--end];
 | 
						||
		}
 | 
						||
		return this;
 | 
						||
	}
 | 
						||
 | 
						||
	// https://github.com/tc39/proposal-flatMap
 | 
						||
	function flat(depth) {
 | 
						||
		if (depth === undefined)
 | 
						||
			depth = 1;
 | 
						||
		else
 | 
						||
			depth |= 0;
 | 
						||
 | 
						||
		var array = [];
 | 
						||
 | 
						||
		function push_item(_this, _depth) {
 | 
						||
			_depth--;
 | 
						||
			_this.forEach(function(item) {
 | 
						||
				if (_depth >= 0 && Array.isArray(item))
 | 
						||
					push_item(item, _depth);
 | 
						||
				else
 | 
						||
					array.push(item);
 | 
						||
			});
 | 
						||
		}
 | 
						||
		push_item(this, depth);
 | 
						||
 | 
						||
		return array;
 | 
						||
	}
 | 
						||
 | 
						||
	function flatMap(mapperFunction, thisArg) {
 | 
						||
		if (false) {
 | 
						||
			return this.map(
 | 
						||
					thisArg ? mapperFunction.bind(thisArg) : mapperFunction)
 | 
						||
					.flat();
 | 
						||
		}
 | 
						||
 | 
						||
		var array = [];
 | 
						||
 | 
						||
		this.forEach(function(item) {
 | 
						||
			item = thisArg ? mapperFunction.call(thisArg, item)
 | 
						||
					: mapperFunction(item);
 | 
						||
			if (Array.isArray(item))
 | 
						||
				item.forEach(function(_item) {
 | 
						||
					array.push(_item);
 | 
						||
				});
 | 
						||
			else
 | 
						||
				array.push(item);
 | 
						||
		});
 | 
						||
 | 
						||
		return array;
 | 
						||
	}
 | 
						||
 | 
						||
	set_method(Array.prototype, {
 | 
						||
		/** <code>Array.prototype.copyWithin(target, start [ , end ])</code> */
 | 
						||
		copyWithin : copyWithin,
 | 
						||
 | 
						||
		// https://github.com/tc39/proposal-relative-indexing-method#polyfill
 | 
						||
		at : get_item_at,
 | 
						||
 | 
						||
		// Array.prototype.includes()
 | 
						||
		// part of the Harmony (ECMAScript 7) proposal.
 | 
						||
		// http://www.2ality.com/2016/01/ecmascript-2016.html
 | 
						||
		includes : function Array_includes(search_target, position) {
 | 
						||
			if (search_target === search_target)
 | 
						||
				return this.indexOf(search_target, position) !== NOT_FOUND;
 | 
						||
			// assert: search_target is NaN
 | 
						||
			var length = this.length;
 | 
						||
			// position = normalize_position(position, length);
 | 
						||
			if (position < 0 && (position += length) < 0)
 | 
						||
				position = 0;
 | 
						||
			else if ((position |= 0) > length)
 | 
						||
				position = length;
 | 
						||
			for (; position < length; position++)
 | 
						||
				if (this[position] !== this[position])
 | 
						||
					return true;
 | 
						||
			return false;
 | 
						||
		},
 | 
						||
		// Array.prototype.reduce()
 | 
						||
		reduce : function reduce(callbackfn/* , initialValue */) {
 | 
						||
			var index = 0, length = this.length, value;
 | 
						||
			if (arguments.length > 1)
 | 
						||
				value = arguments[1];
 | 
						||
			else
 | 
						||
				// initialValue
 | 
						||
				for (; index < length; index++)
 | 
						||
					if (index in this) {
 | 
						||
						value = this[index++];
 | 
						||
						break;
 | 
						||
					}
 | 
						||
			for (; index < length; index++)
 | 
						||
				if (index in this)
 | 
						||
					value = callbackfn(value, this[index], index, this);
 | 
						||
			return value;
 | 
						||
		},
 | 
						||
		// Array.prototype.reduceRight()
 | 
						||
		reduceRight : function reduceRight(callbackfn
 | 
						||
		// , initialValue
 | 
						||
		) {
 | 
						||
			var index = this.length, value;
 | 
						||
			if (arguments.length > 1)
 | 
						||
				value = arguments[1];
 | 
						||
			else
 | 
						||
				// initialValue
 | 
						||
				while (index-- > 0)
 | 
						||
					if (index in this) {
 | 
						||
						value = this[index];
 | 
						||
						break;
 | 
						||
					}
 | 
						||
			while (index-- > 0)
 | 
						||
				if (index in this)
 | 
						||
					value = callbackfn(value, this[index], index, this);
 | 
						||
			return value;
 | 
						||
		},
 | 
						||
 | 
						||
		// Array.prototype.entries()
 | 
						||
		entries : function entries() {
 | 
						||
			// new Array_Iterator(array, use value)
 | 
						||
			return new library_namespace.Array_Iterator(this);
 | 
						||
		},
 | 
						||
		// Array.prototype.values()
 | 
						||
		values : function values() {
 | 
						||
			// new Array_Iterator(array, use value)
 | 
						||
			return new library_namespace.Array_Iterator(this, true);
 | 
						||
		},
 | 
						||
		// Array.prototype.keys()
 | 
						||
		keys : function Array_keys() {
 | 
						||
			var keys = [];
 | 
						||
			for ( var key in this)
 | 
						||
				if (/^\d+$/.test(key))
 | 
						||
					keys.push(key | 0);
 | 
						||
			library_namespace.debug('keys: ' + keys, 5,
 | 
						||
			//
 | 
						||
			'Array.prototype.keys');
 | 
						||
			return new library_namespace.Array_Iterator(keys, true);
 | 
						||
		},
 | 
						||
 | 
						||
		// Array.prototype.findIndex()
 | 
						||
		findIndex : function findIndex(predicate, thisArg) {
 | 
						||
			for (var index = 0, length = this.length; index < length; index++)
 | 
						||
				if (index in this)
 | 
						||
					if (thisArg ? predicate.call(thisArg, this[index], index,
 | 
						||
							this)
 | 
						||
					// 不採用 .call() 以加速執行。
 | 
						||
					: predicate(this[index], index, this))
 | 
						||
						return index;
 | 
						||
			return NOT_FOUND;
 | 
						||
		},
 | 
						||
		// Array.prototype.find()
 | 
						||
		find : function find(predicate, thisArg) {
 | 
						||
			var index = this.findIndex(predicate, thisArg);
 | 
						||
			if (index !== NOT_FOUND)
 | 
						||
				return this[index];
 | 
						||
			// return undefined;
 | 
						||
		},
 | 
						||
		// https://github.com/tc39/proposal-array-find-from-last
 | 
						||
		// Array.prototype.findLastIndex()
 | 
						||
		findLastIndex : function findLastIndex(predicate, thisArg) {
 | 
						||
			for (var index = this.length; index > 0;)
 | 
						||
				if (--index in this)
 | 
						||
					if (thisArg ? predicate.call(thisArg, this[index], index,
 | 
						||
							this)
 | 
						||
					// 不採用 .call() 以加速執行。
 | 
						||
					: predicate(this[index], index, this))
 | 
						||
						return index;
 | 
						||
			return NOT_FOUND;
 | 
						||
		},
 | 
						||
		// Array.prototype.findLast()
 | 
						||
		findLast : function findLast(predicate, thisArg) {
 | 
						||
			var index = this.findLastIndex(predicate, thisArg);
 | 
						||
			if (index !== NOT_FOUND)
 | 
						||
				return this[index];
 | 
						||
			// return undefined;
 | 
						||
		},
 | 
						||
 | 
						||
		// Array.prototype.some()
 | 
						||
		some : function some(callback, thisArg) {
 | 
						||
			for (var index = 0, length = this.length; index < length; index++)
 | 
						||
				if (index in this)
 | 
						||
					if (thisArg ? callback.call(thisArg, this[index], index,
 | 
						||
							this)
 | 
						||
					// 不採用 .call() 以加速執行。
 | 
						||
					: callback(this[index], index, this))
 | 
						||
						return true;
 | 
						||
			return false;
 | 
						||
		},
 | 
						||
		// Array.prototype.every()
 | 
						||
		every : function every(callback, thisArg) {
 | 
						||
			for (var index = 0, length = this.length; index < length; index++)
 | 
						||
				if (index in this)
 | 
						||
					if (!(thisArg ? callback.call(thisArg, this[index], index,
 | 
						||
							this)
 | 
						||
					// 不採用 .call() 以加速執行。
 | 
						||
					: callback(this[index], index, this)))
 | 
						||
						return false;
 | 
						||
			return true;
 | 
						||
		},
 | 
						||
 | 
						||
		// Array.prototype.map()
 | 
						||
		map : function map(callback, thisArg) {
 | 
						||
			var result = [];
 | 
						||
			this.forEach(function() {
 | 
						||
				result.push(callback.apply(this, arguments));
 | 
						||
			}, thisArg);
 | 
						||
			return result;
 | 
						||
		},
 | 
						||
		// Array.prototype.filter()
 | 
						||
		filter : function map(callback, thisArg) {
 | 
						||
			var result = [];
 | 
						||
			this.forEach(function(value) {
 | 
						||
				if (callback.apply(this, arguments))
 | 
						||
					result.push(value);
 | 
						||
			}, thisArg);
 | 
						||
			return result;
 | 
						||
		},
 | 
						||
 | 
						||
		// Array.prototype.flat
 | 
						||
		flat : flat,
 | 
						||
		// Array.prototype.flatMap
 | 
						||
		flatMap : flatMap,
 | 
						||
 | 
						||
		// Array.prototype.indexOf ( searchElement [ , fromIndex ] )
 | 
						||
		indexOf : function indexOf(searchElement, fromIndex) {
 | 
						||
			fromIndex |= 0;
 | 
						||
			var length = this.length;
 | 
						||
			if (fromIndex < 0 && (fromIndex += length) < 0)
 | 
						||
				fromIndex = 0;
 | 
						||
 | 
						||
			for (; fromIndex < length; fromIndex++)
 | 
						||
				if (searchElement === this[fromIndex])
 | 
						||
					return fromIndex;
 | 
						||
			return NOT_FOUND;
 | 
						||
		},
 | 
						||
		// Array.prototype.lastIndexOf ( searchElement [ , fromIndex ] )
 | 
						||
		lastIndexOf : function lastIndexOf(searchElement, fromIndex) {
 | 
						||
			var length = this.length;
 | 
						||
			if (isNaN(fromIndex))
 | 
						||
				fromIndex = length - 1;
 | 
						||
			else if ((fromIndex |= 0) < 0)
 | 
						||
				fromIndex += length;
 | 
						||
			else
 | 
						||
				fromIndex = Math.min(fromIndex, length - 1);
 | 
						||
 | 
						||
			for (; 0 <= fromIndex; fromIndex--)
 | 
						||
				if (searchElement === this[fromIndex])
 | 
						||
					return fromIndex;
 | 
						||
			return NOT_FOUND;
 | 
						||
		},
 | 
						||
		// Array.prototype.fill()
 | 
						||
		fill : function fill(value, start, end) {
 | 
						||
			// Array.prototype.fill() 只會作用於 0~原先的 length 範圍內!
 | 
						||
			if (isNaN(end) || this.length < (end |= 0))
 | 
						||
				end = this.length;
 | 
						||
			else if (end < 0)
 | 
						||
				end += this.length;
 | 
						||
			for (var index = start || 0; index < end;)
 | 
						||
				this[index++] = value;
 | 
						||
			return this;
 | 
						||
		},
 | 
						||
		/**
 | 
						||
		 * 對於舊版沒有 Array.push() 等函數時之判別及處置。<br />
 | 
						||
		 * 不能用t=this.valueOf(); ... this.push(t);
 | 
						||
		 */
 | 
						||
		// Array.prototype.push()
 | 
						||
		push : function push() {
 | 
						||
			var i = 0, l = arguments.length, w = this.length;
 | 
						||
			// 在 FF3 僅用 this[this.length]=o; 效率略好於 Array.push(),
 | 
						||
			// 但 Chrome 6 相反。
 | 
						||
			for (; i < l; i++)
 | 
						||
				this[w++] = arguments[i];
 | 
						||
			return w;
 | 
						||
		},
 | 
						||
		// Array.prototype.pop()
 | 
						||
		pop : function pop() {
 | 
						||
			// 不能用 return this[--this.length];
 | 
						||
			var l = this.length, v;
 | 
						||
			if (l) {
 | 
						||
				v = this[l];
 | 
						||
				this.length--;
 | 
						||
			}
 | 
						||
			return v;
 | 
						||
		},
 | 
						||
		// Array.prototype.shift()
 | 
						||
		shift : function shift() {
 | 
						||
			var v = this[0];
 | 
						||
			// ECMAScript 不允許設定 this=
 | 
						||
			this.value = this.slice(1);
 | 
						||
			return v;
 | 
						||
		},
 | 
						||
		// Array.prototype.unshift()
 | 
						||
		unshift : function unshift() {
 | 
						||
			// ECMAScript 不允許設定 this =
 | 
						||
			this.value = Array_slice.call(arguments).concat(this);
 | 
						||
			return this.length;
 | 
						||
		}
 | 
						||
	}, null);
 | 
						||
 | 
						||
	// ----------------------------------------------------------------------------------------------------------------------------------------------------------//
 | 
						||
 | 
						||
	// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays
 | 
						||
	// It's hard to fully simulate typed arrays.
 | 
						||
	// 只能把原先即存在的功能加強到可用。
 | 
						||
	var typed_arrays = [];
 | 
						||
	if (typeof Uint32Array === 'function')
 | 
						||
		(function() {
 | 
						||
			var Array_prototype = Array.prototype,
 | 
						||
			// TODO: .slice() and others
 | 
						||
			typed_array_methods = 'copyWithin,entries,every,fill,filter,find,findIndex,forEach,includes,indexOf,join,keys,lastIndexOf,map,reduce,reduceRight,reverse,set,slice,some,subarray,values,at'
 | 
						||
					.split(',');
 | 
						||
			'Int8Array,Uint8Array,Uint8ClampedArray,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array'
 | 
						||
			//
 | 
						||
			.split(',').forEach(function(typed_array) {
 | 
						||
				try {
 | 
						||
					typed_array = eval(typed_array);
 | 
						||
				} catch (e) {
 | 
						||
					library_namespace.warn('Not exist: ' + typed_array);
 | 
						||
					return;
 | 
						||
				}
 | 
						||
				var prototype = typed_array.prototype;
 | 
						||
				typed_array_methods.forEach(function(method) {
 | 
						||
					if (!(method in typed_array.prototype))
 | 
						||
						Object.defineProperty(prototype, method, {
 | 
						||
							value : Array_prototype[method]
 | 
						||
						});
 | 
						||
				});
 | 
						||
				typed_arrays.push(typed_array);
 | 
						||
			});
 | 
						||
		})();
 | 
						||
 | 
						||
	if (typed_arrays.length === 0)
 | 
						||
		typed_arrays = null;
 | 
						||
 | 
						||
	_.typed_arrays = typed_arrays;
 | 
						||
 | 
						||
	// ----------------------------------------------------------------------------------------------------------------------------------------------------------//
 | 
						||
	// Number.*
 | 
						||
 | 
						||
	// 1. === 1.0
 | 
						||
 | 
						||
	function ToNumber(value) {
 | 
						||
		// Number(value)
 | 
						||
		return +value;
 | 
						||
	}
 | 
						||
 | 
						||
	// cf. value | 0
 | 
						||
	/** <code>((Number.MAX_SAFE_INTEGER / 4) | 0) < 0, 0 < ((Number.MAX_SAFE_INTEGER / 5) | 0)</code> */
 | 
						||
	function ToInteger(value) {
 | 
						||
		return Math.trunc(value) || 0;
 | 
						||
 | 
						||
		// return value >> 0;
 | 
						||
 | 
						||
		// https://tc39.es/ecma262/#sec-tointeger
 | 
						||
		value = Number(value);
 | 
						||
		return value > 0 ? Math.floor(value) : value < 0 ? -Math.floor(value)
 | 
						||
				: 0;
 | 
						||
	}
 | 
						||
 | 
						||
	// Number.isNaN()
 | 
						||
	// http://wiki.ecmascript.org/doku.php?id=harmony:number.isnan
 | 
						||
	function is_NaN(value) {
 | 
						||
		return typeof value === 'number' &&
 | 
						||
		// isNaN(value)
 | 
						||
		value !== value;
 | 
						||
	}
 | 
						||
 | 
						||
	// calculatable
 | 
						||
	/**
 | 
						||
	 * 取得最小最低可做除法計算數值。回傳值為邊界,已不可再做操作。但可做乘法操作。
 | 
						||
	 * 
 | 
						||
	 * @param [base_integral]
 | 
						||
	 * @returns {Number}
 | 
						||
	 */
 | 
						||
	function dividable_minimum(base_integral, return_last) {
 | 
						||
		if (!base_integral || isNaN(base_integral))
 | 
						||
			base_integral = 1;
 | 
						||
		var last = 1, min;
 | 
						||
		// 預防 min 變成0,因此設置前一步 last。
 | 
						||
		while (base_integral !== base_integral + (min = last / 2))
 | 
						||
			last = min;
 | 
						||
		return !return_last && min || last;
 | 
						||
	}
 | 
						||
 | 
						||
	// calculatable
 | 
						||
	/**
 | 
						||
	 * 取得最大可做加法計算數值。回傳值為邊界,已不可再做加法操作。但可做減法操作。
 | 
						||
	 * 
 | 
						||
	 * @param [base_integral]
 | 
						||
	 * @returns {Integer}
 | 
						||
	 */
 | 
						||
	function addable_maximum(base_integral) {
 | 
						||
		if (!base_integral || isNaN(base_integral))
 | 
						||
			base_integral = 1;
 | 
						||
		var max = 1, test;
 | 
						||
		while ((max *= 2) < (test = max + base_integral)
 | 
						||
				&& max === test - base_integral)
 | 
						||
			;
 | 
						||
		return max;
 | 
						||
	}
 | 
						||
	var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || addable_maximum() - 1;
 | 
						||
	// Number.isSafeInteger()
 | 
						||
	function isSafeInteger(number) {
 | 
						||
		return typeof number === 'number'
 | 
						||
 | 
						||
		// && number <= MAX_SAFE_INTEGER && -MAX_SAFE_INTEGER <= number
 | 
						||
		&& Math.abs(number) <= MAX_SAFE_INTEGER
 | 
						||
 | 
						||
		// 在範圍外的,常常 % 1 亦為 0。
 | 
						||
		&& 0 === number % 1
 | 
						||
		// && Math.floor(number) === number
 | 
						||
		;
 | 
						||
	}
 | 
						||
 | 
						||
	set_method(Number, {
 | 
						||
		/**
 | 
						||
		 * The value of Number.MAX_SAFE_INTEGER is the largest integer value
 | 
						||
		 * that can be represented as a Number value without losing precision,
 | 
						||
		 * which is 9007199254740991 (2^53-1).
 | 
						||
		 */
 | 
						||
		MAX_SAFE_INTEGER : MAX_SAFE_INTEGER,
 | 
						||
		/**
 | 
						||
		 * The value of Number.MIN_SAFE_INTEGER is -9007199254740991
 | 
						||
		 * (-(2^53-1)).
 | 
						||
		 */
 | 
						||
		MIN_SAFE_INTEGER : -MAX_SAFE_INTEGER,
 | 
						||
		/**
 | 
						||
		 * The value of Number.EPSILON is the difference between 1 and the
 | 
						||
		 * smallest value greater than 1 that is representable as a Number
 | 
						||
		 * value, which is approximately 2.2204460492503130808472633361816 x
 | 
						||
		 * 10-16.
 | 
						||
		 */
 | 
						||
		EPSILON : dividable_minimum(0, 1)
 | 
						||
	}, 'number');
 | 
						||
 | 
						||
	set_method(Number, {
 | 
						||
		isSafeInteger : isSafeInteger,
 | 
						||
		/**
 | 
						||
		 * Number.toInteger() is obsolete.
 | 
						||
		 * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toInteger
 | 
						||
		 * Number.toInteger was part of the draft ECMAScript 6 specification,
 | 
						||
		 * but has been removed on August 23, 2013 in Draft Rev 17.
 | 
						||
		 */
 | 
						||
		// toInteger: ToInteger,
 | 
						||
		/**
 | 
						||
		 * Number.isInteger()<br />
 | 
						||
		 * cf. .is_digits()
 | 
						||
		 */
 | 
						||
		isInteger : function isInteger(number) {
 | 
						||
			// https://tc39.es/ecma262/#sec-number.isinteger
 | 
						||
			return Number.isFinite(number)
 | 
						||
			// Math.floor(Infinity) === Infinity
 | 
						||
			&& ToInteger(number) === number;
 | 
						||
		},
 | 
						||
		parseFloat : parseFloat,
 | 
						||
		parseInt : parseInt,
 | 
						||
		/**
 | 
						||
		 * Number.isNaN()
 | 
						||
		 */
 | 
						||
		isNaN : is_NaN,
 | 
						||
		isFinite : function isFinite(value) {
 | 
						||
			return typeof value === 'number' && !isNaN(value)
 | 
						||
					&& value !== Infinity && value !== -Infinity;
 | 
						||
		}
 | 
						||
	});
 | 
						||
 | 
						||
	// ----------------------------------------------------------------------------------------------------------------------------------------------------------//
 | 
						||
	// Math.*
 | 
						||
	// https://rwaldron.github.io/proposal-math-extensions/
 | 
						||
 | 
						||
	// 32 bits
 | 
						||
	var BITS = 1;
 | 
						||
	while (1 !== 1 << BITS)
 | 
						||
		BITS <<= 1;
 | 
						||
	// assert: BITS === 32
 | 
						||
 | 
						||
	// Math.clz32()
 | 
						||
	// TODO: 增進效率。
 | 
						||
	function clz32(value) {
 | 
						||
		// ToUint32() ??
 | 
						||
		value >>>= 0;
 | 
						||
		// console.log(value + ' = ' + value.toString(2) + ' (2)');
 | 
						||
		// binary search: 計算數字本身具有的 bits.
 | 
						||
		for (var min = 0, MAX = BITS, zeros;;) {
 | 
						||
			zeros = (min + MAX) >> 1;
 | 
						||
			// console.log(min + ' - ' + zeros + ' - ' + MAX);
 | 
						||
			if (0 === value >> zeros)
 | 
						||
				if (MAX === zeros)
 | 
						||
					break;
 | 
						||
				else
 | 
						||
					MAX = zeros;
 | 
						||
			else if (min === zeros)
 | 
						||
				break;
 | 
						||
			else
 | 
						||
				min = zeros;
 | 
						||
		}
 | 
						||
		return BITS - MAX;
 | 
						||
	}
 | 
						||
 | 
						||
	// 分界
 | 
						||
	var Math_hypot_up_boundary = Math.sqrt(Number.MAX_SAFE_INTEGER) / 2 | 0,
 | 
						||
	//
 | 
						||
	Math_hypot_down_boundary = Math.sqrt(Number.MIN_VALUE);
 | 
						||
 | 
						||
	// Math.hypot(value_1 , value_2, value_3 = 0)
 | 
						||
	// TODO: 增進效率。
 | 
						||
	// http://en.wikipedia.org/wiki/Hypot
 | 
						||
	function hypot(value_1, value_2, value_3) {
 | 
						||
		var r, MAX = Math.max(value_1 = Math.abs(value_1),
 | 
						||
		// 轉正
 | 
						||
		value_2 = Math.abs(value_2),
 | 
						||
		//
 | 
						||
		value_3 = value_3 === undefined ? 0 : Math.abs(value_3));
 | 
						||
		if (!MAX || !Number.isFinite(MAX))
 | 
						||
			return MAX;
 | 
						||
 | 
						||
		if (MAX < Math_hypot_up_boundary
 | 
						||
				// avoid underflow
 | 
						||
				&& Math_hypot_down_boundary < Math.min(value_1, value_2,
 | 
						||
						value_3)
 | 
						||
				// avoid overflow, minimise rounding errors (預防本該為整數的出現小數).
 | 
						||
				&& Number.isFinite(r = value_1 * value_1 + value_2 * value_2
 | 
						||
						+ value_3 * value_3))
 | 
						||
			return Math.sqrt(r);
 | 
						||
 | 
						||
		return MAX
 | 
						||
				* Math.sqrt((value_1 ? (value_1 /= MAX) * value_1 : 0)
 | 
						||
						+ (value_2 ? (value_2 /= MAX) * value_2 : 0)
 | 
						||
						+ (value_3 ? (value_3 /= MAX) * value_3 : 0));
 | 
						||
	}
 | 
						||
 | 
						||
	set_method(Math, {
 | 
						||
		LN2 : Math.log(2),
 | 
						||
		LN10 : Math.log(10)
 | 
						||
	}, 'number');
 | 
						||
 | 
						||
	var expm1_error = 1e-5;
 | 
						||
 | 
						||
	set_method(Math, {
 | 
						||
		expm1 : function expm1(value) {
 | 
						||
			// If x is −0, the result is −0.
 | 
						||
			if (!value)
 | 
						||
				return value;
 | 
						||
			// 在這範圍外不會有誤差。
 | 
						||
			// x = 1e-5; Math.expm1(x) === Math.exp(x) - 1
 | 
						||
			if (Math.abs(value) > expm1_error)
 | 
						||
				return Math.exp(value) - 1;
 | 
						||
 | 
						||
			// 提高精準度。
 | 
						||
			// x = 1e-6; Math.expm1(x) > Math.exp(x) - 1
 | 
						||
 | 
						||
			// http://www.wolframalpha.com/input/?i=e^x-1
 | 
						||
			// Taylor series: x+x^2/2+x^3/6+x^4/24+x^5/120+O(x^6)
 | 
						||
			var delta = value,
 | 
						||
 | 
						||
			index = 1, result = value;
 | 
						||
			// 頂多跑個 2~3 次就該結束了。
 | 
						||
			while (result + (delta *= value / ++index) !== result)
 | 
						||
				result += delta;
 | 
						||
			return result;
 | 
						||
		},
 | 
						||
 | 
						||
		// hyperbolic functions
 | 
						||
		// https://en.wikipedia.org/wiki/Hyperbolic_function
 | 
						||
		sinh : function sinh(value) {
 | 
						||
			// If x is −0, the result is −0.
 | 
						||
			return value ? Math.abs(value) > expm1_error
 | 
						||
			//
 | 
						||
			? (Math.exp(value) - Math.exp(-value)) / 2
 | 
						||
			//
 | 
						||
			: (Math.expm1(value) - Math.expm1(-value)) / 2
 | 
						||
			//
 | 
						||
			: value;
 | 
						||
		},
 | 
						||
		cosh : function cosh(value) {
 | 
						||
			// If x is −0, the result is −0.
 | 
						||
			return value ? (value = Math.abs(value)) < 19
 | 
						||
			//
 | 
						||
			? (Math.exp(value) + Math.exp(-value)) / 2
 | 
						||
			// value = 19;
 | 
						||
			// Math.exp(value) + Math.exp(-value) === Math.exp(value);
 | 
						||
			: Math.exp(value) / 2 : value;
 | 
						||
		},
 | 
						||
		tanh : function tanh(value) {
 | 
						||
			if (!value)
 | 
						||
				// If x is −0, the result is −0.
 | 
						||
				return value;
 | 
						||
			// value = 19;
 | 
						||
			// Math.exp(value) + Math.exp(-value) === Math.exp(value);
 | 
						||
			if (Math.abs(value) > 19)
 | 
						||
				// If x is +∞, the result is +1.
 | 
						||
				return value > 0 ? 1 : -1;
 | 
						||
			var e = Math.exp(value), me = Math.exp(-value);
 | 
						||
			return (Math.abs(value) < expm1_error
 | 
						||
			// 提高精準度。
 | 
						||
			? Math.expm1(value) - Math.expm1(-value) : e - me) / (e + me);
 | 
						||
		},
 | 
						||
 | 
						||
		// https://en.wikipedia.org/wiki/Hyperbolic_function#Inverse_functions_as_logarithms
 | 
						||
		// inverse hyperbolic function
 | 
						||
		// https://en.wikipedia.org/wiki/Inverse_hyperbolic_function
 | 
						||
		asinh : function asinh(value) {
 | 
						||
			// If x is −0, the result is −0.
 | 
						||
			if (!value)
 | 
						||
				return value;
 | 
						||
			// http://www.wolframalpha.com/input/?i=asinh+x
 | 
						||
			var v = Math.abs(value);
 | 
						||
			v = Math.log(v + Math.sqrt(v * v + 1));
 | 
						||
			return value < 0 ? -v : v;
 | 
						||
		},
 | 
						||
		acosh : function acosh(value) {
 | 
						||
			// http://www.wolframalpha.com/input/?i=acosh+x
 | 
						||
			return Math.log(value + Math.sqrt(value * value - 1));
 | 
						||
		},
 | 
						||
		atanh : function atanh(value) {
 | 
						||
			// If x is −0, the result is −0.
 | 
						||
			return value ? Math.log((1 + value) /
 | 
						||
			// 為 JsDoc 換行。
 | 
						||
			(1 - value)) / 2 : value;
 | 
						||
		},
 | 
						||
 | 
						||
		// Math.log2()
 | 
						||
		log2 : function log2(value) {
 | 
						||
			// return Math.log(value) / Math.LN2;
 | 
						||
			return Math.log(value) * Math.LOG2E;
 | 
						||
		},
 | 
						||
		// Math.log10()
 | 
						||
		log10 : function log10(value) {
 | 
						||
			// 採用 /Math.LN10 會造成 Math.log10(1e-12) !== -12
 | 
						||
			// return Math.log(value) / Math.LN10;
 | 
						||
			return Math.log(value) * Math.LOG10E;
 | 
						||
		},
 | 
						||
		log1p : function log1p(value) {
 | 
						||
			// If x is −0, the result is −0.
 | 
						||
			return value ? Math.log(1 + value) : value;
 | 
						||
		},
 | 
						||
 | 
						||
		hypot : hypot,
 | 
						||
		cbrt : function cbrt(value) {
 | 
						||
			// If x is −0, the result is −0.
 | 
						||
			return value ? Math.pow(value, 1 / 3) : value;
 | 
						||
		},
 | 
						||
 | 
						||
		clz32 : clz32,
 | 
						||
 | 
						||
		// Math.trunc()
 | 
						||
		trunc : function trunc(value) {
 | 
						||
			// value >= 0 ? Math.floor(value) : Math.ceil(value)
 | 
						||
			return value > 0 ? Math.floor(value)
 | 
						||
			//
 | 
						||
			: value < 0 ? Math.ceil(value)
 | 
						||
			//
 | 
						||
			: value === 0 ? value : isNaN(value) ? NaN
 | 
						||
			// null, true, false, ..
 | 
						||
			: value | 0;
 | 
						||
		},
 | 
						||
		sign : function sign(value) {
 | 
						||
			// If x is −0, the result is −0.
 | 
						||
			return 0 < value ? 1 : value < 0 ? -1 : value;
 | 
						||
		}
 | 
						||
	});
 | 
						||
 | 
						||
	// ----------------------------------------------------------------------------------------------------------------------------------------------------------//
 | 
						||
	// RegExp.*
 | 
						||
 | 
						||
	// 注意: 因為要設定額外功能,RegExp.prototype.flags 放在 data.native
 | 
						||
 | 
						||
	// ----------------------------------------------------------------------------------------------------------------------------------------------------------//
 | 
						||
	// String.*
 | 
						||
 | 
						||
	/**
 | 
						||
	 * bug fix (workaround) for String.prototype.split():
 | 
						||
	 * 
 | 
						||
	 * @see https://github.com/es-shims/es5-shim/blob/master/es5-shim.js
 | 
						||
	 *      http://blog.stevenlevithan.com/archives/cross-browser-split
 | 
						||
	 *      http://blog.stevenlevithan.com/archives/fixing-javascript-regexp
 | 
						||
	 * 
 | 
						||
	 * @since 2010/1/1 19:03:40 2015/1/28 17:43:0 refactoring 重構
 | 
						||
	 * 
 | 
						||
	 * @example <code>
 | 
						||
 | 
						||
	 // More examples: see /_test suite/test.js
 | 
						||
 | 
						||
	 * </code>
 | 
						||
	 */
 | 
						||
	if (
 | 
						||
	// [ "", "" ] @ IE8, [ "", "_", "" ] @ firefox 38+
 | 
						||
	'_'.split(/(_)/)[1] !== '_'
 | 
						||
	// 理論上 '.'.split(/\./).length 應該是 2,但 IE 5–8 中卻為 0!
 | 
						||
	// 用 .split('.') 倒是 OK.
 | 
						||
	// || '.'.split(/\./).length === 0
 | 
						||
	)
 | 
						||
		(function() {
 | 
						||
			var native_String_split =
 | 
						||
			// 增加可以管控與回復的手段,預防有時需要回到原有行為。
 | 
						||
			library_namespace.native_String_split = String.prototype.split;
 | 
						||
 | 
						||
			// The length property of the split method is 2.
 | 
						||
			(String.prototype.split = function(separator, limit) {
 | 
						||
				if (!library_namespace.is_RegExp(separator))
 | 
						||
					return native_String_split.apply(this, arguments);
 | 
						||
 | 
						||
				// 不改變 separator 本身,加上 .global flag。
 | 
						||
				separator = new RegExp(separator.source, (separator.global ? ''
 | 
						||
						: 'g')
 | 
						||
						// should use: RegExp_flags(separator)
 | 
						||
						+ ('' + separator).match(/[^\/]*$/)[0]);
 | 
						||
				var matched, result = [], last_index = 0;
 | 
						||
				if (0 <= limit)
 | 
						||
					// ToLength(), ToUint32()
 | 
						||
					limit >>>= 0;
 | 
						||
				else
 | 
						||
					// Math.pow(2, 32) - 1
 | 
						||
					limit = -1 >>> 0;
 | 
						||
 | 
						||
				while (result.length < limit && last_index < this.length) {
 | 
						||
					matched = separator.exec(this);
 | 
						||
					if (!matched) {
 | 
						||
						if (false)
 | 
						||
							library_namespace.debug('push (last) ['
 | 
						||
									+ this.slice(last_index) + ']');
 | 
						||
						result.push(this.slice(last_index));
 | 
						||
						break;
 | 
						||
					}
 | 
						||
					if (false) {
 | 
						||
						library_namespace.warn('index: ' + last_index + '-'
 | 
						||
								+ matched.index + '-' + separator.lastIndex
 | 
						||
								+ ' (' + matched[0].length + ')'
 | 
						||
								+ ' [<span style="color:#22a;">'
 | 
						||
								+ this.slice(0, last_index)
 | 
						||
								+ '<span style="color:#182;">'
 | 
						||
								+ this.slice(last_index, separator.lastIndex)
 | 
						||
								+ '</span>' + this.slice(separator.lastIndex)
 | 
						||
								+ '</span>]');
 | 
						||
						library_namespace.log('matched 1 ['
 | 
						||
								+ matched.join('<b style="color:#b94;">;</b>')
 | 
						||
								+ ']');
 | 
						||
					}
 | 
						||
					if (false && library_namespace.show_value) {
 | 
						||
						library_namespace.show_value(matched, 'matched');
 | 
						||
						library_namespace.show_value(result.slice(), 'result');
 | 
						||
					}
 | 
						||
					// 當有東西時才登記。
 | 
						||
					if (last_index < matched.index || matched[0]) {
 | 
						||
						result.push(this.slice(last_index, matched.index));
 | 
						||
						if (false)
 | 
						||
							library_namespace.debug('push ['
 | 
						||
									+ this.slice(last_index, matched.index)
 | 
						||
									+ ']');
 | 
						||
						if (matched.length > 1 && matched.index < this.length)
 | 
						||
							Array.prototype.push
 | 
						||
									.apply(result, matched.slice(1));
 | 
						||
						// IE 中,匹配到 null string 時,lastIndex 會自動 +1。
 | 
						||
						// /(,?)/g.exec('1') 之 lastIndex = 1,
 | 
						||
						last_index = matched.index + matched[0].length;
 | 
						||
						if (false)
 | 
						||
							library_namespace.debug('last_index: ['
 | 
						||
									+ last_index + ']');
 | 
						||
						if (last_index === this.length) {
 | 
						||
							if (matched[0]) {
 | 
						||
								if (false)
 | 
						||
									library_namespace.debug('push null (last)');
 | 
						||
								result.push('');
 | 
						||
							}
 | 
						||
							break;
 | 
						||
						}
 | 
						||
					} else if (false)
 | 
						||
						library_namespace.debug('Skip this.');
 | 
						||
 | 
						||
					if (separator.lastIndex === matched.index)
 | 
						||
						// 避免無窮迴圈。
 | 
						||
						separator.lastIndex++;
 | 
						||
 | 
						||
					if (false && library_namespace.show_value) {
 | 
						||
						library_namespace
 | 
						||
								.info('lastIndex: <span style="color:#905;">'
 | 
						||
										+ separator.lastIndex
 | 
						||
										+ '</span>, next from: [<span style="color:#62a;">'
 | 
						||
										+ this.slice(0, separator.lastIndex)
 | 
						||
										+ '</span>|<span style="color:#a42;">'
 | 
						||
										+ this.slice(separator.lastIndex)
 | 
						||
										+ '</span>]');
 | 
						||
						library_namespace.log('matched 2 ['
 | 
						||
								+ matched.join('<b style="color:#b94;">;</b>')
 | 
						||
								+ ']');
 | 
						||
						library_namespace.log('result ['
 | 
						||
								+ result.join('<b style="color:#b94;">;</b>')
 | 
						||
								+ ']');
 | 
						||
					}
 | 
						||
				}
 | 
						||
 | 
						||
				return result;
 | 
						||
			})[library_namespace.env.not_native_keyword] = true;
 | 
						||
		})();
 | 
						||
 | 
						||
	// String.prototype.repeat()
 | 
						||
	// in VB: String(count, this)
 | 
						||
	// “x” operator @ perl
 | 
						||
	// http://wiki.ecmascript.org/doku.php?id=harmony:string.prototype.repeat
 | 
						||
	function repeat(count) {
 | 
						||
		var result = [],
 | 
						||
		/**
 | 
						||
		 * The repeat function is intentionally generic
 | 
						||
		 * https://mail.mozilla.org/pipermail/es-discuss/2011-January/012538.html
 | 
						||
		 * Trivia: ""+obj is not the same thing as ToString(obj). They differ if
 | 
						||
		 * obj has a .valueOf method.
 | 
						||
		 */
 | 
						||
		piece = '' + this;
 | 
						||
 | 
						||
		if (!piece || isNaN(count) || (count = Math.floor(count)) < 1)
 | 
						||
			return '';
 | 
						||
 | 
						||
		// https://mail.mozilla.org/pipermail/es-discuss/2011-January/012525.html
 | 
						||
		// If ToUint32(`amount) is not equal to `amount, throw a RangeError.
 | 
						||
		// isFinite()
 | 
						||
		if (count >>> 0 !== count)
 | 
						||
			throw new Error("invalid repeat argument");
 | 
						||
 | 
						||
		// http://stackoverflow.com/questions/202605/repeat-string-javascript
 | 
						||
		// 此法較 (new Array( count + 1 ).join(this)) 稍快。
 | 
						||
		while (true) {
 | 
						||
			library_namespace.debug('left: ' + count, 3,
 | 
						||
					'String.prototype.repeat');
 | 
						||
			if (count & 1)
 | 
						||
				result.push(piece);
 | 
						||
 | 
						||
			if (count >>>= 1)
 | 
						||
				piece += piece;
 | 
						||
			else
 | 
						||
				break;
 | 
						||
		}
 | 
						||
 | 
						||
		return result.join('');
 | 
						||
	}
 | 
						||
 | 
						||
	/**
 | 
						||
	 * <code>
 | 
						||
 | 
						||
	2010/6/1
 | 
						||
	test time:
 | 
						||
 | 
						||
	'   fhdgjk   lh gjkl ;sfdf d  hf gj '
 | 
						||
 | 
						||
	.replace(/^\s+|\s+$/g, '')
 | 
						||
	~<
 | 
						||
	.replace(/\s+$|^\s+/g, '')
 | 
						||
	<
 | 
						||
	.replace(/^\s+/, '').replace(/\s+$/, '')
 | 
						||
	~<
 | 
						||
	.replace(/\s+$/, '').replace(/^\s+/, '')
 | 
						||
 | 
						||
	</code>
 | 
						||
	 */
 | 
						||
 | 
						||
	/**
 | 
						||
	 * 去除首尾空白。去除前後空白。去頭去尾。去掉 string 前後 space.
 | 
						||
	 * 
 | 
						||
	 * @param {String}
 | 
						||
	 *            string input string
 | 
						||
	 * @return {String} 轉換過的 string
 | 
						||
	 * @since 2006/10/27 16:36
 | 
						||
	 * @see from lib/perl/BaseF.pm (or program/database/BaseF.pm)<br />
 | 
						||
	 * function strip() @ Prototype JavaScript framework
 | 
						||
	 * 
 | 
						||
	 * String.prototype.trim()
 | 
						||
	 * http://stackoverflow.com/questions/1418050/string-strip-for-javascript
 | 
						||
	 * https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/Trim
 | 
						||
	 * http://blog.stevenlevithan.com/archives/faster-trim-javascript
 | 
						||
	 * 
 | 
						||
	 * @_memberOf _module_
 | 
						||
	 */
 | 
						||
	function trim() {
 | 
						||
		// The repeat function is intentionally generic
 | 
						||
		var string = String(this)
 | 
						||
 | 
						||
		// https://blog.stevenlevithan.com/archives/faster-trim-javascript
 | 
						||
		.replace(/^\s\s*/, '');
 | 
						||
		var PATTERN_SPACE = /\s/, index = string.length;
 | 
						||
		while (PATTERN_SPACE.test(string.charAt(--index)))
 | 
						||
			;
 | 
						||
		return string.slice(0, index + 1);
 | 
						||
 | 
						||
		// .replace(/\s+$|^\s+/g, '');
 | 
						||
		// .replace(/^\s+|\s+$/g, '');
 | 
						||
 | 
						||
		// The definition of white space is the union of WhiteSpace and
 | 
						||
		// LineTerminator.
 | 
						||
		// .replace(/[\s\n]+$|^[\s\n]+/g, '');
 | 
						||
	}
 | 
						||
 | 
						||
	// String.prototype.trimStart(), String.prototype.trimLeft()
 | 
						||
	var trimStart = String.prototype.trimLeft || function trimStart() {
 | 
						||
		return String(this).replace(/^[\s\n]+/, '');
 | 
						||
	};
 | 
						||
 | 
						||
	// String.prototype.trimEnd(), String.prototype.trimRight()
 | 
						||
	var trimEnd = String.prototype.trimRight || function trimEnd() {
 | 
						||
		return String(this).replace(/[\s\n]+$/, '');
 | 
						||
	};
 | 
						||
 | 
						||
	// ---------------------------------------------------------------------------
 | 
						||
	// http://tc39.github.io/proposal-string-pad-start-end/
 | 
						||
	// https://github.com/tc39/proposal-string-pad-start-end
 | 
						||
	function pad_String(/* targetLength */maxLength, /* padString */
 | 
						||
	fillString) {
 | 
						||
		if (!(this.length < maxLength)) {
 | 
						||
			return '';
 | 
						||
		}
 | 
						||
		if (fillString === undefined) {
 | 
						||
			fillString = ' ';
 | 
						||
		} else if (!(fillString = String(fillString))) {
 | 
						||
			return '';
 | 
						||
		}
 | 
						||
 | 
						||
		maxLength -= this.length;
 | 
						||
 | 
						||
		if (fillString.length < maxLength) {
 | 
						||
			fillString = fillString.repeat(Math.ceil(maxLength
 | 
						||
					/ fillString.length));
 | 
						||
		}
 | 
						||
 | 
						||
		return fillString.length === maxLength ? fillString
 | 
						||
		// assert: fillString.length > maxLength
 | 
						||
		: fillString.slice(0, maxLength);
 | 
						||
	}
 | 
						||
 | 
						||
	// ---------------------------------------------------------------------------
 | 
						||
 | 
						||
	// String.prototype.startsWith()
 | 
						||
	function startsWith(searchString, position) {
 | 
						||
		if (library_namespace.is_RegExp(searchString))
 | 
						||
			throw new Error(
 | 
						||
					"Invalid type: searchString can't be a Regular Expression");
 | 
						||
		searchString = String(searchString);
 | 
						||
		if (!position || !(position |= 0) || position < 0)
 | 
						||
			return this.lastIndexOf(searchString, 0) === 0;
 | 
						||
 | 
						||
		return searchString === this.substr(position, searchString.length);
 | 
						||
	}
 | 
						||
 | 
						||
	// String.prototype.endsWith()
 | 
						||
	function endsWith(searchString, endPosition) {
 | 
						||
		if (library_namespace.is_RegExp(searchString))
 | 
						||
			throw new Error(
 | 
						||
					"Invalid type: searchString can't be a Regular Expression");
 | 
						||
		searchString = String(searchString);
 | 
						||
		var is_tail = endPosition === undefined
 | 
						||
				|| (endPosition |= 0) === this.length,
 | 
						||
		//
 | 
						||
		position = (is_tail ? this.length : endPosition) - searchString.length;
 | 
						||
		return position >= 0
 | 
						||
				&& (is_tail ? this.indexOf(searchString, position) === position
 | 
						||
						: searchString === this.substr(position,
 | 
						||
								searchString.length));
 | 
						||
	}
 | 
						||
 | 
						||
	// String.prototype.codePointAt(position)
 | 
						||
	// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/codePointAt
 | 
						||
	function codePointAt(index) {
 | 
						||
		var _1 = this.charCodeAt(index), _2;
 | 
						||
		if (index + 1 < this.length
 | 
						||
		// check high surrogate
 | 
						||
		&& _1 >= 0xD800 && _1 <= 0xDBFF
 | 
						||
		// check low surrogate
 | 
						||
		&& (_2 = this.charCodeAt(index + 1)) >= 0xDC00 && _2 <= 0xDFFF) {
 | 
						||
			return (_1 - 0xD800) * 0x400 + _2 - 0xDC00 + 0x10000;
 | 
						||
		}
 | 
						||
		return _1;
 | 
						||
	}
 | 
						||
 | 
						||
	// String.fromCodePoint(...codePoints)
 | 
						||
	// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint
 | 
						||
	function fromCodePoint() {
 | 
						||
		var result = '';
 | 
						||
		for (var index = 0, length = arguments.length; index < length; index++) {
 | 
						||
			var char_code = +arguments[index];
 | 
						||
			if (char_code < 0x10000) {
 | 
						||
				result += String.fromCharCode(char_code);
 | 
						||
			} else {
 | 
						||
				char_code -= 0x10000;
 | 
						||
				result += String.fromCharCode((char_code >> 10) + 0xD800,
 | 
						||
						(char_code % 0x400) + 0xDC00);
 | 
						||
			}
 | 
						||
		}
 | 
						||
		return result;
 | 
						||
	}
 | 
						||
 | 
						||
	// ------------------------------------------
 | 
						||
 | 
						||
	// @see CeL.data.native.RegExp_flags()
 | 
						||
	var has_RegExp_flags = /./g.flags === 'g';
 | 
						||
 | 
						||
	// Generator function
 | 
						||
	var has_Generator;
 | 
						||
	(function() {
 | 
						||
		var g, i, l;
 | 
						||
		try {
 | 
						||
			eval('g = function*() { yield 2;yield 1; }; l = []; for(i of g()) l.push(i); l = l.join();');
 | 
						||
			has_Generator = l === '2,1';
 | 
						||
		} catch (e) {
 | 
						||
		}
 | 
						||
	})();
 | 
						||
 | 
						||
	/**
 | 
						||
	 * return a new RegExp instance with global flag
 | 
						||
	 */
 | 
						||
	function new_global_RegExp(regexp) {
 | 
						||
		return library_namespace.is_RegExp(regexp)
 | 
						||
		// do NOT chancg regexp.lastIndex
 | 
						||
		? new RegExp(regexp.source,
 | 
						||
				has_RegExp_flags ? regexp.global ? regexp.flags : regexp.flags
 | 
						||
						+ 'g' : regexp.ignoreCase ? 'ig' : 'g')
 | 
						||
		//
 | 
						||
		: new RegExp(regexp, 'g');
 | 
						||
	}
 | 
						||
	_.new_global_RegExp = new_global_RegExp;
 | 
						||
 | 
						||
	// String.prototype.matchAll()
 | 
						||
	// http://2ality.com/2018/02/string-prototype-matchall.html
 | 
						||
	// https://tc39.github.io/proposal-string-matchall/
 | 
						||
	// let all_matched = [...string.matchAll(regExp)].map(mapfn);
 | 
						||
	// let all_matched = Array.from(string.matchAll(regExp), mapfn);
 | 
						||
	var matchAll;
 | 
						||
	if (has_Generator) {
 | 
						||
		// e.g., node.ja 11.9.0, Chrome/61.0.3163.100 Electron/2.0.9
 | 
						||
		// TODO: returns an RegExpStringIterator
 | 
						||
		// String.prototype.matchAll 調用 RegExp.prototype[Symbol.matchAll]
 | 
						||
		eval('matchAll = function* matchAll(regexp) { const is_global = !library_namespace.is_RegExp(regexp) || regexp.global; regexp = new_global_RegExp(regexp); let matched; if (is_global) { while (matched = regexp.exec(this)) { yield matched; } } else { throw new TypeError("String.prototype.matchAll called with a non-global RegExp argument"); if (matched = regexp.exec(this)) { yield matched; } } }');
 | 
						||
	}
 | 
						||
 | 
						||
	// ------------------------------------------
 | 
						||
 | 
						||
	set_method(String.prototype, {
 | 
						||
		repeat : repeat,
 | 
						||
		trim : trim,
 | 
						||
		trimStart : trimStart,
 | 
						||
		trimEnd : trimEnd,
 | 
						||
		// String.prototype.padStart()
 | 
						||
		padStart : function padStart(maxLength, fillString) {
 | 
						||
			return pad_String.call(this, maxLength, fillString) + this;
 | 
						||
		},
 | 
						||
		// String.prototype.padEnd()
 | 
						||
		padEnd : function padEnd(maxLength, fillString) {
 | 
						||
			return this + pad_String.call(this, maxLength, fillString);
 | 
						||
		},
 | 
						||
		// String.prototype.includes()
 | 
						||
		includes : function includes(searchString, position) {
 | 
						||
			return this.indexOf(searchString, position) !== NOT_FOUND;
 | 
						||
		},
 | 
						||
		startsWith : startsWith,
 | 
						||
		endsWith : endsWith,
 | 
						||
 | 
						||
		matchAll : matchAll || function matchAll_array(regexp) {
 | 
						||
			/* const */var is_global = !library_namespace.is_RegExp(regexp)
 | 
						||
			//
 | 
						||
			|| regexp.global;
 | 
						||
 | 
						||
			/* const */regexp = new_global_RegExp(regexp);
 | 
						||
 | 
						||
			var matched, list = [];
 | 
						||
			if (is_global) {
 | 
						||
				while (matched = regexp.exec(this)) {
 | 
						||
					// yield matched;
 | 
						||
					list.push(matched);
 | 
						||
				}
 | 
						||
			} else if (matched = regexp.exec(this)) {
 | 
						||
				// e.g., 'a1b2A3B4a5cc'.matchAll(/(a)(.)/ig)
 | 
						||
				list.push(matched);
 | 
						||
			}
 | 
						||
 | 
						||
			return list;
 | 
						||
		},
 | 
						||
 | 
						||
		at : get_item_at,
 | 
						||
		codePointAt : codePointAt
 | 
						||
	});
 | 
						||
 | 
						||
	set_method(String, {
 | 
						||
		fromCodePoint : fromCodePoint
 | 
						||
	});
 | 
						||
 | 
						||
	// ----------------------------------------------------------------------------------------------------------------------------------------------------------//
 | 
						||
	// Date.*
 | 
						||
 | 
						||
	function pad_00(integer) {
 | 
						||
		return integer < 10 ? '0' + integer : integer;
 | 
						||
	}
 | 
						||
 | 
						||
	// Date.prototype.toISOString() for IE8
 | 
						||
	// https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString
 | 
						||
	function toISOString() {
 | 
						||
		var year = this.getUTCFullYear(), m = year < 0;
 | 
						||
		if (m)
 | 
						||
			year = -year;
 | 
						||
		if (year < 1000)
 | 
						||
			year = (year < 100 ? year < 10 ? '000' : '00' : '0') + year;
 | 
						||
		if (m)
 | 
						||
			year = '-' + year;
 | 
						||
 | 
						||
		m = this.getUTCMilliseconds();
 | 
						||
		if (m < 100)
 | 
						||
			m = (m < 10 ? '00' : '0') + m;
 | 
						||
 | 
						||
		return year + '-' + pad_00(this.getUTCMonth() + 1) + '-'
 | 
						||
				+ pad_00(this.getUTCDate()) + 'T' + pad_00(this.getUTCHours())
 | 
						||
				+ ':' + pad_00(this.getUTCMinutes()) + ':'
 | 
						||
				+ pad_00(this.getUTCSeconds()) + '.' + m + 'Z';
 | 
						||
	}
 | 
						||
 | 
						||
	set_method(Date, {
 | 
						||
		// Date.UTC()
 | 
						||
		UTC : function UTC(year, month, day, hour, minute, second,
 | 
						||
		//
 | 
						||
		millisecond) {
 | 
						||
			var date = new Date(year || 0, month || 0, isNaN(day) ? 1 : day,
 | 
						||
					hour || 0, minute || 0, second || 0, millisecond || 0);
 | 
						||
			return date.getTime() - 60 * 1000 * date.getTimezoneOffset();
 | 
						||
		},
 | 
						||
		// Date.now()
 | 
						||
		now : function now() {
 | 
						||
			return new Date().getTime();
 | 
						||
		},
 | 
						||
		// Date.parse()
 | 
						||
		parse : function parse(string) {
 | 
						||
			return new Date(string).getTime();
 | 
						||
		}
 | 
						||
	});
 | 
						||
 | 
						||
	set_method(Date.prototype, {
 | 
						||
		// Date.prototype.toJSON()
 | 
						||
		toJSON : function toJSON() {
 | 
						||
			return this.toISOString();
 | 
						||
		},
 | 
						||
		// Date.prototype.toISOString()
 | 
						||
		toISOString : toISOString
 | 
						||
	});
 | 
						||
 | 
						||
	// ----------------------------------------------------------------------------------------------------------------------------------------------------------//
 | 
						||
	// JSON.*
 | 
						||
 | 
						||
	/**
 | 
						||
	 * 2008/12/21 18:53:42<br />
 | 
						||
	 * value to json<br />
 | 
						||
	 * JavaScript Object Notation ECMA-262 3rd Edition
 | 
						||
	 * 
 | 
						||
	 * http://stackoverflow.com/questions/1500745/how-to-pass-parameters-in-eval-in-an-object-form
 | 
						||
	 * json={name:'~',values:..,description:'~'}<br />
 | 
						||
	 * window[json.name].apply(null, json.values)
 | 
						||
	 * 
 | 
						||
	 * usage:<br />
 | 
						||
	 * json(value)
 | 
						||
	 * 
 | 
						||
	 * parse:<code>
 | 
						||
	// 字串的前後記得要加上刮號 (),這是用來告知 Javascript Interpreter 這是個物件描述,不是要執行的 statement。
 | 
						||
	data=eval('('+data+')');
 | 
						||
	eval('data='+data);
 | 
						||
	</code>
 | 
						||
	 * 
 | 
						||
	 * TODO:
 | 
						||
	 * 
 | 
						||
	 * useObj<br />
 | 
						||
	 * 加入function object成員,.prototype可用with()。加入函數相依性(dependency)
 | 
						||
	 * 
 | 
						||
	 * array用name:<code>
 | 
						||
	(function(){
 | 
						||
		var o;
 | 
						||
		o = [...];
 | 
						||
		var i, v = {...};
 | 
						||
		for(i in v)
 | 
						||
			o[i] = v[i];
 | 
						||
		return o;
 | 
						||
	})()
 | 
						||
	</code>
 | 
						||
	 * 
 | 
						||
	 * recursion 循環參照<code>
 | 
						||
	(function(){
 | 
						||
		var o;
 | 
						||
		o = {a:[]};
 | 
						||
		o['b'] = [o['a']],
 | 
						||
		o['a'].push([o['b']]);
 | 
						||
		return o;
 | 
						||
	})()
 | 
						||
	</code>
 | 
						||
	 * 
 | 
						||
	 * BUG:<br />
 | 
						||
	 * function 之名稱被清除掉了,這可能會產生問題!<code>
 | 
						||
	(function(){
 | 
						||
		var f=function(){...};
 | 
						||
		f.a=...;
 | 
						||
		f.b=...;
 | 
						||
		f.prototype={ a:..., b:... }
 | 
						||
		return f;
 | 
						||
	})()
 | 
						||
	</code>
 | 
						||
	 * 
 | 
						||
	 * test recursion 循環參照:<br />
 | 
						||
	 * function 之名稱被清除掉了,這可能會產生問題!<code>
 | 
						||
	(function(){
 | 
						||
		var o=[], _1=[o];
 | 
						||
		o.push(_1);
 | 
						||
		return o;
 | 
						||
	})();
 | 
						||
 | 
						||
	var a=[], b;
 | 
						||
	a.push(b=[a]);
 | 
						||
	json(a);
 | 
						||
	</code>
 | 
						||
	 */
 | 
						||
 | 
						||
	// json[generateCode.dLK]='qNum,dQuote';
 | 
						||
	/**
 | 
						||
	 * 須判別來源是否為 String or Number!
 | 
						||
	 * 
 | 
						||
	 * @deprecated 改用 window.JSON, jQuery.parseJSON.
 | 
						||
	 * @param val
 | 
						||
	 * @param name
 | 
						||
	 * @param type
 | 
						||
	 *            type==2: inside object, treat undefined as ''
 | 
						||
	 * @returns
 | 
						||
	 */
 | 
						||
	function json(val, name, type) {
 | 
						||
		var _f = json, expA = [], expC = [], vType = typeof val, addE = function(
 | 
						||
				o, l, n) {
 | 
						||
			if (l) {
 | 
						||
				o = _f(o, 0, 2);
 | 
						||
				n = typeof n == 'undefined' || n === '' ? ''
 | 
						||
						: (/^(\d{1,8})?(\.\d{1,8})?$/.test(n)
 | 
						||
								|| /^[a-z_][a-z_\d]{0,30}$/i.test(n) ? n
 | 
						||
								: dQuote(n))
 | 
						||
								+ ':' + _f.separator;
 | 
						||
				expA.push(n, o[1]);
 | 
						||
 | 
						||
				// expC.push(_f.indentString+n+o[0].join(_f.line_separator+_f.indentString)+',');
 | 
						||
				o = o[0];
 | 
						||
				o[0] = n + (typeof o[0] == 'undefined' ? '' : o[0]);
 | 
						||
				o[o.length - 1] += ',';
 | 
						||
				for (var i = 0; i < o.length; i++)
 | 
						||
					o[i] = _f.indentString
 | 
						||
							+ (typeof o[i] == 'undefined' ? '' : o[i]);
 | 
						||
				expC = expC.concat(o);
 | 
						||
			} else
 | 
						||
				expA.push(o), expC.push(o);
 | 
						||
		}
 | 
						||
		// 去掉最後一組的 ',' 並作結。
 | 
						||
		, closeB = function(c) {
 | 
						||
			var v = expC.at(-1);
 | 
						||
			if (v.charAt(v.length - 1) == ',')
 | 
						||
				expC[expC.length - 1] = v.slice(0, v.length - 1);
 | 
						||
			addE(c);
 | 
						||
		};
 | 
						||
 | 
						||
		switch (vType) {
 | 
						||
		case 'number':
 | 
						||
			// http://msdn2.microsoft.com/zh-tw/library/y382995a(VS.80).aspx
 | 
						||
			// isFinite(value) ? String(value)
 | 
						||
			var k = 0, m = 'MAX_VALUE,MIN_VALUE,NEGATIVE_INFINITY,POSITIVE_INFINITY,NaN'
 | 
						||
					.split(','), t = 0;
 | 
						||
			if (val === NaN || val === Infinity || val === -Infinity)
 | 
						||
				t = '' + val;
 | 
						||
			else
 | 
						||
				for (; k < m.length; k++)
 | 
						||
					if (val === Number[m[k]]) {
 | 
						||
						t = 'Number.' + m[k];
 | 
						||
						break;
 | 
						||
					}
 | 
						||
			if (!t) {
 | 
						||
				// http://msdn2.microsoft.com/zh-tw/library/shydc6ax(VS.80).aspx
 | 
						||
				for (k = 0, m = 'E,LN10,LN2,LOG10E,LOG2E,PI,SQRT1_2,SQRT2'
 | 
						||
						.split(','); k < m.length; k++)
 | 
						||
					if (val === Math[m[k]]) {
 | 
						||
						t = 'Math.' + m[k];
 | 
						||
						break;
 | 
						||
					}
 | 
						||
				if (!t)
 | 
						||
					if (k = ('' + val).match(/^(-?\d*[1-9])(0{3,})$/))
 | 
						||
						t = k[1] + 'e' + k[2].length;
 | 
						||
					else {
 | 
						||
 | 
						||
						// 有理數判別
 | 
						||
						k = qNum(val);
 | 
						||
 | 
						||
						// 小數不以分數顯示. m==1:非分數
 | 
						||
						m = k[1];
 | 
						||
						while (m % 2 == 0)
 | 
						||
							m /= 2;
 | 
						||
						while (m % 5 == 0)
 | 
						||
							m /= 5;
 | 
						||
 | 
						||
						t = k[2] == 0 && m != 1 ? k[0] + '/' + k[1] :
 | 
						||
						// TODO: 加速(?)
 | 
						||
						(t = Math.floor(val)) == val
 | 
						||
								&& ('' + t).length > (t = '0x'
 | 
						||
										+ val.toString(16)).length ? t : val;
 | 
						||
					}
 | 
						||
 | 
						||
			}
 | 
						||
			addE(t);
 | 
						||
			break;
 | 
						||
		case 'null':
 | 
						||
			addE('' + val);
 | 
						||
			break;
 | 
						||
		case 'boolean':
 | 
						||
			addE(val);
 | 
						||
			break;
 | 
						||
		case 'string':
 | 
						||
			addE(dQuote(val));
 | 
						||
			break;
 | 
						||
		case 'undefined':
 | 
						||
			addE(type == 2 ? '' : 'undefined');
 | 
						||
			break;
 | 
						||
 | 
						||
		case 'function':
 | 
						||
			// 加入function
 | 
						||
			// object成員,.prototype可用with()。加入函數相依性(dependency)
 | 
						||
			var toS, f;
 | 
						||
			// 這在多執行緒有機會出問題!
 | 
						||
			if (typeof val.toString != 'undefined') {
 | 
						||
				toS = val.toString;
 | 
						||
				delete val.toString;
 | 
						||
			}
 | 
						||
			f = '' + val;
 | 
						||
			if (typeof toS != 'undefined')
 | 
						||
				val.toString = toS;
 | 
						||
 | 
						||
			// function 才會產生 \r\n 問題,所以先處理掉
 | 
						||
			f = f.replace(/\r?\n/g, _f.line_separator);
 | 
						||
			var r = /^function\s+([^(\s]+)/, m = f.match(r), t;
 | 
						||
			if (m)
 | 
						||
				m = m[1], addE('//	function [' + m + ']'), t = f.replace(r,
 | 
						||
						'function' + _f.separator);
 | 
						||
			if (m && t.indexOf(m) !== NOT_FOUND)
 | 
						||
				library_namespace.error('function [' + m
 | 
						||
						+ '] 之名稱被清除掉了,這可能會產生問題!');
 | 
						||
			addE(t || f);
 | 
						||
			// UNDO
 | 
						||
			break;
 | 
						||
 | 
						||
		case 'object':
 | 
						||
			try {
 | 
						||
				if (val === null) {
 | 
						||
					addE('' + val);
 | 
						||
					break;
 | 
						||
				}
 | 
						||
				var c = val.constructor;
 | 
						||
				if (c === RegExp) {
 | 
						||
					addE(val);
 | 
						||
					break;
 | 
						||
				}
 | 
						||
				// typeof val.getTime=='function'
 | 
						||
				if (c == Date || vType == 'date') {
 | 
						||
					// 與 now 相隔過短(<1e7, 約3h)視為 now。
 | 
						||
					// 但若是 new Date()+3 之類的會出現誤差!
 | 
						||
					addE('new Date'
 | 
						||
					// date 被當作 object
 | 
						||
					+ ((val - new Date) > 1e7 ? '(' + val.getTime() + ')' : ''));
 | 
						||
					break;
 | 
						||
				}
 | 
						||
				if (('' + c).indexOf('Error') !== NOT_FOUND) {
 | 
						||
					addE('new Error'
 | 
						||
							+ (val.number || val.description ? '('
 | 
						||
									+ (val.number || '')
 | 
						||
									+ (val.description ? (val.number ? ',' : '')
 | 
						||
											+ dQuote(val.description)
 | 
						||
											: '') + ')'
 | 
						||
									: ''));
 | 
						||
					break;
 | 
						||
				}
 | 
						||
 | 
						||
				var useObj = 0;
 | 
						||
				if (c == Array) {
 | 
						||
					var i, l = 0;
 | 
						||
					if (!_f.forceArray)
 | 
						||
						for (i in val)
 | 
						||
							if (isNaN(i)) {
 | 
						||
								useObj = 1;
 | 
						||
								break;
 | 
						||
							} else
 | 
						||
								l++;
 | 
						||
 | 
						||
					if (_f.forceArray || !useObj && l > val.length * .8) {
 | 
						||
						addE('[');
 | 
						||
						for (i = 0; i < val.length; i++)
 | 
						||
							addE(val[i], 1);
 | 
						||
						closeB(']');
 | 
						||
						break;
 | 
						||
					} else
 | 
						||
						useObj = 1;
 | 
						||
				}
 | 
						||
 | 
						||
				if (useObj || c == Object) {// instanceof
 | 
						||
					addE('{');
 | 
						||
					for ( var i in val)
 | 
						||
						addE(val[i], 1, i);
 | 
						||
					closeB('}');
 | 
						||
					break;
 | 
						||
				}
 | 
						||
				addE(dQuote(val));
 | 
						||
				break;
 | 
						||
			} catch (e) {
 | 
						||
				if (28 == (e.number & 0xFFFF))
 | 
						||
					alert('json: Too much recursion?\n循環參照?');
 | 
						||
				return;
 | 
						||
			}
 | 
						||
 | 
						||
		case 'unknown':
 | 
						||
			// sometimes we have this kind of type
 | 
						||
		default:
 | 
						||
			alert('Unknown type: [' + vType + '] (constructor: '
 | 
						||
					+ val.constructor + '), please contract me!\n' + val);
 | 
						||
			break;
 | 
						||
		// alert(vType);
 | 
						||
		}
 | 
						||
		return type ? [ expC, expA ] : expC.join(_f.line_separator);
 | 
						||
	}
 | 
						||
	// dependency List Key
 | 
						||
	json.dL = 'dependencyList';
 | 
						||
	json.forceArray = 1;
 | 
						||
 | 
						||
	json.indentString = '	';
 | 
						||
	json.line_separator = '\n';
 | 
						||
	json.separator = ' ';
 | 
						||
 | 
						||
	// --------------------------------------------
 | 
						||
	// JSON polyfill
 | 
						||
	// 2021: library_namespace.parse_JSON() @ _structure/module.js
 | 
						||
 | 
						||
	function JSON_parse(text, reviver) {
 | 
						||
 | 
						||
		function parse_next_value() {
 | 
						||
			/**
 | 
						||
			 * <code>
 | 
						||
			[...
 | 
						||
			{...
 | 
						||
			"...
 | 
						||
			00000
 | 
						||
			null
 | 
						||
			<code>
 | 
						||
			 */
 | 
						||
			var matched = text.match(/^\s*([\[{"]|null|\d+)/);
 | 
						||
			if (!matched) {
 | 
						||
				// SyntaxError
 | 
						||
				return;
 | 
						||
			}
 | 
						||
 | 
						||
			text = text.slice(/* matched.index(=0) + */matched[0].length);
 | 
						||
 | 
						||
			if (matched[1] === 'null' || /^\d/.test(matched[1])) {
 | 
						||
				return matched[1] === 'null' ? null : +matched[1];
 | 
						||
			}
 | 
						||
 | 
						||
			// https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/JSON
 | 
						||
			if (matched[1] === '"') {
 | 
						||
				matched = text.match(/^(?:\\"|[^"])*"/);
 | 
						||
				text = text.slice(matched[0].length);
 | 
						||
 | 
						||
				return matched[0].slice(0, -1)
 | 
						||
				//
 | 
						||
				.replace(/\\u([0-9a-f]{4})/ig, function(code) {
 | 
						||
					return String.fromCharCode(parseInt(code, 16));
 | 
						||
				}).replace(/\\([\s\S]|$)/g, function(escape_char) {
 | 
						||
					// /\\(["\\\/\b\f\t\r\n])/g
 | 
						||
					var escaped = {
 | 
						||
						'"' : '"',
 | 
						||
						'\\' : '\\',
 | 
						||
						b : '\b',
 | 
						||
						f : '\f',
 | 
						||
						t : '\t',
 | 
						||
						r : '\r',
 | 
						||
						n : '\n'
 | 
						||
					}[escape_char];
 | 
						||
					if (!escaped) {
 | 
						||
						throw new SyntaxError('Unexpected token \\'
 | 
						||
						// Invalid JSON
 | 
						||
						+ escape_char + ' in JSON');
 | 
						||
					}
 | 
						||
					return escaped
 | 
						||
				});
 | 
						||
			}
 | 
						||
 | 
						||
			if (matched[1] === '[') {
 | 
						||
				var array = [];
 | 
						||
				matched = text.match(/^\s*\]/);
 | 
						||
				if (matched) {
 | 
						||
					// "{}"
 | 
						||
					text = text.slice(matched[0].length);
 | 
						||
					return array;
 | 
						||
				}
 | 
						||
				while (true) {
 | 
						||
					var next_value = parse_next_value();
 | 
						||
					if (next_value === undefined)
 | 
						||
						return;
 | 
						||
					array.push(next_value);
 | 
						||
					matched = text.match(/^\s*([,\]])/);
 | 
						||
					if (!matched) {
 | 
						||
						// SyntaxError
 | 
						||
						return;
 | 
						||
					}
 | 
						||
					text = text.slice(matched[0].length);
 | 
						||
					if (matched[1] === ']') {
 | 
						||
						break;
 | 
						||
					}
 | 
						||
				}
 | 
						||
				return array;
 | 
						||
			}
 | 
						||
 | 
						||
			// assert: matched[1] === '{'
 | 
						||
			var object = {};
 | 
						||
			matched = text.match(/^\s*}/);
 | 
						||
			if (matched) {
 | 
						||
				// "{}"
 | 
						||
				text = text.slice(matched[0].length);
 | 
						||
				return object;
 | 
						||
			}
 | 
						||
			while (true) {
 | 
						||
				var next_key = parse_next_value();
 | 
						||
				if (typeof next_key !== 'string') {
 | 
						||
					throw new SyntaxError('Unexpected token in JSON: '
 | 
						||
							+ next_key + '; ' + text);
 | 
						||
				}
 | 
						||
				matched = text.match(/^\s*:/);
 | 
						||
				if (!matched) {
 | 
						||
					// SyntaxError
 | 
						||
					return;
 | 
						||
				}
 | 
						||
				text = text.slice(matched[0].length);
 | 
						||
				var next_value = parse_next_value();
 | 
						||
				if (next_value === undefined)
 | 
						||
					return;
 | 
						||
				matched = text.match(/^\s*([,}])/);
 | 
						||
				if (!matched) {
 | 
						||
					// SyntaxError
 | 
						||
					return;
 | 
						||
				}
 | 
						||
				object[next_key] = next_value;
 | 
						||
				text = text.slice(matched[0].length);
 | 
						||
				if (matched[1] === '}') {
 | 
						||
					break;
 | 
						||
				}
 | 
						||
			}
 | 
						||
			return object;
 | 
						||
		}
 | 
						||
 | 
						||
		text = String(text);
 | 
						||
 | 
						||
		var result = parse_next_value();
 | 
						||
		if (text.trim())
 | 
						||
			throw new SyntaxError('Unexpected token in JSON: ' + text);
 | 
						||
 | 
						||
		return result;
 | 
						||
	}
 | 
						||
 | 
						||
	function JSON_stringify(value, replacer, space) {
 | 
						||
		var object_Set = new Set;
 | 
						||
 | 
						||
		if (!space) {
 | 
						||
			space = '';
 | 
						||
		} else if (typeof space === 'number') {
 | 
						||
			if (space > 10)
 | 
						||
				space = 10;
 | 
						||
			else if (space < 1)
 | 
						||
				space = 0;
 | 
						||
			space = ' '.repeat(space);
 | 
						||
		} else {
 | 
						||
			space = String(space).slice(0, 10);
 | 
						||
		}
 | 
						||
 | 
						||
		function stringify(object, /* 累積的空格 */_spaces, key, obj) {
 | 
						||
			if (object && typeof object.toJSON === 'function') {
 | 
						||
				// e.g., {Date}
 | 
						||
				object = object.toJSON(key);
 | 
						||
			}
 | 
						||
 | 
						||
			if (replacer)
 | 
						||
				object = replacer.call(obj, key, value);
 | 
						||
 | 
						||
			if (typeof object === 'string')
 | 
						||
				return '"' + object.replace(/["\\\/\b\f\t\r\n]/g, '\\"') + '"';
 | 
						||
 | 
						||
			if (!object || typeof object !== 'object') {
 | 
						||
				if (object === undefined) {
 | 
						||
					if (!key)
 | 
						||
						return;
 | 
						||
					object = null;
 | 
						||
				}
 | 
						||
				object = String(object);
 | 
						||
				return object;
 | 
						||
			}
 | 
						||
 | 
						||
			var next_spaces = _spaces + space;
 | 
						||
 | 
						||
			var output = [];
 | 
						||
			if (Array.isArray(object)) {
 | 
						||
				for (var index = 0; index < object.length; index++) {
 | 
						||
					var value = object[index];
 | 
						||
					if (object_Set.has(value)) {
 | 
						||
						throw new Error(
 | 
						||
						// Too much recursion?\n循環參照?
 | 
						||
						'Converting circular structure to JSON: [' + index
 | 
						||
								+ ']');
 | 
						||
					}
 | 
						||
					if (value === undefined)
 | 
						||
						value = null;
 | 
						||
					output.push('\n' + next_spaces
 | 
						||
							+ stringify(value, next_spaces, key, object));
 | 
						||
				}
 | 
						||
				return '[' + output.join(',') + (space ? '\n' + _spaces : '')
 | 
						||
						+ ']';
 | 
						||
			}
 | 
						||
 | 
						||
			var keys = Object.keys(object);
 | 
						||
			for (var index = 0; index < keys.length; index++) {
 | 
						||
				var key = keys[index], value = object[key];
 | 
						||
				if (object_Set.has(value)) {
 | 
						||
					throw new Error('Converting circular structure to JSON: .'
 | 
						||
							+ key);
 | 
						||
				}
 | 
						||
				if (value === undefined)
 | 
						||
					continue;
 | 
						||
				output.push('\n' + next_spaces + stringify(key, next_spaces)
 | 
						||
						+ (space ? ': ' : ':')
 | 
						||
						+ stringify(value, next_spaces, key, object));
 | 
						||
			}
 | 
						||
			return '{' + output.join(',') + (space ? '\n' + _spaces : '') + '}';
 | 
						||
		}
 | 
						||
 | 
						||
		return stringify(value, '');
 | 
						||
	}
 | 
						||
 | 
						||
	if (!globalThis.JSON) {
 | 
						||
		globalThis.JSON = {
 | 
						||
			parse : JSON_parse,
 | 
						||
			stringify : JSON_stringify
 | 
						||
		};
 | 
						||
	}
 | 
						||
 | 
						||
	// --------------------------------------------
 | 
						||
	// for old node.js
 | 
						||
 | 
						||
	function Buffer_from(source, encoding) {
 | 
						||
		return new Buffer(source, encoding);
 | 
						||
	}
 | 
						||
 | 
						||
	if (library_namespace.platform.nodejs) {
 | 
						||
		if (!Buffer.from) {
 | 
						||
			// For Node.js v5.11.1 and below
 | 
						||
			// e.g., node.js v0.10.25
 | 
						||
			Buffer.from = Buffer_from
 | 
						||
		}
 | 
						||
		if (!Buffer.allocUnsafe) {
 | 
						||
			Buffer.allocUnsafe = Buffer_from;
 | 
						||
		}
 | 
						||
		if (!Buffer.prototype.forEach) {
 | 
						||
			Buffer.prototype.forEach = Array.prototype.forEach;
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	// ----------------------------------------------------------------------------------------------------------------------------------------------------------//
 | 
						||
 | 
						||
	/**
 | 
						||
	 * Promise polyfill
 | 
						||
	 * 
 | 
						||
	 * <code>
 | 
						||
 | 
						||
	https://babeljs.io/repl
 | 
						||
 | 
						||
	Promise test cases:
 | 
						||
 | 
						||
	promise = new Promise(function(r,R){r()})
 | 
						||
		r(value)	fulfilled
 | 
						||
			(p=new Promise(function(r,R){r( 2 )}));console.log(p)
 | 
						||
				Promise {<resolved>: 2}
 | 
						||
		r(promise)	promise→p
 | 
						||
			(p=new Promise(function(r,R){r( Promise.resolve(2) )}));console.log(p)
 | 
						||
				p: Promise {<pending>}
 | 
						||
				→ Promise {<resolved>: 2}
 | 
						||
			(p=new Promise(function(r,R){r( Promise.reject(2) )})).then(null,function(){});console.log(p)
 | 
						||
				p: Promise {<pending>}
 | 
						||
				→ Promise {<rejected>: 2}
 | 
						||
			(q=(p=new Promise(function(r,R){r( Promise.reject(2) )})).then(3,4)).then(5,function(v){console.log(v)});console.log(p);console.log(q)
 | 
						||
				2
 | 
						||
				p: Promise {<pending>}
 | 
						||
				→ Promise {<rejected>: 2}
 | 
						||
			p=new Promise(function(r,R){r(Promise.resolve(1));throw 2});console.log(p)
 | 
						||
				p: Promise {<pending>}
 | 
						||
				→ Promise {<resolved>: 1}
 | 
						||
		r(thenable)	thenable→p
 | 
						||
			(p=new Promise(function(r,R){r({then:function(r,R){r(4)}})}));console.log(p)
 | 
						||
				Promise {<pending>}
 | 
						||
				→ Promise {<resolved>: 4}
 | 
						||
			(p=new Promise(function(r,R){r({then:function(r,R){console.log(this);R(4)}})})).then(null,function(){});
 | 
						||
				this: {then: ƒ}
 | 
						||
				p → Promise {<rejected>: 4}
 | 
						||
		R(value)	rejected
 | 
						||
		R(promise)
 | 
						||
			(p=new Promise(function(r,R){R( Promise.resolve(2) )})).then(null,function(){});console.log(p)
 | 
						||
				Promise {<rejected>: Promise}
 | 
						||
		R(thenable)
 | 
						||
 | 
						||
	promise.then(r_,R_)
 | 
						||
	promise: pending, fulfilled, rejected
 | 
						||
		r_ return value	value→return
 | 
						||
			(p=new Promise(function(r){throw 2})).then(null,function(){});console.log(p)
 | 
						||
				Promise {<rejected>: 2}
 | 
						||
		r_ return promise	p→promise→return
 | 
						||
			p=Promise.resolve(2).then(function(){return Promise.resolve(4)});console.log(p)
 | 
						||
				p: Promise {<pending>}
 | 
						||
				→ Promise {<resolved>: 4}
 | 
						||
			(p=Promise.reject(3)).then(null,function(){});(q=Promise.resolve(2).then(function(){return p})).then(null,function(){});console.log(p);console.log(q)
 | 
						||
				Promise {<rejected>: 3} ; Promise {<pending>}
 | 
						||
				→ Promise {<rejected>: 3} ; Promise {<rejected>: 3}
 | 
						||
			p=Promise.resolve(2).then(function(){return new Promise(function(r){setTimeout(function(){r(4)},2);})});console.log(p)
 | 
						||
				p: Promise {<pending>}
 | 
						||
				→ Promise {<resolved>: 4}
 | 
						||
			p=Promise.resolve(2).then(function(){return new Promise(function(){})});console.log(p)
 | 
						||
				Promise {<pending>}
 | 
						||
			Promise.resolve().then(function(){return Promise.reject(3)}).then(null,function(){});console.log(p)
 | 
						||
				Promise {<pending>}
 | 
						||
			p=Promise.resolve().then(function(){return new Promise(function(r){setTimeout(function(){console.log(4);r(4)},500)}).then(function(v){console.log(++v);return v})}).then(function(v){console.log(++v);return v},function(){});console.log(p)
 | 
						||
				p: Promise {<pending>}
 | 
						||
				4
 | 
						||
				5
 | 
						||
				6
 | 
						||
				p → Promise {<resolved>: 6}
 | 
						||
		r_ return thenable	p→thenable→returned
 | 
						||
			p=Promise.resolve(2).then(function(){return {then:function(r,R){r(4)}}});console.log(p)
 | 
						||
				p: Promise {<pending>}
 | 
						||
				→ Promise {<resolved>: 4}
 | 
						||
			(p=Promise.resolve(2).then(function(){return {then:function(r,R){R(4)}}})).then(null,function(){});
 | 
						||
				chrome: Promise {<resolved>: undefined}	or	firefox: Promise { <state>: "pending" }
 | 
						||
				p: Promise {<pending>}
 | 
						||
				→ Promise {<rejected>: 4}
 | 
						||
			p=Promise.resolve(2).then(function(){return {then:function(r,R){setTimeout(function(){r(4)},2);}}});console.log(p)
 | 
						||
				p: Promise {<pending>}
 | 
						||
				→ Promise {<resolved>: 4}
 | 
						||
		R_ return value
 | 
						||
			(p=new Promise(function(r,R){R(2)})).then(null,function(){});console.log(p)
 | 
						||
				Promise {<rejected>: 2}
 | 
						||
		R_ return promise
 | 
						||
			(p=new Promise(function(r,R){R(Promise.resolve(2))})).then(null,function(){});console.log(p)
 | 
						||
				Promise {<rejected>: Promise}
 | 
						||
			(q=Promise.reject(2)).then(null,function(){});(p=new Promise(function(r,R){R(q)})).then(null,function(){});console.log(p)
 | 
						||
				Promise {<rejected>: Promise}
 | 
						||
		R_ return thenable
 | 
						||
			(p=new Promise(function(r,R){R({then:function(r,R){R(2)}})})).then(null,function(){});console.log(p)
 | 
						||
				Promise {<rejected>: {…}}
 | 
						||
 | 
						||
 | 
						||
	Promise.resolve(v)
 | 
						||
		v: value: tested above
 | 
						||
			p=Promise.resolve('v');console.log(p)
 | 
						||
			p=Promise.resolve(2).then(function(v){console.log(v);return 3}).then(function(v){console.log(v);return 3});console.log(p)
 | 
						||
				Promise {<pending>}
 | 
						||
				2
 | 
						||
				3
 | 
						||
		v: promise
 | 
						||
			(q=Promise.resolve(2)).then(null,function(){});p=Promise.resolve(q);console.log(p)
 | 
						||
				Promise {<resolved>: 2}
 | 
						||
			(q=Promise.reject(2)).then(null,function(){});p=Promise.resolve(q);console.log(p)
 | 
						||
				Promise {<rejected>: 2}
 | 
						||
		v: thenable
 | 
						||
			q={then:function(r,R){r(2)}};p=Promise.resolve(q);console.log(p)
 | 
						||
				p: Promise {<pending>}
 | 
						||
				→ Promise {<resolved>: 2}
 | 
						||
			q={then:function(r,R){R(2)}};(p=Promise.resolve(q)).then(null,function(){});console.log(p)
 | 
						||
				p: Promise {<pending>}
 | 
						||
				→ Promise {<rejected>: 2}
 | 
						||
			q={b:2,then:function(r){console.log(this.b);r(2)}};p=Promise.resolve(q);console.log(p)
 | 
						||
			q={b:2,then:function(r){console.log(this.b);throw 2}};(p=Promise.resolve(q)).then(null,function(){});console.log(p)
 | 
						||
 | 
						||
 | 
						||
	Promise.reject(v)
 | 
						||
		v: value: tested above
 | 
						||
			(p=Promise.reject('v')).then(null,function(){});console.log(p)
 | 
						||
			p=Promise.resolve(1);console.log(p);(q=Promise.reject(p)).then(null,function(){});console.log(p!==q)
 | 
						||
				Promise {<resolved>: 1}
 | 
						||
				console.log(p!==q): true
 | 
						||
			(p=Promise.reject(1)).then(null,function(){});(q=Promise.reject(p)).then(null,function(){});console.log(p!==q)
 | 
						||
				console.log(p!==q): true
 | 
						||
		v: promise
 | 
						||
			(p=Promise.reject(Promise.resolve(2))).then(null,function(){});console.log(p)
 | 
						||
				Promise {<rejected>: Promise}
 | 
						||
			(q=Promise.resolve(2)).then(null,function(){});(p=Promise.reject(q)).then(null,function(){});console.log(p)
 | 
						||
				Promise {<rejected>: Promise}
 | 
						||
			(q=Promise.reject(2)).then(null,function(){});(p=Promise.reject(q)).then(null,function(){});console.log(p)
 | 
						||
				Promise {<rejected>: Promise}
 | 
						||
		v: thenable
 | 
						||
			q={then:function(r,R){R(2)}};(p=Promise.reject(q)).then(null,function(){});console.log(p)
 | 
						||
				Promise {<rejected>: {…}}
 | 
						||
 | 
						||
 | 
						||
	Promise.all ( iterable )
 | 
						||
		iterable: value:
 | 
						||
			(p=Promise.all([])).then(function(L){console.log(Array.isArray(L)+','+L.length)});console.log(p)
 | 
						||
				p: Promise {<resolved>: Array(0)}
 | 
						||
				true,0
 | 
						||
			Promise.all(1).then(function(L){console.log('_'+L)},function(x){console.log('!'+x.message)})
 | 
						||
				x: TypeError: number 1 is not iterable (cannot read property Symbol(Symbol.iterator))
 | 
						||
		iterable: promise, thenable:
 | 
						||
			Promise.all([2,1,Promise.reject(8)]).then(function(){},function(R){console.log(R)})
 | 
						||
				8
 | 
						||
			l=[3,'a',new Promise(function(r,R){setTimeout(function(){r(4)},8)}),9,{then:3},{then:function(r,R){return r(7)}},Promise.resolve(3),[2,3],null,undefined];Promise.all(l).then(function(L){console.log(L);})
 | 
						||
				Promise {<pending>}
 | 
						||
				(10) [3, "a", 4, 9, {…}, 7, 3, Array(2), null, undefined]
 | 
						||
 | 
						||
 | 
						||
	Promise.race ( iterable )
 | 
						||
			Promise.race([new Promise(function(r,R){setTimeout(function(){console.log(500);r(3)},500)}),new Promise(function(r,R){setTimeout(function(){console.log(5);r(1)},5)}),Promise.resolve(6),new Promise(function(r,R){setTimeout(function(){console.log(400);r(7)},400)})]).then(function(r){console.log('** '+r)})
 | 
						||
				→ 6
 | 
						||
 | 
						||
	promise.finally(onFinally)	Promise_finally(onFinally)
 | 
						||
		onFinally: value
 | 
						||
			p=Promise.resolve(2)['finally'](3).then(function(v){console.log(v);return 4});console.log(p)
 | 
						||
				2
 | 
						||
				p: Promise {<pending>}
 | 
						||
				→ Promise {<resolved>: 4}
 | 
						||
		onFinally: function return value
 | 
						||
			p=Promise.resolve(2).then(function(v){console.log(v);return 3})['finally'](function(v){console.log('_'+v);return 4}).then(function(v){console.log(v);return 5});console.log(p)
 | 
						||
				p: Promise {<pending>}
 | 
						||
				2
 | 
						||
				_undefined
 | 
						||
				3
 | 
						||
				p → Promise {<resolved>: 5}
 | 
						||
			p=Promise.reject(2).then(function(v){console.log(v);return 3})['finally'](function(v){console.log('_'+v);return 4}).then(function(v){console.log(v);return 5}).then(null,function(v){console.log(v);return 6});console.log(p)
 | 
						||
				p: Promise {<pending>}
 | 
						||
				_undefined
 | 
						||
				2
 | 
						||
				p → Promise {<resolved>: 6}
 | 
						||
		onFinally: function return promise, thenable:
 | 
						||
			p=Promise.resolve(2).then(function(v){console.log(v);return 3})['finally'](function(v){console.log('_'+v);return Promise.resolve(3)}).then(function(v){console.log(v);return 5});console.log(p)
 | 
						||
				p: Promise {<pending>}
 | 
						||
				2
 | 
						||
				_undefined
 | 
						||
				3
 | 
						||
				p → Promise {<resolved>: 5}
 | 
						||
 | 
						||
	// --------------------------------------------------------------------------------------
 | 
						||
 | 
						||
	var v='';(p=new Promise(function(r,R){r(1);throw '0'}))
 | 
						||
	.then(function(x){v+=x;console.log(v);return 2})
 | 
						||
	.then(function(x){v+=x;return 3})
 | 
						||
	.then(function(x){v+=x;throw '4'},function(y){v+=y;return 0})
 | 
						||
	.then(function(x){v+=x;throw 0},function(y){v+=y;return 5})
 | 
						||
	.then(function(x){v+=x;return 6})
 | 
						||
	.then(function(x){v+=x;console.log(v==="_123456")});v='_';console.log(p)
 | 
						||
	console.log(v)
 | 
						||
 | 
						||
	var v='';(p=new Promise(function(r,R){r(Promise.resolve(1));r(0);throw '2'}))
 | 
						||
	.then(function(x){v+=x;console.log(v);return 2})
 | 
						||
	.then(function(x){v+=x;return 3})
 | 
						||
	.then(function(x){v+=x;throw '4'},function(y){v+=y;return 0})
 | 
						||
	.then(function(x){v+=x;throw 0},function(y){v+=y;return 5})
 | 
						||
	.then(function(x){v+=x;return 6})
 | 
						||
	.then(function(x){v+=x;console.log(v==="_123456")});v='_';console.log(p)
 | 
						||
	console.log(v)
 | 
						||
 | 
						||
 | 
						||
	(q=Promise.reject(2).then(function(){})).then(function(){},function(y){})
 | 
						||
	// console.log(q): Promise {<rejected>: 2}
 | 
						||
 | 
						||
 | 
						||
	var v='';(p=new Promise(function(r,R){R(1);r(0)}))
 | 
						||
	.then(function(x){v+=x;console.log('!!');return 0})
 | 
						||
	.then(function(x){v+=x;return 0})
 | 
						||
	.then(function(x){v+=x;throw '0'},function(y){console.log('y='+y);v+=y;return 2})
 | 
						||
	.then(function(x){v+=x;throw '3'},function(y){v+=y;return 0})
 | 
						||
	.then(function(x){v+=x;return 0})
 | 
						||
	.then(function(x){v+=x;},function(x){v+=x;console.log(v);console.log(v==="_123")});v='_';
 | 
						||
 | 
						||
 | 
						||
	var q=null,r='';Promise.resolve()
 | 
						||
	.then(function(x){r+=x;return 1})
 | 
						||
	.then(function(x){r+=x;q=new Promise(function(r,R){setTimeout(r,8)});return q})
 | 
						||
	.then(function(x){r+=x;return 2})
 | 
						||
	.then(function(x){r+=x;
 | 
						||
		q
 | 
						||
		.then(function(x){r+=x;return 3})
 | 
						||
		.then(function(x){r+=x;return 5})
 | 
						||
		.then(function(x){r+=x;return 7})
 | 
						||
		.then(function(x){r+=x;});
 | 
						||
		return '_'})
 | 
						||
	.then(function(x){r+=x;return 4})
 | 
						||
	.then(function(x){r+=x;return 6})
 | 
						||
	.then(function(x){r+=x;return 8})
 | 
						||
	.then(function(x){r+=x;console.log(r==="undefined1undefined2undefined_345678")})
 | 
						||
 | 
						||
 | 
						||
	var q=null,r='';Promise.resolve()
 | 
						||
	.then(function(x){r+='_';return 1})
 | 
						||
	.then(function(x){r+=x;q=new Promise(function(r,R){setTimeout(function(){R(2)},8)});q.then(function(x){r+=x;return 0},function(y){r+=2&&y;});return q})
 | 
						||
	.then(function(x){r+=x;return 0})
 | 
						||
	.then(function(x){r+=x;
 | 
						||
		q
 | 
						||
		.then(function(x){r+=x;return 0},function(y){r+=y;return 0})
 | 
						||
		.then(function(x){r+=x;return 0})
 | 
						||
		.then(function(x){r+=x;return 0})
 | 
						||
		.then(function(x){r+=x;});
 | 
						||
		return 0})
 | 
						||
	.then(function(x){r+=x;return 0},function(y){r+=3;return 4})
 | 
						||
	.then(function(x){r+=x;return 5})
 | 
						||
	.then(function(x){r+=x;return 6})
 | 
						||
	.then(function(x){r+=x;console.log(r==="_123456")})
 | 
						||
 | 
						||
 | 
						||
	var q=null,r='';Promise.resolve()
 | 
						||
	.then(function(x){r+='_';return 1})
 | 
						||
	.then(function(x){r+=x;q=new Promise(function(r,R){setTimeout(function(){R(2)},8)});q.then(function(x){r+=x;return 0},function(y){r+=2&&y;});return q})
 | 
						||
	.then(function(x){r+=x;return 0},function(y){r+=3;return 4})
 | 
						||
	//.then(function(x){r+='^^^';return 4})
 | 
						||
	.then(function(x){r+=4&&x;
 | 
						||
		console.log(q)
 | 
						||
		q // Promise {<rejected>: 2}
 | 
						||
		.then(function(x){r+=x;return 0},function(y){r+=5;return 7})
 | 
						||
		.then(function(x){r+=x;return 9})
 | 
						||
		.then(function(x){r+=x;return 'b'})
 | 
						||
		.then(function(x){r+=x;});
 | 
						||
		return 6})
 | 
						||
	.then(function(x){r+=6&&x;return 8},function(y){r+=y;return 0})
 | 
						||
	.then(function(x){r+=x;return 'a'})
 | 
						||
	.then(function(x){r+=x;return 'c'})
 | 
						||
	.then(function(x){r+=x;console.log(r==="_123456789abc")})
 | 
						||
 | 
						||
 | 
						||
 | 
						||
 | 
						||
	r='';(q=new Promise(function(r,R){setTimeout(function(){R(1)},5000)}))
 | 
						||
	.then(function(x){r+=x;return 0},function(y){r+=1&&y;return 2})
 | 
						||
	.then(function(x){r+=2&&x;return 3})
 | 
						||
	.then(function(x){r+=3&&x;console.log(r==='123')})
 | 
						||
 | 
						||
	r='';(q=new Promise(function(r,R){setTimeout(function(){R(1)},50)}))
 | 
						||
	.then(function(x){r+=x;return 0},function(y){r+=1&&y;return 2})
 | 
						||
	.then(function(x){r+=2&&x;return 3})
 | 
						||
	//console.log(r): '12'
 | 
						||
	//
 | 
						||
	// another:
 | 
						||
	q.then(function(x){r+=3&&x;console.log(r==='123')})
 | 
						||
	// Uncaught (in promise) 1
 | 
						||
	// r==='12'
 | 
						||
 | 
						||
 | 
						||
 | 
						||
	q=new Promise(function(r,R){setTimeout(R,8)})
 | 
						||
	s=q.then(function(x){},function(y){})
 | 
						||
	console.log([s!==q,s,q])
 | 
						||
	// s: resolved, q: rejected. then方法執行完會另外產生一個新的 Promise 物件。
 | 
						||
	// https://eyesofkids.gitbooks.io/javascript-start-es6-promise/content/contents/then_adv.html
 | 
						||
 | 
						||
 | 
						||
 | 
						||
	test of Promise.prototype.then(): this[KEY_STATE] === PromiseState_pending
 | 
						||
 | 
						||
	p=new Promise(function(r,R){setTimeout(function(){r(9)},8)});
 | 
						||
	p.then(function(a){r+=1});
 | 
						||
	p.then(function(a){r+=2});
 | 
						||
	p.then(function(a){r+=3});
 | 
						||
	console.log(p);r='';
 | 
						||
	//
 | 
						||
	console.log(r==='123');
 | 
						||
 | 
						||
	p=new Promise(function(r,R){setTimeout(function(){R(9)},8)});
 | 
						||
	p.then(null,function(a){r+=1});
 | 
						||
	p.then(null,function(a){r+=2});
 | 
						||
	p.then(null,function(a){r+=3});
 | 
						||
	console.log(p);r='';
 | 
						||
	//
 | 
						||
	console.log(r==='123');
 | 
						||
 | 
						||
 | 
						||
	p=new Promise(function(r,R){setTimeout(function(){r(9)},8)});p.then(123).then(function(x){console.log(x===9);});
 | 
						||
 | 
						||
 | 
						||
	</code>
 | 
						||
	 * 
 | 
						||
	 * @since 2018/9/16 7:37:10
 | 
						||
	 */
 | 
						||
 | 
						||
	// Promise 標準規格書/定義
 | 
						||
	// https://www.ecma-international.org/ecma-262/9.0/index.html#sec-promise-objects
 | 
						||
	// https://promisesaplus.com/
 | 
						||
	// https://github.com/promises-aplus/promises-spec
 | 
						||
	// https://developer.mozilla.org/en-US/docs/Web/API/Window/setImmediate
 | 
						||
	// http://azu.github.io/promises-book/
 | 
						||
	// 實作 Promise polyfill
 | 
						||
	// https://github.com/stefanpenner/es6-promise/tree/master/lib/es6-promise
 | 
						||
	// https://github.com/taylorhakes/promise-polyfill/blob/master/dist/polyfill.js
 | 
						||
	// https://www.ecma-international.org/ecma-262/9.0/index.html#sec-enqueuejob
 | 
						||
	var EnqueueJob = library_namespace.platform.nodejs && process.nextTick
 | 
						||
	// https://github.com/stefanpenner/es6-promise/blob/master/lib/es6-promise/asap.js
 | 
						||
	// node version 0.10.x displays a deprecation warning when nextTick is used
 | 
						||
	// recursively
 | 
						||
	// see https://github.com/cujojs/when/issues/410 for details
 | 
						||
	? function(job) {
 | 
						||
		return process.nextTick(job);
 | 
						||
	}
 | 
						||
	// https://github.com/cssmagic/ChangeLog/issues/3
 | 
						||
	// setImmediate() 會調度宏微任務而不是微任務,可能導致不一樣的調度結果。
 | 
						||
	: typeof setImmediate === 'function' ? setImmediate : function(job) {
 | 
						||
		setTimeout(job, 1);
 | 
						||
	},
 | 
						||
	// https://www.ecma-international.org/ecma-262/9.0/index.html#sec-properties-of-promise-instances
 | 
						||
	// const
 | 
						||
	PromiseState_pending = 0, PromiseState_fulfilled = 1, PromiseState_rejected = -1,
 | 
						||
	// const
 | 
						||
	KEY_STATE = '_state', KEY_VALUE = '_value', KEY_HANDLED = '_handled', KEY_REACTIONS = '_reactions',
 | 
						||
	// KEY_DEPEND_ON = KEY_VALUE; 因為沒有衝突,可重複利用 KEY_VALUE。
 | 
						||
	// 應對: @see TriggerPromiseReactions(promise)
 | 
						||
	KEY_DEPEND_ON = '_depend_on' && KEY_VALUE;
 | 
						||
 | 
						||
	// @private
 | 
						||
	// assert: won't throw
 | 
						||
	function PerformPromiseThen(promise, reaction, rejected, value) {
 | 
						||
		if (typeof reaction !== 'function') {
 | 
						||
			(rejected ? RejectPromise : FulfillPromise)(promise, value);
 | 
						||
			return;
 | 
						||
		}
 | 
						||
 | 
						||
		/**
 | 
						||
		 * <code>
 | 
						||
		(p=Promise.reject(2).then(function(){},function(v){return Promise.resolve(2)})).then(null,function(){});
 | 
						||
			Promise {<pending>}
 | 
						||
			→ Promise {<resolved>: 2}
 | 
						||
		</code>
 | 
						||
		 */
 | 
						||
		try {
 | 
						||
			value = reaction(value);
 | 
						||
			FulfillPromise(promise, value);
 | 
						||
		} catch (e) {
 | 
						||
			RejectPromise(promise, e);
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	// @private
 | 
						||
	// assert: won't throw
 | 
						||
	// https://www.ecma-international.org/ecma-262/9.0/index.html#sec-triggerpromisereactions
 | 
						||
	function TriggerPromiseReactions(promise) {
 | 
						||
		var value = promise[KEY_VALUE], rejected = promise[KEY_STATE] === PromiseState_rejected, reactions = promise[KEY_REACTIONS];
 | 
						||
		if (!reactions) {
 | 
						||
			// TriggerPromiseReactions() already executed
 | 
						||
			/** <code>p=new Promise(function(r,R){r(Promise.resolve(1));throw 2});console.log(p)</code> */
 | 
						||
			return;
 | 
						||
		}
 | 
						||
 | 
						||
		// free
 | 
						||
		delete promise[KEY_REACTIONS];
 | 
						||
		if (false && KEY_VALUE !== KEY_DEPEND_ON)
 | 
						||
			delete promise[KEY_DEPEND_ON];
 | 
						||
 | 
						||
		// assert: (`value` is not thenable || rejected) === true
 | 
						||
		EnqueueJob(function() {
 | 
						||
			if (rejected && !promise[KEY_HANDLED]) {
 | 
						||
				// assert: no onRejected reaction
 | 
						||
 | 
						||
				// Promise.reject(2).then(function(v){}).catch(function(v){})
 | 
						||
 | 
						||
				// HostPromiseRejectionTracker ( promise, operation )
 | 
						||
				// https://www.ecma-international.org/ecma-262/9.0/index.html#sec-host-promise-rejection-tracker
 | 
						||
				throw new Error('Uncaught (in promise): ' + value);
 | 
						||
			}
 | 
						||
 | 
						||
			library_namespace.debug('reactions of ' + promise + ': '
 | 
						||
					+ reactions.join(';'), 8, 'TriggerPromiseReactions');
 | 
						||
			// PromiseReactionJob ( reaction, argument )
 | 
						||
			// https://www.ecma-international.org/ecma-262/9.0/index.html#sec-promisereactionjob
 | 
						||
			reactions.forEach(function(reaction) {
 | 
						||
				if (Array.isArray(reaction)) {
 | 
						||
					// @see
 | 
						||
					// https://www.ecma-international.org/ecma-262/9.0/index.html#sec-promisereaction-records
 | 
						||
					// reaction = [ promise, onFulfilled, onRejected ]
 | 
						||
					PerformPromiseThen(reaction[0], reaction[rejected ? 2 : 1],
 | 
						||
							rejected, value);
 | 
						||
				} else {
 | 
						||
					// assert: IsPromise(reaction)
 | 
						||
					(rejected ? RejectPromise : FulfillPromise)
 | 
						||
							(reaction, value);
 | 
						||
				}
 | 
						||
			});
 | 
						||
		});
 | 
						||
	}
 | 
						||
 | 
						||
	// @private
 | 
						||
	// assert: won't throw
 | 
						||
	// Promise Resolve Functions, resolve `promise` with `result`.
 | 
						||
	// https://www.ecma-international.org/ecma-262/9.0/index.html#sec-promise-resolve-functions
 | 
						||
	// https://promisesaplus.com/#the-promise-resolution-procedure
 | 
						||
	function FulfillPromise(promise, result, no_enqueue) {
 | 
						||
		// test if promise is settled / alreadyResolved
 | 
						||
		if (promise[KEY_STATE] !== PromiseState_pending) {
 | 
						||
			// p=new Promise(function(r,R){setTimeout(function(){r(1);r(p)},1)})
 | 
						||
			return;
 | 
						||
		}
 | 
						||
 | 
						||
		function executing_job(job) {
 | 
						||
			if (no_enqueue)
 | 
						||
				job();
 | 
						||
			else
 | 
						||
				EnqueueJob(job);
 | 
						||
		}
 | 
						||
 | 
						||
		var then;
 | 
						||
		try {
 | 
						||
			if (promise === result) {
 | 
						||
				// selfResolutionError
 | 
						||
				/** <code>(p=new Promise(function(r,R){setTimeout(function(){r(p)},1)})).then(null,function(){})</code> */
 | 
						||
				// Chaining cycle detected for promise #<Promise>
 | 
						||
				throw new TypeError('A promise cannot be resolved with itself.');
 | 
						||
			}
 | 
						||
 | 
						||
			then = get_then_of_thenable(result);
 | 
						||
			if (!then) {
 | 
						||
				// p=new Promise(function(r,R){r({then:2})})
 | 
						||
				promise[KEY_STATE] = PromiseState_fulfilled;
 | 
						||
				promise[KEY_VALUE] = result;
 | 
						||
 | 
						||
				TriggerPromiseReactions(promise);
 | 
						||
 | 
						||
			} else {
 | 
						||
				if (IsPromise(result)) {
 | 
						||
					/**
 | 
						||
					 * <code>
 | 
						||
					// won't throw HostPromiseRejectionTracker:
 | 
						||
					(p=new Promise(function(r,R){r( Promise.reject(2) )})).then(null,function(){});console.log(p)
 | 
						||
					q=Promise.reject(2);(p=new Promise(function(r,R){r( q )})).then(null,function(){});console.log(p)
 | 
						||
					Promise.resolve().then(function(){return Promise.reject(3)}).then(null,function(){});
 | 
						||
					Promise.resolve().then(function(){return new Promise(function(r,R){R(3)})}).then(null,function(){});
 | 
						||
					</code>
 | 
						||
					 */
 | 
						||
					library_namespace.debug('handled: ' + result, 8,
 | 
						||
							'FulfillPromise');
 | 
						||
					result[KEY_HANDLED] = true;
 | 
						||
				}
 | 
						||
 | 
						||
				executing_job(function() {
 | 
						||
					/**
 | 
						||
					 * old runtime environment may not has
 | 
						||
					 * Function.prototype.bind(), so we use anonymous functions
 | 
						||
					 * instead of FulfillPromise.bind() to improve performance.
 | 
						||
					 */
 | 
						||
					function onFulfilled(value) {
 | 
						||
						if (!called) {
 | 
						||
							called = true;
 | 
						||
							FulfillPromise(promise, value, no_enqueue);
 | 
						||
						}
 | 
						||
					}
 | 
						||
					function onRejected(value) {
 | 
						||
						if (!called) {
 | 
						||
							called = true;
 | 
						||
							RejectPromise(promise, value);
 | 
						||
						}
 | 
						||
					}
 | 
						||
					var called;
 | 
						||
					try {
 | 
						||
						if (IsPromise(result)) {
 | 
						||
							/**
 | 
						||
							 * <code>
 | 
						||
							p=new Promise(function(r){setTimeout(function(){r(Promise.resolve(2))},1)})
 | 
						||
							</code>
 | 
						||
							 */
 | 
						||
							if (result[KEY_STATE] === PromiseState_pending) {
 | 
						||
								promise[KEY_DEPEND_ON] = result;
 | 
						||
								result[KEY_REACTIONS].push(promise);
 | 
						||
 | 
						||
							} else {
 | 
						||
								// copy result → promise
 | 
						||
								promise[KEY_STATE] = result[KEY_STATE];
 | 
						||
								promise[KEY_VALUE] = result[KEY_VALUE];
 | 
						||
 | 
						||
								// assert: promise[KEY_STATE] !==
 | 
						||
								// PromiseState_pending
 | 
						||
								TriggerPromiseReactions(promise);
 | 
						||
							}
 | 
						||
 | 
						||
						} else {
 | 
						||
							/**
 | 
						||
							 * <code>
 | 
						||
							 is_thenable(result)
 | 
						||
							 p=new Promise(function(r,R){r({then:function(){}})})
 | 
						||
							 t={};t.then=function(r){r(2)};p=new Promise(function(r,R){r(t)})
 | 
						||
							 t=function(){};t.then=function(r){r(2)};p=new Promise(function(r,R){r(t)})
 | 
						||
							 </code>
 | 
						||
							 */
 | 
						||
 | 
						||
							then.call(result, onFulfilled, onRejected);
 | 
						||
						}
 | 
						||
					} catch (e) {
 | 
						||
						onRejected(e);
 | 
						||
					}
 | 
						||
				});
 | 
						||
			}
 | 
						||
 | 
						||
		} catch (e) {
 | 
						||
			executing_job(function() {
 | 
						||
				RejectPromise(promise, e);
 | 
						||
			});
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	// @private
 | 
						||
	// assert: won't throw
 | 
						||
	// https://www.ecma-international.org/ecma-262/9.0/index.html#sec-promise-reject-functions
 | 
						||
	function RejectPromise(promise, reason) {
 | 
						||
		// test if promise is settled / alreadyResolved
 | 
						||
		if (promise[KEY_STATE] !== PromiseState_pending) {
 | 
						||
			return;
 | 
						||
		}
 | 
						||
 | 
						||
		// https://www.ecma-international.org/ecma-262/9.0/index.html#sec-rejectpromise
 | 
						||
 | 
						||
		// p=new Promise(function(r,R){setTimeout(function(){R(p)},1)})
 | 
						||
		// p=new Promise(function(r,R){R(function(){throw p})})
 | 
						||
		// p=new Promise(function(r,R){R({then:function(){throw p}})})
 | 
						||
		promise[KEY_STATE] = PromiseState_rejected;
 | 
						||
		promise[KEY_VALUE] = reason;
 | 
						||
		EnqueueJob(function() {
 | 
						||
			// (q=(p=Promise.reject(2)).then(3,4)).then(null,function(){});console.log(p);console.log(q);
 | 
						||
			TriggerPromiseReactions(promise);
 | 
						||
		});
 | 
						||
	}
 | 
						||
 | 
						||
	// @private
 | 
						||
	// is thenable object, thenable 物件
 | 
						||
	function get_then_of_thenable(value) {
 | 
						||
		// `value &&`: for null
 | 
						||
		if (value && (typeof value === 'function' || typeof value === 'object')) {
 | 
						||
			var then = value.then;
 | 
						||
			return typeof then === 'function' && then;
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	// @see is_thenable() @ base.js
 | 
						||
	// cf. Promise.isPromise()
 | 
						||
	function is_thenable(value) {
 | 
						||
		return !!get_then_of_thenable(value);
 | 
						||
		// old style
 | 
						||
		return value && typeof value.then === 'function';
 | 
						||
	}
 | 
						||
	// library_namespace.is_thenable = is_thenable;
 | 
						||
 | 
						||
	// https://www.ecma-international.org/ecma-262/9.0/index.html#sec-ispromise
 | 
						||
	// https://stackoverflow.com/questions/27746304/how-do-i-tell-if-an-object-is-a-promise
 | 
						||
	// test if is Promise 物件, Promise.isPromise(),
 | 
						||
	// `require('util').types.isPromise()`
 | 
						||
	function IsPromise(value) {
 | 
						||
		return value instanceof Promise;
 | 
						||
 | 
						||
		return Promise.resolve(value) == value;
 | 
						||
		return value && value.constructor === Promise;
 | 
						||
		return value
 | 
						||
				&& Object.prototype.toString.call(value) === "[object Promise]";
 | 
						||
 | 
						||
		// NG: only thenable
 | 
						||
		return is_thenable(value);
 | 
						||
 | 
						||
		// assert: Should NOT access value.then
 | 
						||
		// https://promisesaplus.com/#point-75
 | 
						||
 | 
						||
		// NG:
 | 
						||
		var then = get_then_of_thenable(value), constructor;
 | 
						||
		return then && typeof (constructor = value.constructor) === 'function'
 | 
						||
				&& typeof constructor.resolve === 'function'
 | 
						||
				&& typeof constructor.reject === 'function'
 | 
						||
				// ECMA-262, 9th edition 標準
 | 
						||
				// && typeof constructor.all === 'function'
 | 
						||
				// && typeof constructor.race === 'function'
 | 
						||
				&& then;
 | 
						||
	}
 | 
						||
 | 
						||
	// --------------------------------------------------------
 | 
						||
 | 
						||
	// https://www.ecma-international.org/ecma-262/9.0/index.html#sec-promise-executor
 | 
						||
	function Promise(executor) {
 | 
						||
		if (!(this instanceof Promise))
 | 
						||
			// calling a builtin Promise constructor without new is forbidden
 | 
						||
			throw new TypeError((typeof this) + ' is not a promise');
 | 
						||
 | 
						||
		if (typeof executor !== 'function')
 | 
						||
			// calling a builtin Promise constructor without new is forbidden
 | 
						||
			throw new TypeError('Promise resolver ' + executor
 | 
						||
					+ ' is not a function');
 | 
						||
 | 
						||
		// private:
 | 
						||
 | 
						||
		// [[PromiseState]]
 | 
						||
		this[KEY_STATE] = PromiseState_pending;
 | 
						||
 | 
						||
		// [[PromiseResult]]
 | 
						||
		// this[KEY_VALUE] = undefined;
 | 
						||
 | 
						||
		// [[PromiseIsHandled]]
 | 
						||
		// this[KEY_HANDLED] = false;
 | 
						||
 | 
						||
		// 因為本promise擱置而擱置的{Promise} or 函數。
 | 
						||
		// subscribers, deferreds
 | 
						||
		// [[PromiseFulfillReactions]] queue
 | 
						||
		// [[PromiseRejectReactions]] queue
 | 
						||
		this[KEY_REACTIONS] = [];
 | 
						||
 | 
						||
		var promise = this;
 | 
						||
		// forced to convert to thenable
 | 
						||
		FulfillPromise(promise, 'then' in executor ? executor : {
 | 
						||
			then : executor
 | 
						||
		}, /* no_enqueue */!('then' in executor));
 | 
						||
	}
 | 
						||
 | 
						||
	// https://www.ecma-international.org/ecma-262/9.0/index.html#sec-promise.prototype.then
 | 
						||
	// .then()會另外產生一個新的promise物件。 NewPromiseCapability()
 | 
						||
	function then(onFulfilled, onRejected) {
 | 
						||
		// https://www.ecma-international.org/ecma-262/9.0/index.html#sec-performpromisethen
 | 
						||
		if (typeof onFulfilled !== 'function')
 | 
						||
			onFulfilled = undefined;
 | 
						||
		if (typeof onRejected !== 'function')
 | 
						||
			onRejected = undefined;
 | 
						||
 | 
						||
		if (onRejected) {
 | 
						||
			var promise = this;
 | 
						||
			do {
 | 
						||
				/**
 | 
						||
				 * if promise[KEY_HANDLED] is already `true`, it means there are
 | 
						||
				 * more than one handler.<br />
 | 
						||
				 * e.g.,
 | 
						||
				 * <code>(q=Promise.reject(2)).then(null,function h1(){});q.then(null,function h2(){});</code>
 | 
						||
				 */
 | 
						||
				promise[KEY_HANDLED] = true;
 | 
						||
			} while (IsPromise(promise = promise[KEY_DEPEND_ON]));
 | 
						||
		}
 | 
						||
 | 
						||
		// 回傳另一個被擱置的 Promise 物件
 | 
						||
		// NewPromiseCapability(C)
 | 
						||
		var promise = new this.constructor(library_namespace.null_function);
 | 
						||
		promise[KEY_DEPEND_ON] = this;
 | 
						||
 | 
						||
		if (this[KEY_STATE] === PromiseState_pending) {
 | 
						||
			// test case: see "test of Promise.prototype.then()" above
 | 
						||
 | 
						||
			/**
 | 
						||
			 * 等待本promise解決了再處理。<br />
 | 
						||
			 * p=Promise.resolve(2);q=p.then();p!==q
 | 
						||
			 * 
 | 
						||
			 * @see https://www.ecma-international.org/ecma-262/9.0/index.html#sec-promisereaction-records
 | 
						||
			 */
 | 
						||
			this[KEY_REACTIONS].push(onFulfilled || onRejected
 | 
						||
			// reaction = [ promise, onFulfilled, onRejected ]
 | 
						||
			? [ promise, onFulfilled, onRejected ] : promise);
 | 
						||
 | 
						||
		} else {
 | 
						||
			// assert: `this` promise is settled, solved.
 | 
						||
 | 
						||
			/**
 | 
						||
			 * <code>
 | 
						||
			p=Promise.resolve().then(function(){})
 | 
						||
			p=Promise.resolve().then(function(a){return {a:1,b:2}})
 | 
						||
			p=Promise.resolve().then(function(a){return function(a){return a}})
 | 
						||
			q={b:2,then:5};p=Promise.resolve(2).then(a=>{return q});console.log(p);
 | 
						||
 | 
						||
			(p=Promise.reject(2)).catch(a=>0);(q=Promise.resolve(1).then(a=>p)).catch(a=>0);p!==q
 | 
						||
			q=new Promise(r=>setTimeout(r,500));p=Promise.resolve(2).then(a=>{return q});console.log(p);
 | 
						||
			p=new Promise((r,R)=>{setTimeout(function(){r(9)},8)});p.then(p);p.then(p);
 | 
						||
 | 
						||
			value is thenable object / function
 | 
						||
			q={then:r=>r(1)};p=Promise.resolve(2).then(a=>{return q})
 | 
						||
			q={b:2,then:r=>{console.log(this);r(this.b)}};p=Promise.resolve(2).then(a=>{return q});console.log(p);
 | 
						||
 | 
						||
			(q=(p=Promise.reject(2)).then(3,4)).then(null,function(){});console.log(p);console.log(q);
 | 
						||
			(q=Promise.reject(2).then(function(){})).then(function(){},function(y){})
 | 
						||
			</code>
 | 
						||
			 */
 | 
						||
			var rejected = this[KEY_STATE] === PromiseState_rejected, value = this[KEY_VALUE];
 | 
						||
			EnqueueJob(function() {
 | 
						||
				PerformPromiseThen(promise,
 | 
						||
						rejected ? onRejected : onFulfilled, rejected, value);
 | 
						||
			});
 | 
						||
		}
 | 
						||
 | 
						||
		return promise;
 | 
						||
	}
 | 
						||
 | 
						||
	// --------------------------------------------------------
 | 
						||
 | 
						||
	Promise.resolve = function resolve(result) {
 | 
						||
		if (IsPromise(result) && result[KEY_STATE] === PromiseState_fulfilled)
 | 
						||
			// p=Promise.resolve(1);q=Promise.resolve(p);console.log(p===q)
 | 
						||
			return result;
 | 
						||
 | 
						||
		// NewPromiseCapability(C)
 | 
						||
		// https://www.ecma-international.org/ecma-262/9.0/index.html#sec-newpromisecapability
 | 
						||
		var promise = new this(library_namespace.null_function);
 | 
						||
 | 
						||
		/**
 | 
						||
		 * <code>
 | 
						||
		(q=new Promise(function(r,R){setTimeout(function(){R(2)},80)}));(p=Promise.resolve(q)).then(function(x){console.log('*'+x)},function(y){console.log('_'+y)});console.log(p)
 | 
						||
		</code>
 | 
						||
		 */
 | 
						||
		if (IsPromise(result) && result[KEY_STATE] !== PromiseState_pending) {
 | 
						||
			// Promise.resolve() 基本上盡可能早點得到一個確定的 Promise 物件,並且不會改變傳入的引數。
 | 
						||
			// copy result → promise
 | 
						||
			promise[KEY_STATE] = result[KEY_STATE];
 | 
						||
			promise[KEY_VALUE] = result[KEY_VALUE];
 | 
						||
 | 
						||
			/**
 | 
						||
			 * <code>
 | 
						||
			// won't throw HostPromiseRejectionTracker:
 | 
						||
			(q=Promise.reject(2)).then(null,function(){});p=Promise.resolve(q);console.log(p)
 | 
						||
			(q=new Promise(function(r,R){R(2)})).then(null,function(){});p=Promise.resolve(q);console.log(p)
 | 
						||
			</code>
 | 
						||
			 */
 | 
						||
 | 
						||
			// will throw HostPromiseRejectionTracker:
 | 
						||
			// Promise.resolve(Promise.reject(2))
 | 
						||
			/**
 | 
						||
			 * <code>
 | 
						||
 | 
						||
			//	throw:
 | 
						||
			(q=Promise.reject(2));
 | 
						||
			//	no throw:
 | 
						||
			console.log(q);p=Promise.resolve(q);console.log(p)
 | 
						||
 | 
						||
			//	no throw:
 | 
						||
			(q=new Promise(function(r,R){R(2)})).then(null,function(){});
 | 
						||
			//	throw:
 | 
						||
			p=new Promise(function(r){r(q)});console.log(p)
 | 
						||
			
 | 
						||
			//	throw:
 | 
						||
			(q=Promise.reject(2));
 | 
						||
			//	throw:
 | 
						||
			console.log(q);p=new Promise(function(r,R){r(q)});console.log(p)
 | 
						||
			</code>
 | 
						||
			 */
 | 
						||
			promise[KEY_HANDLED] = true;
 | 
						||
		} else {
 | 
						||
			FulfillPromise(promise, result);
 | 
						||
		}
 | 
						||
 | 
						||
		return promise;
 | 
						||
	};
 | 
						||
 | 
						||
	Promise.reject = function reject(reason) {
 | 
						||
		// NewPromiseCapability(C)
 | 
						||
		var promise = new this(library_namespace.null_function);
 | 
						||
		RejectPromise(promise, reason);
 | 
						||
		return promise;
 | 
						||
	};
 | 
						||
 | 
						||
	// @private
 | 
						||
	function GetIterator(iterable) {
 | 
						||
		if (typeof iterable === 'string') {
 | 
						||
			// return Array.from(iterable);
 | 
						||
			return iterable.split('');
 | 
						||
		}
 | 
						||
 | 
						||
		if (!Array.isArray(iterable)) {
 | 
						||
			// e.g., {Number|Object|Function|Null|Undefined}
 | 
						||
			throw new TypeError('Argument of Promise.all: {'
 | 
						||
					+ (typeof iterable) + '} ' + iterable + ' is not iterable');
 | 
						||
		}
 | 
						||
 | 
						||
		return iterable;
 | 
						||
	}
 | 
						||
 | 
						||
	/**
 | 
						||
	 * <code>
 | 
						||
	var promise1 = Promise.resolve(3);
 | 
						||
	var promise2 = new Promise(function(resolve, reject) {
 | 
						||
		setTimeout(function() {
 | 
						||
			reject('foo');
 | 
						||
		}, 100);
 | 
						||
	});
 | 
						||
	var promises = [ promise1, promise2 ];
 | 
						||
 | 
						||
	Promise.allSettled(promises).then(function(results) {
 | 
						||
		results.forEach(function(result) {
 | 
						||
			console.log([ result.status, result.value, result.reason ].join(', '));
 | 
						||
		});
 | 
						||
	});
 | 
						||
 | 
						||
	Promise.all(promises).then(function(results) {
 | 
						||
		results.forEach(function(result) {
 | 
						||
			console.log(result.join(', '));
 | 
						||
		});
 | 
						||
	});
 | 
						||
	</code>
 | 
						||
	 */
 | 
						||
 | 
						||
	function for_all_promises(iterable, onFulfilled, onRejected) {
 | 
						||
		// -iterable.length: be sure `remaining` is negative before
 | 
						||
		// iterable.forEach() completed
 | 
						||
		var remaining = -iterable.length, result_list = [];
 | 
						||
 | 
						||
		function fill(value, index) {
 | 
						||
			// CeL.log([ remaining, index, value ].join(', '));
 | 
						||
			result_list[index] = value;
 | 
						||
			if (--remaining === 0) {
 | 
						||
				// CeL.log('result_list: ' + result_list);
 | 
						||
				onFulfilled(result_list);
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		GetIterator(iterable).forEach(function(item, index) {
 | 
						||
			var then = get_then_of_thenable(item);
 | 
						||
			if (!then) {
 | 
						||
				// Not a .then() object
 | 
						||
				result_list[index] = item;
 | 
						||
				return;
 | 
						||
			}
 | 
						||
 | 
						||
			function reject(reason) {
 | 
						||
				if (onRejected) {
 | 
						||
					// Promise.all()
 | 
						||
					onRejected(reason);
 | 
						||
				} else {
 | 
						||
					// Promise.allSettled()
 | 
						||
					fill({
 | 
						||
						status : "rejected",
 | 
						||
						reason : reason
 | 
						||
					}, index);
 | 
						||
				}
 | 
						||
			}
 | 
						||
 | 
						||
			try {
 | 
						||
				then.call(item, function(result) {
 | 
						||
					fill(onRejected ? result : {
 | 
						||
						status : "fulfilled",
 | 
						||
						value : result
 | 
						||
					}, index);
 | 
						||
				}, reject);
 | 
						||
			} catch (e) {
 | 
						||
				reject(e);
 | 
						||
			}
 | 
						||
 | 
						||
			remaining++;
 | 
						||
		});
 | 
						||
 | 
						||
		remaining += iterable.length;
 | 
						||
		if (remaining === 0) {
 | 
						||
			// No .then() object
 | 
						||
			onFulfilled(result_list);
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	// https://eyesofkids.gitbooks.io/javascript-start-es6-promise/content/contents/promise_all_n_race.html
 | 
						||
	// Promises/A+並沒有關於Promise.reject或Promise.resolve的定義,它們是ES6 Promise標準中的實作。
 | 
						||
	// 如果參數中 promise 有一個失敗(rejected),此實例回調失敗(reject)
 | 
						||
	Promise.all = function all(iterable) {
 | 
						||
		return new this(function(onFulfilled, onRejected) {
 | 
						||
			for_all_promises(iterable, onFulfilled, onRejected);
 | 
						||
		});
 | 
						||
	};
 | 
						||
 | 
						||
	// 拿到每個Promise的狀態 不管其是否處理成功
 | 
						||
	Promise.allSettled = function allSettled(iterable) {
 | 
						||
		return new this(function(onFulfilled/* , onRejected */) {
 | 
						||
			for_all_promises(iterable, onFulfilled);
 | 
						||
		});
 | 
						||
	};
 | 
						||
 | 
						||
	// 如果參數中某個promise解決或拒絕,返回的promise就會解決或拒絕。
 | 
						||
	Promise.race = function race(iterable) {
 | 
						||
		return new this(function(onFulfilled, onRejected) {
 | 
						||
			GetIterator(iterable)
 | 
						||
			// won't skip anyone. Using forEach for sparse array
 | 
						||
			.forEach(function(item) {
 | 
						||
				try {
 | 
						||
					var then = get_then_of_thenable(item);
 | 
						||
					if (then) {
 | 
						||
						then.call(item, onFulfilled, onRejected);
 | 
						||
					} else {
 | 
						||
						onFulfilled(item);
 | 
						||
					}
 | 
						||
				} catch (e) {
 | 
						||
					onRejected(e);
 | 
						||
				}
 | 
						||
			});
 | 
						||
		});
 | 
						||
	};
 | 
						||
 | 
						||
	// https://sung.codes/blog/2019/05/18/promise-race-vs-promise-any-and-promise-all-vs-promise-allsettled/
 | 
						||
	Promise.any = function any(iterable) {
 | 
						||
		return new this(function(onFulfilled, onRejected) {
 | 
						||
			var remaining = 0, waiting;
 | 
						||
			GetIterator(iterable)
 | 
						||
			// won't skip anyone. Using forEach for sparse array
 | 
						||
			.forEach(function(item) {
 | 
						||
				try {
 | 
						||
					var then = get_then_of_thenable(item);
 | 
						||
					if (then) {
 | 
						||
						remaining++;
 | 
						||
						then.call(item, function(item) {
 | 
						||
							remaining--;
 | 
						||
							some_fulfilled = true;
 | 
						||
							onFulfilled(item);
 | 
						||
						}, function(item) {
 | 
						||
							// TODO: check what to onRejected()
 | 
						||
							// TODO: Using AggregateError object
 | 
						||
							if (--remaining === 0 && waiting)
 | 
						||
								onRejected(item);
 | 
						||
						});
 | 
						||
					} else {
 | 
						||
						onFulfilled(item);
 | 
						||
					}
 | 
						||
				} catch (e) {
 | 
						||
					// ignores rejections. Ignore error
 | 
						||
				}
 | 
						||
			});
 | 
						||
			if (remaining > 0)
 | 
						||
				waiting = true;
 | 
						||
			else
 | 
						||
				onFulfilled();
 | 
						||
		});
 | 
						||
	};
 | 
						||
 | 
						||
	// --------------------------------------------------------
 | 
						||
 | 
						||
	// https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Promise/finally
 | 
						||
	function Promise_finally(onFinally) {
 | 
						||
		if (typeof onFinally !== 'function')
 | 
						||
			// p=Promise.resolve(2)['finally'](3).then(function(v){console.log(v)});console.log(p)
 | 
						||
			return this.then();
 | 
						||
 | 
						||
		if (false)
 | 
						||
			library_namespace.debug('creating onFinally step: ' + onFinally, 0,
 | 
						||
					'Promise_finally');
 | 
						||
		var _Promise = this.constructor || globalThis.Promise;
 | 
						||
		// onFinally won't get any arguments
 | 
						||
		return this.then(function(value) {
 | 
						||
			return _Promise.resolve(onFinally()).then(function() {
 | 
						||
				// inherit value
 | 
						||
				return value;
 | 
						||
			});
 | 
						||
		}, function(value) {
 | 
						||
			return _Promise.resolve(onFinally()).then(function() {
 | 
						||
				// return _Promise.reject(value);
 | 
						||
				throw value;
 | 
						||
			});
 | 
						||
		});
 | 
						||
	}
 | 
						||
 | 
						||
	// https://github.com/tc39/proposal-promise-try
 | 
						||
	// a syntactic sugar
 | 
						||
	function Promise_try(executor) {
 | 
						||
		return new this(function(resolve/* , reject */) {
 | 
						||
			return resolve(executor());
 | 
						||
		});
 | 
						||
	}
 | 
						||
 | 
						||
	// Promise.allSettled()
 | 
						||
	//
 | 
						||
	// Cannot catch Promise.allSettled(). Only use Using Promise.allSettled()
 | 
						||
	// when you do not care errors. Or using `Promise.all().then(, cacher)`
 | 
						||
	// before Promise.allSettled() to catch error.
 | 
						||
	//
 | 
						||
	// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled
 | 
						||
	// https://github.com/es-shims/Promise.allSettled/blob/master/implementation.js
 | 
						||
	function Promise_allSettled(iterable) {
 | 
						||
		function onFulfilled(result) {
 | 
						||
			return {
 | 
						||
				status : "fulfilled",
 | 
						||
				value : result
 | 
						||
			};
 | 
						||
		}
 | 
						||
 | 
						||
		function onRejected(reason) {
 | 
						||
			return {
 | 
						||
				status : "rejected",
 | 
						||
				reason : reason
 | 
						||
			};
 | 
						||
		}
 | 
						||
 | 
						||
		var _Promise = this;
 | 
						||
		iterable = Array.from(iterable, function(promise) {
 | 
						||
			if (!is_thenable(promise))
 | 
						||
				promise = _Promise.resolve(promise);
 | 
						||
			return promise.then(onFulfilled, onRejected);
 | 
						||
		});
 | 
						||
		return _Promise.all(iterable);
 | 
						||
	}
 | 
						||
 | 
						||
	// --------------------------------------------------------
 | 
						||
 | 
						||
	function Promise_toString() {
 | 
						||
		return 'Promise {<i style="color:#43e">['
 | 
						||
				+ (this[KEY_STATE] === PromiseState_pending ? 'pending'
 | 
						||
						: this[KEY_STATE] === PromiseState_rejected ? 'rejected'
 | 
						||
								: 'fulfilled') + ']</i>: ' + this[KEY_VALUE]
 | 
						||
				+ '}';
 | 
						||
	}
 | 
						||
 | 
						||
	// Do not set `Promise.prototype={...}`,
 | 
						||
	// so we can use `new this.constructor()`
 | 
						||
	Object.assign(Promise.prototype, {
 | 
						||
		// for debug only
 | 
						||
		// toString : Promise_toString,
 | 
						||
 | 
						||
		then : then,
 | 
						||
		// a syntactic sugar
 | 
						||
		// caught
 | 
						||
		'catch' : function Promise_catch(onRejected) {
 | 
						||
			return this.then(undefined, onRejected);
 | 
						||
		}
 | 
						||
	});
 | 
						||
 | 
						||
	set_method(globalThis, {
 | 
						||
		Promise : Promise
 | 
						||
	}, 'function');
 | 
						||
 | 
						||
	set_method(globalThis.Promise, {
 | 
						||
		'try' : Promise_try,
 | 
						||
		allSettled : Promise_allSettled
 | 
						||
	}, 'function');
 | 
						||
 | 
						||
	set_method(globalThis.Promise.prototype, {
 | 
						||
		// finale
 | 
						||
		'finally' : Promise_finally
 | 
						||
	}, 'function');
 | 
						||
 | 
						||
	// --------------------------------------------------------
 | 
						||
 | 
						||
	if (typeof Symbol === 'function') {
 | 
						||
		// Symbol.prototype.description
 | 
						||
		if (!('description' in Symbol.prototype)) {
 | 
						||
			// Object.getOwnPropertyDescriptor(Symbol.prototype, 'description');
 | 
						||
			Object.defineProperty(Symbol.prototype, 'description', {
 | 
						||
				enumerable : false,
 | 
						||
				configurable : true,
 | 
						||
				get : function() {
 | 
						||
					var matched = String(this).match(/^Symbol\(([\s\S]*)\)$/);
 | 
						||
					return matched[1];
 | 
						||
				}
 | 
						||
			});
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	// --------------------------------------------------------
 | 
						||
 | 
						||
	/**
 | 
						||
	 * defective polyfill for W3C URL API, URLSearchParams() @ CeL.application.net.
 | 
						||
	 * 
 | 
						||
	 * defective polyfill for W3C fetch API @ CeL.application.net.Ajax.
 | 
						||
	 */
 | 
						||
 | 
						||
	// ----------------------------------------------------------------------------------------------------------------------------------------------------------//
 | 
						||
	return (_// JSDT:_module_
 | 
						||
	);
 | 
						||
}
 |