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