mirror of
				https://scm.univ-tours.fr/22107988t/rappaurio-sae501_502.git
				synced 2025-11-04 12:05:21 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			2235 lines
		
	
	
		
			62 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			2235 lines
		
	
	
		
			62 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/**
 | 
						||
 * @name	CeL log function
 | 
						||
 * @fileoverview
 | 
						||
 * 本檔案包含了記錄用 functions。
 | 
						||
 * 
 | 
						||
 * @since	2009/11/17
 | 
						||
 * @see
 | 
						||
 * <a href="http://getfirebug.com/lite.html" accessdate="2010/1/1 14:54">Firebug Lite</a>,
 | 
						||
 * <a href="http://www.mozilla.org/projects/venkman/" accessdate="2010/1/1 16:43">Venkman JavaScript Debugger project page</a>
 | 
						||
 */
 | 
						||
 | 
						||
//	http://blogs.msdn.com/b/webdevtools/archive/2007/03/02/jscript-intellisense-in-orcas.aspx
 | 
						||
///	<reference path="../../ce.js" />
 | 
						||
/**
 | 
						||
 * <code>
 | 
						||
 TODO:
 | 
						||
 https://developers.google.com/web/tools/chrome-devtools/console/console-write#styling_console_output_with_css
 | 
						||
 console.log("%c", 將 CSS 樣式規則應用到第二個參數指定的輸出字符串)
 | 
						||
 | 
						||
 | 
						||
 emergency/urgent situation alert
 | 
						||
 會盡量以網頁上方/頂部黄色的導航條/警告條展示
 | 
						||
 「不再顯示」功能
 | 
						||
 .format()
 | 
						||
 將 div format 成 log panel。
 | 
						||
 分群, http://developer.yahoo.com/yui/examples/uploader/uploader-simple-button.html
 | 
						||
 </code>
 | 
						||
 */
 | 
						||
 | 
						||
/**
 | 
						||
 * <code>
 | 
						||
 to include:
 | 
						||
 include code_for_including
 | 
						||
 <div id="debug_panel"></div>
 | 
						||
 var SL = new Debug.log('debug_panel'), sl = function() { SL.log.apply(SL, arguments); }, error = function() { SL.error.apply(SL, arguments); }, warn = function() { SL.warn.apply(SL, arguments); };
 | 
						||
 | 
						||
 http://www.comsharp.com/GetKnowledge/zh-CN/TeamBlogTimothyPage_K742.aspx
 | 
						||
 | 
						||
 if possible, use Firebug Lite instead.
 | 
						||
 http://benalman.com/projects/javascript-debug-console-log/
 | 
						||
 </code>
 | 
						||
 */
 | 
						||
 | 
						||
'use strict';
 | 
						||
 | 
						||
// --------------------------------------------------------------------------------------------
 | 
						||
 | 
						||
// 不採用 if 陳述式,可以避免 Eclipse JSDoc 與 format 多縮排一層。
 | 
						||
typeof CeL === 'function' && CeL.run({
 | 
						||
	// module name
 | 
						||
	name : 'application.debug.log',
 | 
						||
 | 
						||
	// Object.is()
 | 
						||
	require : 'data.code.compatibility.',
 | 
						||
 | 
						||
	// 設定不匯出的子函式。
 | 
						||
	no_extend : 'this,do_log,extend',
 | 
						||
 | 
						||
	// 為了方便格式化程式碼,因此將 module 函式主體另外抽出。
 | 
						||
	code : module_code,
 | 
						||
 | 
						||
	finish : finish
 | 
						||
});
 | 
						||
 | 
						||
function module_code(library_namespace) {
 | 
						||
 | 
						||
	// WScript.Echo(this);
 | 
						||
 | 
						||
	var
 | 
						||
 | 
						||
	// class private -----------------------------------
 | 
						||
 | 
						||
	// class name, 需要用到這個都不是好方法。
 | 
						||
	// cn = 'Debug.log',
 | 
						||
 | 
						||
	/**
 | 
						||
	 * private storage pool
 | 
						||
	 * 
 | 
						||
	 * @ignore
 | 
						||
	 */
 | 
						||
	p = [],
 | 
						||
	//
 | 
						||
	has_performance_now,
 | 
						||
	//
 | 
						||
	log_data = function(message, options) {
 | 
						||
		// 由於 .set_method() 會用到 .debug(),
 | 
						||
		// 若在 log 的 core 中用上 .set_method() 會循環呼叫,造成 stack overflow。
 | 
						||
		// ** NG: library_namespace.set_method(this, options);
 | 
						||
		if (library_namespace.is_Object(options))
 | 
						||
			Object.assign(this, options);
 | 
						||
 | 
						||
		this.date = new Date();
 | 
						||
		if (has_performance_now)
 | 
						||
			this.time = performance.now();
 | 
						||
		this.message = message;
 | 
						||
		return this;
 | 
						||
	},
 | 
						||
 | 
						||
	/**
 | 
						||
	 * default write/show log function
 | 
						||
	 * 
 | 
						||
	 * @ignore
 | 
						||
	 * @param {string}id
 | 
						||
	 *            element id
 | 
						||
	 */
 | 
						||
	write_log = function(id) {
 | 
						||
		// console.log(id);
 | 
						||
		var o, m, c, _p = p[id], _t = _p.instance,
 | 
						||
		/**
 | 
						||
		 * buffer
 | 
						||
		 * 
 | 
						||
		 * @inner
 | 
						||
		 * @ignore
 | 
						||
		 */
 | 
						||
		b = _p.buf, B = _p.board, F = _p.do_function, level;
 | 
						||
 | 
						||
		if (_p.clean)
 | 
						||
			_t.clear(), _p.clean = 0;
 | 
						||
 | 
						||
		if (!B && !F)
 | 
						||
			return;
 | 
						||
 | 
						||
		while (b.length) {
 | 
						||
			// 預防 multi-threading 時重複顯示。
 | 
						||
			m = b.shift();
 | 
						||
 | 
						||
			if (F)
 | 
						||
				F(m);
 | 
						||
 | 
						||
			// IE8: 'constructor' 是 null 或不是一個物件
 | 
						||
			try {
 | 
						||
				c = m.constructor;
 | 
						||
				if (false)
 | 
						||
					alert((m.constructor === log_data) + '\n' + m.constructor
 | 
						||
							+ '\n' + m);
 | 
						||
			} catch (e) {
 | 
						||
			}
 | 
						||
			if (c === log_data) {
 | 
						||
				if (!isNaN(m.level) && m.level < library_namespace.set_debug())
 | 
						||
					continue;
 | 
						||
				c = m.level in _t.className_set ? m.level : 0;
 | 
						||
				o = m.add_class;
 | 
						||
				// 添加各種標記。
 | 
						||
				m = [ _t.message_prefix(c), _t.show_time(m.date, m.time),
 | 
						||
						m.message ];
 | 
						||
				c = _t.className_set[c];
 | 
						||
				if (o)
 | 
						||
					c += ' ' + o;
 | 
						||
 | 
						||
			} else {
 | 
						||
				// add default style set
 | 
						||
				if (c = _t.message_prefix('log'))
 | 
						||
					m = [ c, m ];
 | 
						||
				c = _t.className_set.log || 0;
 | 
						||
			}
 | 
						||
			_p.lbuf.push(m);
 | 
						||
 | 
						||
			if (B
 | 
						||
			// && typeof document === 'object'
 | 
						||
			) {
 | 
						||
				o = _p.instance.log_tag;
 | 
						||
				if (o) {
 | 
						||
					o = document.createElement(o);
 | 
						||
					if (c)
 | 
						||
						o.className = c;
 | 
						||
 | 
						||
					new_node(m, o);
 | 
						||
 | 
						||
				} else {
 | 
						||
					o = document.createTextNode(m);
 | 
						||
				}
 | 
						||
				// TODO: pause
 | 
						||
				B.appendChild(o);
 | 
						||
				while (B.childNodes.length > _p.max_logs) {
 | 
						||
					B.removeChild(B.firstChild);
 | 
						||
				}
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		if (false) {
 | 
						||
			if (_t.auto_hide)
 | 
						||
				B.style.display = B.innerHTML ? 'block' : 'none';
 | 
						||
		}
 | 
						||
		// TODO: 有時無法捲到最新。
 | 
						||
		if (B && _t.auto_scroll)
 | 
						||
			B.scrollTop = B.scrollHeight - B.clientHeight;
 | 
						||
	},
 | 
						||
 | 
						||
	/**
 | 
						||
	 * save log.
 | 
						||
	 * 
 | 
						||
	 * @ignore
 | 
						||
	 * @param m
 | 
						||
	 *            message
 | 
						||
	 * @param {string}
 | 
						||
	 *            id element id
 | 
						||
	 * @param force
 | 
						||
	 *            force to clean the message area
 | 
						||
	 */
 | 
						||
	do_save_log = function(m, id, force) {
 | 
						||
		// console.log(m);
 | 
						||
		var _p = p[id], _t = _p.instance,
 | 
						||
		// log file handler
 | 
						||
		f = _p.logF, s = _t.save_log;
 | 
						||
		if (!s || typeof s === 'function' && !s(m, l))
 | 
						||
			return;
 | 
						||
 | 
						||
		if (m)
 | 
						||
			_p.sbuf
 | 
						||
					.push(m = (_t.save_date && typeof gDate === 'function' ? _t.save_line_separator
 | 
						||
							+ gDate() + _t.save_line_separator
 | 
						||
							: '')
 | 
						||
							+ m);
 | 
						||
 | 
						||
		if (force || _t.flush || _p.sbufL > _t.save_limit)
 | 
						||
			try {
 | 
						||
				if (f
 | 
						||
						|| _t.log_file
 | 
						||
						&& (f = _p.logF = fso.OpenTextFile(_t.log_file,
 | 
						||
						/* ForAppending */8, /* create */true,
 | 
						||
								_t.log_encoding)))
 | 
						||
					f.Write(_p.sbuf.join(_t.save_line_separator)),
 | 
						||
							_p.sbuf = [], _p.sbufL = 0, _t.error_message = 0;
 | 
						||
			} catch (e) {
 | 
						||
				// error(e);
 | 
						||
				_t.error_message = e;
 | 
						||
			}
 | 
						||
		else if (m)
 | 
						||
			_p.sbufL += m.length;
 | 
						||
	},
 | 
						||
 | 
						||
	using_DOM_new_node = false,
 | 
						||
	// 使 log 能用到 new_node 的功能。
 | 
						||
	// @see function_placeholder() @ module.js
 | 
						||
	new_node = function(o, layer) {
 | 
						||
		// console.log(o);
 | 
						||
		if (library_namespace.is_Function(library_namespace.new_node)) {
 | 
						||
			// alert('開始利用 library 之 new_node。');
 | 
						||
			using_DOM_new_node = true;
 | 
						||
			return (new_node = library_namespace.new_node)(o, layer);
 | 
						||
		}
 | 
						||
 | 
						||
		var list = [];
 | 
						||
 | 
						||
		// workaround: 簡易版 new_node().
 | 
						||
		(function add(o) {
 | 
						||
			var node, tag, child;
 | 
						||
			if (Array.isArray(o))
 | 
						||
				for (node = 0; node < o.length; node++)
 | 
						||
					add(o[node]);
 | 
						||
			else if (library_namespace.is_Object(o)) {
 | 
						||
				if (o.$) {
 | 
						||
					tag = o.$;
 | 
						||
					list.push('<' + tag);
 | 
						||
					delete o.$;
 | 
						||
				}
 | 
						||
				for (node in o) {
 | 
						||
					if (tag)
 | 
						||
						list.push(' ' + node + '="'
 | 
						||
								+ ('' + o[node]).replace(/"/g, '"') + '"');
 | 
						||
					else {
 | 
						||
						tag = node;
 | 
						||
						list.push('<' + tag);
 | 
						||
						child = o[node] || null;
 | 
						||
					}
 | 
						||
				}
 | 
						||
				if (child === null)
 | 
						||
					list.push(' />');
 | 
						||
				else {
 | 
						||
					list.push('>');
 | 
						||
					add(child);
 | 
						||
					list.push('</' + tag + '>');
 | 
						||
				}
 | 
						||
			} else
 | 
						||
				list.push(o);
 | 
						||
		})(o);
 | 
						||
 | 
						||
		layer.innerHTML = list.join('');
 | 
						||
 | 
						||
		return using_DOM_new_node;
 | 
						||
	},
 | 
						||
 | 
						||
	show_date = function(date) {
 | 
						||
		var h = date.getHours(), m = date.getMinutes(), s = date.getSeconds(), ms = date
 | 
						||
				.getMilliseconds();
 | 
						||
		return (h || m || s ? (h || m ? (h ? h + ':' : '') + m + ':' : '') + s
 | 
						||
				: '')
 | 
						||
				+ '.' + (ms > 99 ? '' : ms > 9 ? '0' : '00') + ms;
 | 
						||
	},
 | 
						||
 | 
						||
	has_caller,
 | 
						||
 | 
						||
	// instance constructor ---------------------------
 | 
						||
	// (document object)
 | 
						||
	/**
 | 
						||
	 * <code>
 | 
						||
 | 
						||
	_ = this
 | 
						||
 | 
						||
 | 
						||
	TODO:
 | 
						||
	set class in each input
 | 
						||
	input array
 | 
						||
	show file path & directory functional	可從 FSO operation.hta 移植。
 | 
						||
 | 
						||
	count
 | 
						||
	c.f.: GLog
 | 
						||
 | 
						||
	dependency:
 | 
						||
 | 
						||
	</code>
 | 
						||
	 */
 | 
						||
	/**
 | 
						||
	 * initial a log tool's instance/object
 | 
						||
	 * 
 | 
						||
	 * @class log function
 | 
						||
	 * @_see usage: <a href="#.extend">_module_.extend</a>
 | 
						||
	 * @since 2008/8/20 23:9:48
 | 
						||
	 * @requires gDate(),line_separator,fso
 | 
						||
	 * 
 | 
						||
	 * @constructor
 | 
						||
	 * @_name _module_
 | 
						||
	 * @param {String|object
 | 
						||
	 *            HTMLElement} obj log target: message area element or id
 | 
						||
	 * @param {Object}
 | 
						||
	 *            [className_set] class name set
 | 
						||
	 */
 | 
						||
	_// JSDT:_tmp;_module_
 | 
						||
	= function(obj, className_set) {
 | 
						||
		// Initial instance object. You can set it yourself.
 | 
						||
		/**
 | 
						||
		 * log 時 warning/error message 之 className
 | 
						||
		 * 
 | 
						||
		 * @_name _module_.prototype.className_set
 | 
						||
		 */
 | 
						||
		this.className_set = className_set || {
 | 
						||
			/**
 | 
						||
			 * @_description 當呼叫 {@link _module_.prototype.log} 時使用的 className,
 | 
						||
			 *               DEFAULT className.
 | 
						||
			 * @_name _module_.prototype.className_set.log
 | 
						||
			 */
 | 
						||
			log : 'debug_log',
 | 
						||
			/**
 | 
						||
			 * @_description 當呼叫 {@link _module_.prototype.warn} 時使用的 className
 | 
						||
			 * @_name _module_.prototype.className_set.warn
 | 
						||
			 */
 | 
						||
			warn : 'debug_warn',
 | 
						||
			/**
 | 
						||
			 * @_description 當呼叫 {@link _module_.prototype.error} 時使用的 className
 | 
						||
			 * @_name _module_.prototype.className_set.error
 | 
						||
			 */
 | 
						||
			error : 'debug_error',
 | 
						||
			/**
 | 
						||
			 * @_description 當顯示時間時使用的 className
 | 
						||
			 * @_name _module_.prototype.className_set.time
 | 
						||
			 */
 | 
						||
			time : 'debug_time',
 | 
						||
			/**
 | 
						||
			 * @_description 當呼叫 {@link _module_.prototype.set_board} 時設定 log
 | 
						||
			 *               panel 使用的 className
 | 
						||
			 * @_name _module_.prototype.className_set.panel
 | 
						||
			 */
 | 
						||
			panel : 'debug_panel'
 | 
						||
		};
 | 
						||
		this.class_hide = {};
 | 
						||
 | 
						||
		var prefix = {
 | 
						||
			/**
 | 
						||
			 * @_description 當呼叫 {@link _module_.prototype.log} 時使用的 prefix,
 | 
						||
			 *               DEFAULT prefix.
 | 
						||
			 * @_name _module_.prototype.message_prefix.log
 | 
						||
			 */
 | 
						||
			log : '',
 | 
						||
			/**
 | 
						||
			 * @_description 當呼叫 {@link _module_.prototype.warn} 時使用的 prefix
 | 
						||
			 * @_name _module_.prototype.message_prefix.warn
 | 
						||
			 */
 | 
						||
			warn : '',
 | 
						||
			/**
 | 
						||
			 * @_description 表示當呼叫 {@link _module_.prototype.error}, 是錯誤 error
 | 
						||
			 *               message 時使用的 prefix
 | 
						||
			 * @_name _module_.prototype.message_prefix.error
 | 
						||
			 */
 | 
						||
			error : '<em>!! Error !!</em> '
 | 
						||
		};
 | 
						||
		/**
 | 
						||
		 * log 時 warning/error message 之 prefix。
 | 
						||
		 * 
 | 
						||
		 * @_name _module_.prototype.message_prefix
 | 
						||
		 */
 | 
						||
		this.message_prefix = function(level) {
 | 
						||
			return level in prefix ? prefix[level] : '';
 | 
						||
		};
 | 
						||
 | 
						||
		this.id = p.length;
 | 
						||
		p.push({
 | 
						||
			instance : this,
 | 
						||
			/** write buffer */
 | 
						||
			buf : [],
 | 
						||
			/** save buffer when we need to save the messages */
 | 
						||
			sbuf : [],
 | 
						||
			/** length of save buffer */
 | 
						||
			sbufL : 0,
 | 
						||
			/** now logged buffer */
 | 
						||
			lbuf : []
 | 
						||
		});
 | 
						||
		this.set_board(obj);
 | 
						||
	};
 | 
						||
 | 
						||
	try {
 | 
						||
		has_performance_now = performance.now() > 0;
 | 
						||
	} catch (e) {
 | 
						||
	}
 | 
						||
 | 
						||
	try {
 | 
						||
		has_caller = function(a) {
 | 
						||
			'use strict';
 | 
						||
			return arguments.callee.caller !== undefined;
 | 
						||
		};
 | 
						||
		has_caller = (function() {
 | 
						||
			return has_caller();
 | 
						||
		})();
 | 
						||
	} catch (e) {
 | 
						||
		has_caller = false;
 | 
						||
	}
 | 
						||
 | 
						||
	// class public interface ---------------------------
 | 
						||
 | 
						||
	_// JSDT:_module_
 | 
						||
	.
 | 
						||
	/**
 | 
						||
	 * do the log action
 | 
						||
	 * 
 | 
						||
	 * @_memberOf _module_
 | 
						||
	 * @private
 | 
						||
	 */
 | 
						||
	do_log = function(id) {
 | 
						||
		/**
 | 
						||
		 * <code>
 | 
						||
 | 
						||
		這段應該只在 module namespace 重複定義時才會發生
 | 
						||
 | 
						||
		var I = p[id];
 | 
						||
		if (!I) {
 | 
						||
			alert('.do_log: not exist: [' + id + ']');
 | 
						||
			return;
 | 
						||
		}
 | 
						||
		I = I.instance;
 | 
						||
		</code>
 | 
						||
		 */
 | 
						||
 | 
						||
		var I = p[id].instance;
 | 
						||
		if (I.do_log)
 | 
						||
			I.do_log();
 | 
						||
	};
 | 
						||
 | 
						||
	_// JSDT:_module_
 | 
						||
	.
 | 
						||
	/**
 | 
						||
	 * 對各種不同 error object 作應對,獲得可理解的 error message。
 | 
						||
	 * 
 | 
						||
	 * @param e
 | 
						||
	 *            error object
 | 
						||
	 * @param line_separator
 | 
						||
	 *            line separator
 | 
						||
	 * @param caller
 | 
						||
	 *            function caller
 | 
						||
	 * @_memberOf _module_
 | 
						||
	 * @see http://msdn.microsoft.com/en-us/library/ms976144.aspx The facility
 | 
						||
	 *      code establishes who originated the error. For example, all internal
 | 
						||
	 *      script engine errors generated by the JScript engine have a facility
 | 
						||
	 *      code of "A".
 | 
						||
	 *      http://msdn.microsoft.com/en-us/library/ms690088(VS.85).aspx
 | 
						||
	 * @see http://msdn.microsoft.com/en-us/library/t9zk6eay.aspx
 | 
						||
	 *      http://msdn.microsoft.com/en-us/library/microsoft.jscript.errorobject.aspx
 | 
						||
	 *      Specifies the name of the type of the error. Possible values include
 | 
						||
	 *      Error, EvalError, RangeError, ReferenceError, SyntaxError,
 | 
						||
	 *      TypeError, and URIError.
 | 
						||
	 */
 | 
						||
	get_error_message = function get_error_message(e, line_separator, caller) {
 | 
						||
		if (!line_separator)
 | 
						||
			line_separator = _.prototype.save_line_separator;
 | 
						||
 | 
						||
		if (!caller || typeof caller !== 'string') {
 | 
						||
			if (typeof caller !== 'function' && has_caller)
 | 
						||
				caller = get_error_message.caller;
 | 
						||
 | 
						||
			if (caller === null)
 | 
						||
				caller = 'from the top level';
 | 
						||
			else if (typeof caller === 'function')
 | 
						||
				caller = '@'
 | 
						||
						+ (library_namespace.get_function_name(caller) || caller);
 | 
						||
			else
 | 
						||
				caller = '@' + library_namespace.Class;
 | 
						||
		}
 | 
						||
 | 
						||
		// from popErr()
 | 
						||
		// type
 | 
						||
		var T = library_namespace.is_type(e),
 | 
						||
		// message
 | 
						||
		m = T === 'Error' ? 'Error '
 | 
						||
				+ caller
 | 
						||
				+ ': '
 | 
						||
				/**
 | 
						||
				 * <code>
 | 
						||
					http://msdn.microsoft.com/en-us/library/cc231198(PROT.10).aspx
 | 
						||
					<a href="http://msdn.microsoft.com/en-us/library/ms819773.aspx">Winerror.h</a>: error code definitions for the Win32 API functions
 | 
						||
					(e.number & 0xFFFF): See 錯誤代碼 /錯誤提示碼 <a href="http://msdn.microsoft.com/en-us/library/ms681381%28VS.85%29.aspx">System Error Codes</a>
 | 
						||
					http://social.msdn.microsoft.com/Search/zh-TW/?Query=%22System+Error+Codes%22+740&AddEnglish=1
 | 
						||
					http://msdn.microsoft.com/en-us/library/aa394559(VS.85).aspx
 | 
						||
					net helpmsg (e.number & 0xFFFF)
 | 
						||
				</code>
 | 
						||
				 */
 | 
						||
				+ (e.number & 0xFFFF)
 | 
						||
				+ (e.name ? ' [' + e.name + '] ' : ' ')
 | 
						||
				+ '(facility code '
 | 
						||
				+ (e.number >> 16 & 0x1FFF)
 | 
						||
				+ '): '
 | 
						||
				+ line_separator
 | 
						||
				+ (e.message || '').replace(/\r?\n/g, '<br />')
 | 
						||
				// .message 為主,.description 是舊的。
 | 
						||
				+ (!e.description || e.description === e.message ? ''
 | 
						||
						: line_separator
 | 
						||
								+ line_separator
 | 
						||
								+ ('' + e.description).replace(/\r?\n/g,
 | 
						||
										'<br />'))
 | 
						||
 | 
						||
		: T === 'DOMException' ?
 | 
						||
		// http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-17189187
 | 
						||
		'[' + T + '] ' + e.code + ': ' + e.message
 | 
						||
		//
 | 
						||
		: !e || T === 'string' ? e
 | 
						||
		//
 | 
						||
		: '[' + T + '] ' + (e.message || e);
 | 
						||
 | 
						||
		if (library_namespace.is_debug(2) && typeof e === 'object' && e)
 | 
						||
			for (T in e)
 | 
						||
				try {
 | 
						||
					// Firefox has (new Error).stack
 | 
						||
					// http://eriwen.com/javascript/js-stack-trace/
 | 
						||
					m += '<br /> <span class="debug_debug">'
 | 
						||
							+ T
 | 
						||
							+ '</span>: '
 | 
						||
							+ (typeof e[T] === 'string' && T === 'stack' ? e[T]
 | 
						||
									.replace(/[\r\n]+$/, '')
 | 
						||
									.replace(/(@)([a-z\-]+:\/\/.+)(:)(\d+)$/gm,
 | 
						||
											'$1<a href="view-source:$2#$4" target="_blank">$2</a>$3$4')
 | 
						||
									.replace(/\n/g, '<br />- ')
 | 
						||
									: typeof e[T] === 'string'
 | 
						||
											&& T === 'fileName' ? '<a href="view-source:'
 | 
						||
											+ e[T]
 | 
						||
											+ '" target="_blank">'
 | 
						||
											+ e[T] + '</a>'
 | 
						||
											: e[T]);
 | 
						||
				} catch (e) {
 | 
						||
					// TODO: handle exception
 | 
						||
				}
 | 
						||
 | 
						||
		// m += ' (' + arguments.callee.caller + ')';
 | 
						||
		return m;
 | 
						||
	};
 | 
						||
 | 
						||
	_// JSDT:_module_
 | 
						||
	.
 | 
						||
	/**
 | 
						||
	 * get node description
 | 
						||
	 * 
 | 
						||
	 * @param node
 | 
						||
	 *            HTML node
 | 
						||
	 * @_memberOf _module_
 | 
						||
	 */
 | 
						||
	node_description = function(node, flag) {
 | 
						||
		// console.log(node);
 | 
						||
		if (typeof node === 'string')
 | 
						||
			node = document.getElementById(node);
 | 
						||
		if (!node)
 | 
						||
			return;
 | 
						||
 | 
						||
		var description = '';
 | 
						||
 | 
						||
		if (node.id)
 | 
						||
			description += '#' + node.id;
 | 
						||
 | 
						||
		if (node.className)
 | 
						||
			description += '.' + node.className;
 | 
						||
 | 
						||
		if (node.tagName)
 | 
						||
			description = '<' + node.tagName + description + '>';
 | 
						||
 | 
						||
		if (!description && node.innerHTML) {
 | 
						||
			description = node.innerHTML;
 | 
						||
			if (description.length > 40)
 | 
						||
				description = description.slice(0, 40);
 | 
						||
			description = description.replace(/</g, '<');
 | 
						||
		}
 | 
						||
 | 
						||
		// TODO: 對 Range object 之類的處理
 | 
						||
		// http://help.dottoro.com/ljxsqnoi.php
 | 
						||
		return description || '(null description node: '
 | 
						||
				+ library_namespace.is_type(node) + ')';
 | 
						||
	};
 | 
						||
 | 
						||
	_// JSDT:_module_
 | 
						||
	.default_log_target = function(message_data) {
 | 
						||
		var level = typeof message_data === 'object' && message_data.level, logger;
 | 
						||
		if (logger = level && library_namespace.debug_console[level])
 | 
						||
			logger(message_data.message);
 | 
						||
		else
 | 
						||
			console.log(level ? '[' + level + '] ' + message_data.message
 | 
						||
					: message_data);
 | 
						||
	};
 | 
						||
 | 
						||
	_// JSDT:_module_
 | 
						||
	.
 | 
						||
	/**
 | 
						||
	 * get new extend instance
 | 
						||
	 * 
 | 
						||
	 * @param {String|object
 | 
						||
	 *            HTMLElement} [obj] message area element or id
 | 
						||
	 * @return {Array} [ instance of this module, log function, warning
 | 
						||
	 *         function, error function ]
 | 
						||
	 * @_example<code>
 | 
						||
 | 
						||
	// status logger
 | 
						||
	var SL = new _module_('log'), sl = SL[1], warn = SL[2], error = SL[3];
 | 
						||
	sl(msg);
 | 
						||
	sl(msg, clear);
 | 
						||
 | 
						||
	// general log
 | 
						||
	function_set = new _module_.extend('panel', {});
 | 
						||
	// 1.
 | 
						||
	function_set = new CeL.code.log.extend('panel', {});
 | 
						||
	logger = function_set[1];
 | 
						||
	// 2.
 | 
						||
	log_only = (new CeL.code.log.extend('panel', {}))[1];
 | 
						||
 | 
						||
	</code>
 | 
						||
	 * @_memberOf _module_
 | 
						||
	 * @since 2009/8/24 20:15:31
 | 
						||
	 */
 | 
						||
	extend = function(obj, className_set) {
 | 
						||
		if (false) {
 | 
						||
			CeL.Log = new CeL.code.log(
 | 
						||
					function(m) {
 | 
						||
						var F = typeof JSalert === 'function' ? JSalert
 | 
						||
								: typeof alert === 'function' ? alert
 | 
						||
										: WScript.Echo;
 | 
						||
						F(typeof m === 'object' ? '[' + m.level + '] '
 | 
						||
								+ m.message : m);
 | 
						||
					});
 | 
						||
		}
 | 
						||
 | 
						||
		/**
 | 
						||
		 * new instance
 | 
						||
		 * 
 | 
						||
		 * @_type _module_
 | 
						||
		 * @inner
 | 
						||
		 * @ignore
 | 
						||
		 */
 | 
						||
		var log_controller = new _// JSDT:_module_
 | 
						||
		(obj || _.default_log_target, className_set);
 | 
						||
 | 
						||
		// TODO: do not use arguments
 | 
						||
		return [ log_controller, function() {
 | 
						||
			// console.log(arguments);
 | 
						||
			log_controller.log.apply(log_controller, arguments);
 | 
						||
		}, function() {
 | 
						||
			log_controller.warn.apply(log_controller, arguments);
 | 
						||
		}, function() {
 | 
						||
			log_controller.error.apply(log_controller, arguments);
 | 
						||
		} ];
 | 
						||
 | 
						||
	};
 | 
						||
 | 
						||
	/**
 | 
						||
	 * <code>
 | 
						||
	_.option_open=function(p){
 | 
						||
 | 
						||
	};
 | 
						||
 | 
						||
	_.option_file=function(p){
 | 
						||
	};
 | 
						||
 | 
						||
	_.option_folder=function(p){
 | 
						||
	};
 | 
						||
	</code>
 | 
						||
	 */
 | 
						||
 | 
						||
	// class constructor ---------------------------
 | 
						||
	_// JSDT:_module_
 | 
						||
	.prototype = {
 | 
						||
 | 
						||
		// instance public interface -------------------
 | 
						||
 | 
						||
		/**
 | 
						||
		 * 當執行寫檔案或任何錯誤發生時之錯誤訊息。<br />
 | 
						||
		 * while error occurred.. should read only
 | 
						||
		 * 
 | 
						||
		 * @_name _module_.prototype.error_message
 | 
						||
		 */
 | 
						||
		error_message : '',
 | 
						||
 | 
						||
		/**
 | 
						||
		 * 超過這長度才 save。<=0 表示 autoflash,非數字則不紀錄。
 | 
						||
		 * 
 | 
						||
		 * @_name _module_.prototype.save_limit
 | 
						||
		 * @type Number
 | 
						||
		 */
 | 
						||
		save_limit : 4000,
 | 
						||
 | 
						||
		/**
 | 
						||
		 * 在 log 結束時執行,相當於 VB 中 DoEvent() 或 。
 | 
						||
		 * 
 | 
						||
		 * @_name _module_.prototype.do_event
 | 
						||
		 */
 | 
						||
		do_event : library_namespace.DoNoting || null,
 | 
						||
 | 
						||
		/**
 | 
						||
		 * log 時使用之 tagName, 可用 div / span 等。若不設定會用 document.createTextNode
 | 
						||
		 * 
 | 
						||
		 * @_name _module_.prototype.log_tag
 | 
						||
		 */
 | 
						||
		log_tag : 'div',
 | 
						||
 | 
						||
		/**
 | 
						||
		 * boolean or function(message, log level) return save or not
 | 
						||
		 * 
 | 
						||
		 * @_name _module_.prototype.save_log
 | 
						||
		 * @type Boolean
 | 
						||
		 */
 | 
						||
		save_log : false,
 | 
						||
		/**
 | 
						||
		 * save log to this file path
 | 
						||
		 * 
 | 
						||
		 * @_name _module_.prototype.log_file
 | 
						||
		 * @type Boolean
 | 
						||
		 */
 | 
						||
		log_file : false,
 | 
						||
		/**
 | 
						||
		 * auto save log. 若未設定,記得在 onunload 時 .save()
 | 
						||
		 * 
 | 
						||
		 * @_name _module_.prototype.flush
 | 
						||
		 * @type Boolean
 | 
						||
		 */
 | 
						||
		flush : false,
 | 
						||
		/**
 | 
						||
		 * 在 save log 時 add date
 | 
						||
		 * 
 | 
						||
		 * @_name _module_.prototype.save_date
 | 
						||
		 * @type Boolean
 | 
						||
		 */
 | 
						||
		save_date : true,
 | 
						||
		/**
 | 
						||
		 * 在 save log 時的換行
 | 
						||
		 * 
 | 
						||
		 * @_name _module_.prototype.save_line_separator
 | 
						||
		 * @type string
 | 
						||
		 */
 | 
						||
		save_line_separator : library_namespace.env.line_separator || '\r\n',
 | 
						||
		/**
 | 
						||
		 * 在 save log 時的 encoding
 | 
						||
		 * 
 | 
						||
		 * @_name _module_.prototype.log_encoding
 | 
						||
		 */
 | 
						||
		log_encoding : -1,// -1: TristateTrue
 | 
						||
 | 
						||
		/**
 | 
						||
		 * 自動捲動
 | 
						||
		 * 
 | 
						||
		 * @_name _module_.prototype.auto_scroll
 | 
						||
		 * @type Boolean
 | 
						||
		 */
 | 
						||
		auto_scroll : true,
 | 
						||
		/**
 | 
						||
		 * 沒有內容時自動隱藏
 | 
						||
		 * 
 | 
						||
		 * @deprecated TODO
 | 
						||
		 * @_name _module_.prototype.auto_hide
 | 
						||
		 * @type Boolean
 | 
						||
		 */
 | 
						||
		auto_hide : false,
 | 
						||
 | 
						||
		/**
 | 
						||
		 * 等待多久才顯示 log。若為 0 則直接顯示。<br />
 | 
						||
		 * e.g., 即時顯示,不延遲顯示: CeL.Log.interval = 0;<br />
 | 
						||
		 * (WScript 沒有 setTimeout)
 | 
						||
		 * 
 | 
						||
		 * @_name _module_.prototype.interval
 | 
						||
		 */
 | 
						||
		interval : typeof setTimeout === 'undefined' ? 0 : 1,
 | 
						||
 | 
						||
		/**
 | 
						||
		 * log function (no delay)
 | 
						||
		 * 
 | 
						||
		 * @_name _module_.prototype.do_log
 | 
						||
		 */
 | 
						||
		do_log : function(level) {
 | 
						||
			if (false)
 | 
						||
				if (p[this.id].th)
 | 
						||
					clearTimeout(p[this.id].th);
 | 
						||
 | 
						||
			// reset timeout handler
 | 
						||
			p[this.id].th = 0;
 | 
						||
 | 
						||
			// TODO: 提升效率.
 | 
						||
			if ('controller' in this)
 | 
						||
				this.set_controller();
 | 
						||
 | 
						||
			write_log(this.id);
 | 
						||
		},
 | 
						||
 | 
						||
		/**
 | 
						||
		 * class instance 預設作 log 之 function
 | 
						||
		 * 
 | 
						||
		 * @param {String}
 | 
						||
		 *            message message
 | 
						||
		 * @param {Boolean}clean
 | 
						||
		 *            clean message area
 | 
						||
		 * @param {Object}options
 | 
						||
		 *            選擇性項目. { level : log level, 記錄複雜度. }
 | 
						||
		 * @return
 | 
						||
		 * @_name _module_.prototype.log
 | 
						||
		 */
 | 
						||
		log : function(message, clean, options) {
 | 
						||
			// console.log(message);
 | 
						||
			var t = this, _p = p[t.id], level, force_save;
 | 
						||
 | 
						||
			if (library_namespace.is_Object(options)) {
 | 
						||
				level = options.level;
 | 
						||
				force_save = options.save;
 | 
						||
			} else if (options) {
 | 
						||
				force_save = level = options;
 | 
						||
				(options = {}).level = level;
 | 
						||
			}
 | 
						||
 | 
						||
			/**
 | 
						||
			 * <code>
 | 
						||
					var message_head = (arguments.callee.caller + '')
 | 
						||
							.match(/function\do_save_log([^\(]+)/);
 | 
						||
					if (message_head)
 | 
						||
						message_head = message_head[1] + ' ';
 | 
						||
			</code>
 | 
						||
			 */
 | 
						||
			do_save_log(message, t.id, force_save);
 | 
						||
 | 
						||
			// window.status = message;
 | 
						||
			if (options) {
 | 
						||
				message = new log_data(message, options);
 | 
						||
			}
 | 
						||
 | 
						||
			if (clean) {
 | 
						||
				// clean log next time
 | 
						||
				_p.clean = 1, _p.buf = [ message ];
 | 
						||
			} else {
 | 
						||
				_p.buf.push(message);
 | 
						||
			}
 | 
						||
 | 
						||
			if (!(t.interval > 0))
 | 
						||
				t.do_log();
 | 
						||
			else if (!_p.th)
 | 
						||
				// no window.setTimeout @ node.js
 | 
						||
				if (typeof setTimeout === 'undefined')
 | 
						||
					t.interval = 0, t.do_log();
 | 
						||
				else
 | 
						||
					// _p.th = setTimeout(cn + '.do_log(' + t.id + ');',
 | 
						||
					// t.interval);
 | 
						||
					_p.th = setTimeout(function() {
 | 
						||
						_.do_log(t.id);
 | 
						||
					}, t.interval);
 | 
						||
 | 
						||
			if (t.do_event)
 | 
						||
				t.do_event();
 | 
						||
		},
 | 
						||
 | 
						||
		/*
 | 
						||
		 * TODO: other methods: INFO,DEBUG,WARNING,ERROR,FATAL,UNKNOWN
 | 
						||
		 */
 | 
						||
 | 
						||
		/**
 | 
						||
		 * save message
 | 
						||
		 * 
 | 
						||
		 * @_name _module_.prototype.save
 | 
						||
		 */
 | 
						||
		save : function() {
 | 
						||
			do_save_log('', this.id, 1/* force */);
 | 
						||
		},
 | 
						||
 | 
						||
		/**
 | 
						||
		 * <code>
 | 
						||
 | 
						||
		 ** important ** 這邊不能作 object 之 initialization,否則因為 object 只會 copy reference,因此 new 時東西會一樣。initialization 得在 _() 中作!
 | 
						||
 | 
						||
		 </code>
 | 
						||
		 */
 | 
						||
		// className_set : {},
 | 
						||
		/**
 | 
						||
		 * log a warning / caution / alert / 警告.
 | 
						||
		 * 
 | 
						||
		 * @_name _module_.prototype.warn
 | 
						||
		 */
 | 
						||
		warn : function(m, clean) {
 | 
						||
			this.log(m, clean, 'warn');
 | 
						||
		},
 | 
						||
 | 
						||
		/**
 | 
						||
		 * deal with error message
 | 
						||
		 * 
 | 
						||
		 * @_name _module_.prototype.error
 | 
						||
		 */
 | 
						||
		error : function error(e, clean) {
 | 
						||
			var caller = '';
 | 
						||
			if (has_caller) {
 | 
						||
				caller = '' + error.caller;
 | 
						||
				if (caller.indexOf('.error.apply(') !== -1)
 | 
						||
					// ** 判斷 call from _.extend. TODO: 應該避免!
 | 
						||
					caller = caller.caller;
 | 
						||
			}
 | 
						||
 | 
						||
			this.log(Array.isArray(e) || library_namespace.is_Object(e) ? e : _
 | 
						||
					.get_error_message(e, this.save_line_separator, caller),
 | 
						||
					clean, 'error');
 | 
						||
		},
 | 
						||
 | 
						||
		timezone_offset : /* msPerMinute */60000 * (new Date)
 | 
						||
				.getTimezoneOffset(),
 | 
						||
 | 
						||
		/**
 | 
						||
		 * 在 log 中依照格式顯示時間。
 | 
						||
		 * 
 | 
						||
		 * @param {Date}date
 | 
						||
		 * @returns {String} 依照格式顯示成之時間。
 | 
						||
		 * @_name _module_.prototype.show_time
 | 
						||
		 * @since 2012/3/16 22:36:46
 | 
						||
		 */
 | 
						||
		show_time : function show_time(date, time) {
 | 
						||
			var add_s, _diff_ms,
 | 
						||
			//
 | 
						||
			date_stamp = (date.getMonth() + 1) + '/' + date.getDate() + ' '
 | 
						||
					+ show_date(date),
 | 
						||
			//
 | 
						||
			diff_ms = has_performance_now && this.last_show ? time
 | 
						||
					- this.last_show : (_diff_ms = date
 | 
						||
					- (this.last_show || this.timezone_offset));
 | 
						||
 | 
						||
			if (diff_ms > 0)
 | 
						||
				if (diff_ms < 60000) {
 | 
						||
					add_s = diff_ms >= 1000 && (diff_ms /= 1000);
 | 
						||
					diff_ms = diff_ms.to_fixed ? String(diff_ms.to_fixed(3))
 | 
						||
							.replace(/^0/, '')
 | 
						||
					// : diff_ms.toFixed ? diff_ms.toFixed(3)
 | 
						||
					: (diff_ms | 0);
 | 
						||
					if (add_s)
 | 
						||
						diff_ms += 's';
 | 
						||
				} else
 | 
						||
					diff_ms = show_date(new Date(diff_ms + this.timezone_offset));
 | 
						||
 | 
						||
			this.last_show = has_performance_now ? time : date;
 | 
						||
 | 
						||
			// 不用 CSS.quotes: 在舊版 browser 上可能無效,但本 module 須在舊版上亦正常作動。
 | 
						||
			return '<span class="' + this.className_set.time + '" title="'
 | 
						||
					+ date_stamp + '  '
 | 
						||
					+ (has_performance_now ? time : '+' + _diff_ms) + ' ms">['
 | 
						||
					+ diff_ms + ']</span> ';
 | 
						||
		},
 | 
						||
 | 
						||
		/**
 | 
						||
		 * 當記錄太長時,限制記錄數目在 max_logs。超過這個數目就會把之前的最舊的紀錄消除掉。
 | 
						||
		 * 
 | 
						||
		 * @param {Natural}max_logs
 | 
						||
		 *            最大記錄數目
 | 
						||
		 */
 | 
						||
		set_max_logs : function(max_logs) {
 | 
						||
			var _t = this, _p = p[_t.id];
 | 
						||
			max_logs = Math.floor(max_logs);
 | 
						||
			// accept NaN
 | 
						||
			_p.max_logs = max_logs < 0 ? 0 : max_logs;
 | 
						||
		},
 | 
						||
 | 
						||
		/**
 | 
						||
		 * 設定寫入到哪<br />
 | 
						||
		 * set log board for each instance (document object)
 | 
						||
		 * 
 | 
						||
		 * @_name _module_.prototype.set_board
 | 
						||
		 */
 | 
						||
		set_board : function(o) {
 | 
						||
			var _t = this, _p = p[_t.id];
 | 
						||
			if (o)
 | 
						||
				if (typeof o === 'function')
 | 
						||
					_p.do_function = o;
 | 
						||
 | 
						||
				else {
 | 
						||
					if (typeof o !== 'object' && typeof document === 'object')
 | 
						||
						o = document.getElementById(o);
 | 
						||
					if (o
 | 
						||
					// TODO
 | 
						||
					// && library_namespace.is_HTML_obj(o)
 | 
						||
					) {
 | 
						||
						_p.board = o;
 | 
						||
						_t.set_controller();
 | 
						||
						if (_t = _t.className_set.panel)
 | 
						||
							o.className += ' ' + _t;
 | 
						||
						delete _p.do_function;
 | 
						||
					}
 | 
						||
				}
 | 
						||
 | 
						||
			return _p.board;
 | 
						||
		},
 | 
						||
 | 
						||
		// TODO: 若之後才 include 'interact.DOM',則 controller 沒辦法顯示出來 @ Chrome/25。
 | 
						||
		set_controller : function(c) {
 | 
						||
			var b = p[this.id].board;
 | 
						||
			if (b && (c || (c = this.controller))
 | 
						||
					&& (c = new_node(c, [ b, 0 ])) !== using_DOM_new_node) {
 | 
						||
				if ('controller' in this)
 | 
						||
					delete this.controller;
 | 
						||
				// c.style.height = '1em';
 | 
						||
				// c.style.height = '';
 | 
						||
			}
 | 
						||
		},
 | 
						||
 | 
						||
		/**
 | 
						||
		 * 獲取當前 buffer 中的 log。
 | 
						||
		 * 
 | 
						||
		 * @_name _module_.prototype.get_log
 | 
						||
		 */
 | 
						||
		get_log : function() {
 | 
						||
			return p[this.id].lbuf;
 | 
						||
		},
 | 
						||
 | 
						||
		/**
 | 
						||
		 * show/hide log board. 切換可見狀態。
 | 
						||
		 * 
 | 
						||
		 * @_name _module_.prototype.toggle
 | 
						||
		 */
 | 
						||
		toggle : function(s) {
 | 
						||
			return library_namespace.toggle_display(p[this.id].board, s) !== 'none';
 | 
						||
		},
 | 
						||
 | 
						||
		/**
 | 
						||
		 * clear log board. TODO: use .remove_all_child().
 | 
						||
		 * 
 | 
						||
		 * @_name _module_.prototype.clear_board
 | 
						||
		 */
 | 
						||
		clear_board : function(b) {
 | 
						||
			b.innerHTML = '';
 | 
						||
		},
 | 
						||
 | 
						||
		/**
 | 
						||
		 * 清除全部訊息 clear message
 | 
						||
		 * 
 | 
						||
		 * @_name _module_.prototype.clear
 | 
						||
		 */
 | 
						||
		clear : function() {
 | 
						||
			var _p = p[this.id];
 | 
						||
			if (_p.board) {
 | 
						||
				this.clear_board(_p.board);
 | 
						||
			}
 | 
						||
			_p.lbuf = [];
 | 
						||
		}
 | 
						||
 | 
						||
	};
 | 
						||
 | 
						||
	return (_// JSDT:_module_
 | 
						||
	);
 | 
						||
 | 
						||
}
 | 
						||
 | 
						||
// ----------------------------------------------------------------------------------------------------------------------------------------------------------//
 | 
						||
 | 
						||
function finish(name_space) {
 | 
						||
	// 為 module log 所作的初始化工作。
 | 
						||
 | 
						||
	var module_name = this.id;
 | 
						||
 | 
						||
	// 確認 cssRules 之後才作 delete,否則就得按順序先增者後減。因為刪掉 [2] 之後,後面全部皆會遞補,[3] 會變成 [2]。
 | 
						||
	// TODO: 一般化。
 | 
						||
	function search_CSS_rule(style_sheet, selector) {
 | 
						||
		var rules = style_sheet.cssRules || style_sheet.rules, i = 0, l = rules.length;
 | 
						||
		for (; i < l; i++)
 | 
						||
			if (selector === rules[i].selectorText)
 | 
						||
				return i;
 | 
						||
	}
 | 
						||
 | 
						||
	// WScript.Echo(n.extend);
 | 
						||
 | 
						||
	if (false)
 | 
						||
		code_for_including[generateCode.dLK] = '*var Debug={log:code_for_including()};';
 | 
						||
 | 
						||
	// include resources of module.
 | 
						||
	CeL.run(CeL.get_module_path(module_name, 'log.css'));
 | 
						||
 | 
						||
	// 為本 library 用
 | 
						||
	if (!CeL.Log) {
 | 
						||
		var i, l, log_controller = name_space.extend(), has_caller,
 | 
						||
		// 偵錯等級, debug level, log level.
 | 
						||
		log_icon = {
 | 
						||
			/**
 | 
						||
			 * MEMO (U+1F4DD).<br />
 | 
						||
			 * http://codepoints.net/U+1F4DD http://wiki.livedoor.jp/qvarie/
 | 
						||
			 */
 | 
						||
			log : '📝',
 | 
						||
			/**
 | 
						||
			 * emphasized text<br />
 | 
						||
			 * U+2383 EMPHASIS SYMBOL<br />
 | 
						||
			 * http://codepoints.net/U+2383
 | 
						||
			 */
 | 
						||
			em : '⎃',
 | 
						||
			/**
 | 
						||
			 * 資訊,消息,報告,通知,情報<br />
 | 
						||
			 * WARNING SIGN (U+26A0) @ Miscellaneous Symbols.
 | 
						||
			 */
 | 
						||
			warn : '⚠',
 | 
						||
			/**
 | 
						||
			 * error / fault<br />
 | 
						||
			 * U+2620 SKULL AND CROSSBONES
 | 
						||
			 */
 | 
						||
			error : '☠',
 | 
						||
			/**
 | 
						||
			 * U+2139 INFORMATION SOURCE<br />
 | 
						||
			 * http://en.wiktionary.org/wiki/%E2%84%B9
 | 
						||
			 */
 | 
						||
			info : 'ℹ',
 | 
						||
			/**
 | 
						||
			 * U+1F41B BUG
 | 
						||
			 */
 | 
						||
			debug : '🐛',
 | 
						||
			/**
 | 
						||
			 * U+1F463 footprints
 | 
						||
			 * https://unicode.org/emoji/charts/full-emoji-list.html
 | 
						||
			 */
 | 
						||
			trace : '👣'
 | 
						||
		},
 | 
						||
		// base path of icon
 | 
						||
		icon_path = CeL.get_module_path(module_name, 'icon/');
 | 
						||
 | 
						||
		try {
 | 
						||
			has_caller = function(a) {
 | 
						||
				'use strict';
 | 
						||
				return arguments.callee.caller !== undefined;
 | 
						||
			};
 | 
						||
			has_caller = (function() {
 | 
						||
				return has_caller();
 | 
						||
			})();
 | 
						||
		} catch (e) {
 | 
						||
			has_caller = false;
 | 
						||
		}
 | 
						||
 | 
						||
		// console.log('override: CeL.Log = ' + log_controller[0]);
 | 
						||
		CeL.Log = log_controller[0];
 | 
						||
		// console.log('setup CeL.Log.className_set');
 | 
						||
		Object.assign(CeL.Log.className_set, {
 | 
						||
			info : 'debug_info',
 | 
						||
			em : 'debug_em',
 | 
						||
			debug : 'debug_debug'
 | 
						||
		});
 | 
						||
 | 
						||
		// log 支援 gettext.
 | 
						||
		CeL.Log.message_prefix = function(level) {
 | 
						||
			if (level in log_icon) {
 | 
						||
				return {
 | 
						||
					img : null,
 | 
						||
					'class' : 'debug_icon',
 | 
						||
					src : icon_path + level + '.png',
 | 
						||
					alt : '[' + log_icon[level] + ']',
 | 
						||
					title : log_icon[level] + ' '
 | 
						||
					// gettext_config:{"id":"log-type-fatal","mark_type":"combination_message_id"}
 | 
						||
					// gettext_config:{"id":"log-type-error","mark_type":"combination_message_id"}
 | 
						||
					// gettext_config:{"id":"log-type-warn","mark_type":"combination_message_id"}
 | 
						||
					// gettext_config:{"id":"log-type-em","mark_type":"combination_message_id"}
 | 
						||
					// gettext_config:{"id":"log-type-info","mark_type":"combination_message_id"}
 | 
						||
					// gettext_config:{"id":"log-type-log","mark_type":"combination_message_id"}
 | 
						||
					// gettext_config:{"id":"log-type-debug","mark_type":"combination_message_id"}
 | 
						||
					// gettext_config:{"id":"log-type-trace","mark_type":"combination_message_id"}
 | 
						||
					+ CeL.gettext('log-type-' + level)
 | 
						||
				};
 | 
						||
			}
 | 
						||
			return '';
 | 
						||
		};
 | 
						||
 | 
						||
		// TODO: copy result, paste code.
 | 
						||
		var controller = [ ':', {
 | 
						||
			// U+239A CLEAR SCREEN SYMBOL
 | 
						||
			a : '⎚',
 | 
						||
			href : '#',
 | 
						||
			title : "clear / 清除所有訊息",
 | 
						||
			onclick : function() {
 | 
						||
				CeL.Log.clear();
 | 
						||
				return false;
 | 
						||
			}
 | 
						||
		}, {
 | 
						||
			// toggle / switch
 | 
						||
			// U+1F50C ELECTRIC PLUG
 | 
						||
			a : '🔌',
 | 
						||
			href : '#',
 | 
						||
			title : "切換訊息面板\nshow/hidden log panel",
 | 
						||
			onclick : function() {
 | 
						||
				CeL.set_class(this, 'debug_hide', {
 | 
						||
					remove : CeL.Log.toggle()
 | 
						||
				});
 | 
						||
				return false;
 | 
						||
			}
 | 
						||
		}, {
 | 
						||
			span : '↑',
 | 
						||
			title : "提升偵錯等級",
 | 
						||
			S : 'cursor:pointer;font-size:.7em;',
 | 
						||
			onselect : function() {
 | 
						||
				return false;
 | 
						||
			},
 | 
						||
			onclick : function() {
 | 
						||
				CeL.set_debug(CeL.is_debug() + 1);
 | 
						||
				CeL.debug('提升偵錯等級至 ' + CeL.is_debug(), 1, 'Log.controller');
 | 
						||
				return false;
 | 
						||
			}
 | 
						||
		}, {
 | 
						||
			span : '↓',
 | 
						||
			title : "降低偵錯等級",
 | 
						||
			S : 'cursor:pointer;font-size:.7em;',
 | 
						||
			onselect : function() {
 | 
						||
				return false;
 | 
						||
			},
 | 
						||
			onclick : function() {
 | 
						||
				CeL.set_debug(CeL.is_debug() - 1);
 | 
						||
				CeL.debug('降低偵錯等級至 ' + CeL.is_debug(), 0, 'Log.controller');
 | 
						||
				return false;
 | 
						||
			}
 | 
						||
		}, {
 | 
						||
			span : '↓',
 | 
						||
			title : "取消 debug",
 | 
						||
			S : 'cursor:pointer;font-size:.7em;text-decoration:underline;',
 | 
						||
			onselect : function() {
 | 
						||
				return false;
 | 
						||
			},
 | 
						||
			onclick : function() {
 | 
						||
				CeL.set_debug(0);
 | 
						||
				return false;
 | 
						||
			}
 | 
						||
		}, {
 | 
						||
			br : null
 | 
						||
		} ];
 | 
						||
		l = {
 | 
						||
			debug : 0,
 | 
						||
			log : 0,
 | 
						||
			info : 'information',
 | 
						||
			em : 'emphasis',
 | 
						||
			warn : 'warning',
 | 
						||
			error : 'error'
 | 
						||
		};
 | 
						||
		for (i in l) {
 | 
						||
			controller.push(' ', {
 | 
						||
				a : log_icon[i],
 | 
						||
				href : '#',
 | 
						||
				title : 'toggle [' + i + ']\n切換 ' + (l[i] || i) + ' 訊息',
 | 
						||
				onclick : function() {
 | 
						||
					var tag = this.title.match(/\[([^\]]+)\]/);
 | 
						||
					if (tag)
 | 
						||
						CeL.set_class(this, 'debug_hide', {
 | 
						||
							remove : CeL.toggle_log(tag[1])
 | 
						||
						});
 | 
						||
					return false;
 | 
						||
				}
 | 
						||
			});
 | 
						||
		}
 | 
						||
		// 增加 group 以便在多項輸入時亦可 toggle 或排版。
 | 
						||
		CeL.Log.controller = {
 | 
						||
			div : [ {
 | 
						||
				a : 'log',
 | 
						||
				href : '#',
 | 
						||
				title : 'log 控制項',
 | 
						||
				onclick : function() {
 | 
						||
					var parentNode = this.parentNode;
 | 
						||
					if (parentNode.force_show) {
 | 
						||
						// DOM 不可使用 delete @ IE9
 | 
						||
						// delete parentNode.force_show;
 | 
						||
						parentNode.force_show = false;
 | 
						||
					} else {
 | 
						||
						CeL.toggle_display(this.nextSibling,
 | 
						||
						//
 | 
						||
						parentNode.force_show = true);
 | 
						||
					}
 | 
						||
					return false;
 | 
						||
				}
 | 
						||
			}, {
 | 
						||
				span : controller,
 | 
						||
				C : 'debug_controller'
 | 
						||
			} ],
 | 
						||
			// TODO: 即使僅是移動 mouse 進入 child,也會執行多次。
 | 
						||
			onmouseover : function() {
 | 
						||
				CeL.toggle_display(this.firstChild.nextSibling, 1);
 | 
						||
			},
 | 
						||
			onmouseout : function() {
 | 
						||
				if (!this.force_show) {
 | 
						||
					CeL.toggle_display(this.firstChild.nextSibling, 0);
 | 
						||
				}
 | 
						||
			},
 | 
						||
			C : 'debug_controller_panel'
 | 
						||
		};
 | 
						||
 | 
						||
		// 在 CeL.log 被重新設定前先 cache 一下。
 | 
						||
		var log_buffer = CeL.log && CeL.log.buffer;
 | 
						||
 | 
						||
		// --------------------------------------------------------------------------------------------
 | 
						||
		// front ends of log function
 | 
						||
 | 
						||
		/**
 | 
						||
		 * 警告: 在 node.js v0.10.25, v0.11.16 中,不使用 var 的模式設定 function,會造成:<br />
 | 
						||
		 * In strict mode code, functions can only be declared at top level or
 | 
						||
		 * immediately within another function.
 | 
						||
		 * 
 | 
						||
		 * 在 node.js v4.2.1 中可以順利 pass。
 | 
						||
		 */
 | 
						||
 | 
						||
		var log_front_end_fatal =
 | 
						||
		// fatal: the most serious 致命錯誤。
 | 
						||
		function log_front_end_fatal(message, error_to_throw) {
 | 
						||
			if (CeL.is_WWW())
 | 
						||
				try {
 | 
						||
					console.trace(error_to_throw);
 | 
						||
					// 模擬 throw 以 get .stack
 | 
						||
					throw CeL.is_type(error_to_throw, 'Error') ? error_to_throw
 | 
						||
							: new Error(error_to_throw || 'Fatal error');
 | 
						||
				} catch (e) {
 | 
						||
					// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
 | 
						||
					CeL.error(e.stack ? message
 | 
						||
							+ '<br />stack:<div class="debug_stack">'
 | 
						||
							+ (typeof e.stack === 'string' ? e.stack.replace(
 | 
						||
									/\n/g, '<br />') : e.stack) + '</div>'
 | 
						||
							: message);
 | 
						||
					if (typeof console === 'object' && console.trace) {
 | 
						||
						// Will show stacks
 | 
						||
						console.trace(e);
 | 
						||
					}
 | 
						||
				}
 | 
						||
			else
 | 
						||
				CeL.error(message);
 | 
						||
 | 
						||
			if (typeof error_to_throw === 'undefined')
 | 
						||
				// 預設會 throw message.
 | 
						||
				error_to_throw = message;
 | 
						||
 | 
						||
			if (error_to_throw) {
 | 
						||
				if (CeL.platform.nodejs && error_to_throw !== message)
 | 
						||
					// node.js 中,throw Error 可能無法顯示 local encoding,因此在此先顯示一次。
 | 
						||
					console.error(error_to_throw);
 | 
						||
				throw CeL.is_type(error_to_throw, 'Error') ? error_to_throw
 | 
						||
						: new Error(error_to_throw);
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		var log_front_end_debug =
 | 
						||
		// 增加 debug 訊息。
 | 
						||
		function log_front_end_debug(message, level, caller, clean) {
 | 
						||
			if (false) {
 | 
						||
				alert(CeL.is_debug() + ',' + l + '(' + (l === undefined) + '),'
 | 
						||
						+ message);
 | 
						||
			}
 | 
						||
 | 
						||
			if (!CeL.is_debug(level)) {
 | 
						||
				return;
 | 
						||
			}
 | 
						||
 | 
						||
			if (typeof message === 'function') {
 | 
						||
				// for .debug(function(){return some_function(..);}, 3);
 | 
						||
				message = 'function: [' + message + ']<br />return: ['
 | 
						||
						+ message() + ']';
 | 
						||
			}
 | 
						||
 | 
						||
			if (!caller && has_caller) {
 | 
						||
				// TODO: do not use arguments
 | 
						||
				caller = caller !== arguments.callee
 | 
						||
						&& CeL.get_function_name(arguments.callee.caller);
 | 
						||
				if (false) {
 | 
						||
					CeL.log(CeL.is_type(arguments.callee.caller));
 | 
						||
					CeL.log(Array.isArray(caller));
 | 
						||
					CeL.log(caller + ': ' + arguments.callee.caller);
 | 
						||
					CeL.warn(CeL.debug);
 | 
						||
				}
 | 
						||
			}
 | 
						||
			if (caller) {
 | 
						||
				message = CeL.is_WWW() ? [ {
 | 
						||
					// (caller.charAt(0) === '.' ? CeL.Class + caller :
 | 
						||
					// caller)
 | 
						||
					span : caller,
 | 
						||
					'class' : 'debug_caller'
 | 
						||
				}, ': ', message ] : CeL.to_SGR([ '', 'fg=yellow',
 | 
						||
						caller + ': ', '-fg', message ]);
 | 
						||
			}
 | 
						||
 | 
						||
			CeL.Log.log(message, clean, {
 | 
						||
				level : 'debug',
 | 
						||
				add_class : 'debug_' + (level || CeL.is_debug())
 | 
						||
			});
 | 
						||
		}
 | 
						||
 | 
						||
		var log_front_end_info =
 | 
						||
		//
 | 
						||
		function log_front_end_info(message, clean) {
 | 
						||
			// information
 | 
						||
			CeL.Log.log.call(CeL.Log, message, clean, 'info');
 | 
						||
			// CeL.log.apply(CeL, arguments);
 | 
						||
		};
 | 
						||
 | 
						||
		var log_front_end_toggle_log =
 | 
						||
		// 切換(顯示/隱藏)個別訊息。
 | 
						||
		function log_front_end_toggle_log(type, show) {
 | 
						||
			if (!type)
 | 
						||
				type = 'debug';
 | 
						||
			var hiding = type in CeL.Log.class_hide;
 | 
						||
			if (typeof show === 'undefined' || show && hiding || !show
 | 
						||
					&& !hiding)
 | 
						||
				try {
 | 
						||
					// need switch.
 | 
						||
					var style_sheet = document.styleSheets[0], selector = '.'
 | 
						||
							+ CeL.Log.className_set[type], CSS_index = hiding ? search_CSS_rule(
 | 
						||
							style_sheet, selector)
 | 
						||
							: undefined;
 | 
						||
					if (isNaN(CSS_index)) {
 | 
						||
						// assign a new index.
 | 
						||
						CSS_index = style_sheet.cssRules
 | 
						||
								&& style_sheet.cssRules.length ||
 | 
						||
								// IE6
 | 
						||
								style_sheet.rules && style_sheet.rules.length
 | 
						||
								|| 0;
 | 
						||
						CeL.debug('insert CSS index: ' + CSS_index, 2,
 | 
						||
								'toggle_log');
 | 
						||
						var style = 'display:none';
 | 
						||
						style_sheet.insertRule ?
 | 
						||
						/**
 | 
						||
						 * firefox, IE 必須輸入 index.<br />
 | 
						||
						 * <a
 | 
						||
						 * href="https://developer.mozilla.org/en/DOM/CSSStyleSheet/insertRule"
 | 
						||
						 * accessdate="2012/5/14 13:13">insertRule - MDN</a>
 | 
						||
						 */
 | 
						||
						style_sheet.insertRule(selector + '{' + style + ';}',
 | 
						||
								CSS_index) :
 | 
						||
						/**
 | 
						||
						 * IE6: <a
 | 
						||
						 * href="http://msdn.microsoft.com/en-us/library/aa358796%28v=vs.85%29.aspx"
 | 
						||
						 * accessdate="2012/5/14 13:13">IHTMLStyleSheet::addRule
 | 
						||
						 * method (Internet Explorer)</a>
 | 
						||
						 */
 | 
						||
						style_sheet.addRule(selector, style, CSS_index);
 | 
						||
 | 
						||
						// OK 之後才設定.
 | 
						||
						CeL.Log.class_hide[type] = CSS_index;
 | 
						||
 | 
						||
					} else {
 | 
						||
						CeL.debug('delete CSS index: ' + CSS_index, 2,
 | 
						||
								'toggle_log');
 | 
						||
						style_sheet.deleteRule ? style_sheet
 | 
						||
								.deleteRule(CSS_index) :
 | 
						||
						// IE6
 | 
						||
						style_sheet.removeRule(CSS_index);
 | 
						||
						// OK 之後才 delete.
 | 
						||
						delete CeL.Log.class_hide[type];
 | 
						||
					}
 | 
						||
					hiding = !hiding;
 | 
						||
				} catch (e) {
 | 
						||
					CeL
 | 
						||
							.log('The browser may not support <a href="http://www.w3.org/TR/DOM-Level-2-Style/css" target="_blank">Document Object Model CSS</a>? Cannot toggle debug message: <em>'
 | 
						||
									+ e.message + '</em>');
 | 
						||
				}
 | 
						||
			return !hiding;
 | 
						||
		}
 | 
						||
 | 
						||
		var log_front_end_assert =
 | 
						||
		/**
 | 
						||
		 * 斷定/測試/驗證 verify/檢查狀態。<br />
 | 
						||
		 * 
 | 
						||
		 * @param {Boolean|Array|Function}condition
 | 
						||
		 *            test case.<br />
 | 
						||
		 *            {Function} testing function to run. Using default expected
 | 
						||
		 *            value: true<br />
 | 
						||
		 *            {Array} [ condition 1, condition 2 ]<br />
 | 
						||
		 *            {Object} 直接將之當作 options
 | 
						||
		 * 
 | 
						||
		 * @param {Object}[options]
 | 
						||
		 *            附加參數/設定選擇性/特殊功能與選項。 {<br />
 | 
						||
		 *            {String}name: test name 此次測試名稱。,<br />
 | 
						||
		 *            {String}NG: meaning of failure,<br />
 | 
						||
		 *            {String}OK: meaning of passed,<br />
 | 
						||
		 *            {String}hide_OK: false: 當 passed 時不顯示,<br />
 | 
						||
		 *            {Boolean}ignorable: false / need 手動 check,<br />
 | 
						||
		 *            {String|Object}type: expected type,<br />
 | 
						||
		 *            {Boolean}no_cache: false,<br />
 | 
						||
		 *            {Any}expect: expected value 預期的結果。should be what value.,<br />
 | 
						||
		 *            {Number}error_rate > 0: 容許誤差率 permissible error ratio.
 | 
						||
		 *            e.g., Number.EPSILON,<br />
 | 
						||
		 *            {Boolean}exactly: true, need exactly (value === expected)
 | 
						||
		 *            or false: equal (value == expected) is also OK.<br />
 | 
						||
		 *            {Boolean}force_true: false, 當測試效能時,強迫測試結果一定成功。<br />
 | 
						||
		 *            {String}eval: testing expression code to eval = value /
 | 
						||
		 *            function(){return value_to_test;}<br />
 | 
						||
		 *            {Function}callback: 回調函數。 callback(passed)<br /> }
 | 
						||
		 * 
 | 
						||
		 * @returns {Boolean|...} {Boolean}assertion is succeed.<br />
 | 
						||
		 *          {...} ignorable message.
 | 
						||
		 * 
 | 
						||
		 * @since 2012/9/19 00:20:49, 2015/10/18 21:31:35 重構
 | 
						||
		 */
 | 
						||
		function log_front_end_assert(condition, options) {
 | 
						||
 | 
						||
			// --------------------------------
 | 
						||
			// 前置處理作業: condition。
 | 
						||
			if (!options) {
 | 
						||
				if (CeL.is_Object(condition)) {
 | 
						||
					// 直接將之當作 options
 | 
						||
					options = condition;
 | 
						||
					condition = options.eval;
 | 
						||
				} else {
 | 
						||
					// 前置處理作業: options。
 | 
						||
					// (undefined | null).attribute is NOT accessable.
 | 
						||
					// ('attribute' in false), ('attribute' in 0) is NOT
 | 
						||
					// evaluable.
 | 
						||
 | 
						||
					// options = Object.create(null);
 | 
						||
					// This is faster.
 | 
						||
					options = new Boolean;
 | 
						||
 | 
						||
					// assert: options.attribute is accessable.
 | 
						||
					// assert: ('attribute' in options) is evaluable.
 | 
						||
				}
 | 
						||
			} else if (typeof options === 'string') {
 | 
						||
				options = {
 | 
						||
					name : options
 | 
						||
				};
 | 
						||
			} else if (typeof options === 'function') {
 | 
						||
				options = {
 | 
						||
					callback : options
 | 
						||
				};
 | 
						||
			}
 | 
						||
 | 
						||
			var type = options.type;
 | 
						||
			if (Array.isArray(condition)) {
 | 
						||
				condition = condition.slice(0, type ? 1 : 2);
 | 
						||
				if (!type && typeof condition[1] !== 'function'
 | 
						||
						&& typeof condition[1] !== 'object')
 | 
						||
					// record original condition.
 | 
						||
					condition.original = condition[0];
 | 
						||
			} else {
 | 
						||
				// 有 options.type 將忽略 options.expect 以及 condition[1]!!
 | 
						||
				if (type) {
 | 
						||
					condition = [ condition ];
 | 
						||
				} else {
 | 
						||
					condition = [ condition,
 | 
						||
					// default expected value: true
 | 
						||
					'expect' in options ? options.expect : true ];
 | 
						||
					// record original condition.
 | 
						||
					condition.original = condition[0];
 | 
						||
				}
 | 
						||
			}
 | 
						||
			// assert: condition = {Array} [ condition 1, condition 2 ]
 | 
						||
 | 
						||
			function condition_handler(_c, index) {
 | 
						||
				if (options.eval && typeof _c === 'string')
 | 
						||
					_c = CeL.eval_parse(_c);
 | 
						||
 | 
						||
				if (typeof _c === 'function')
 | 
						||
					_c = _c();
 | 
						||
 | 
						||
				// may use .map()
 | 
						||
				condition[index] = _c;
 | 
						||
			}
 | 
						||
 | 
						||
			// fatal error
 | 
						||
			var fatal;
 | 
						||
			// if (!options.force_true)
 | 
						||
			condition.forEach(options.no_cache ? condition_handler
 | 
						||
			//
 | 
						||
			: function(_c, index) {
 | 
						||
				try {
 | 
						||
					condition_handler(_c, index);
 | 
						||
				} catch (e) {
 | 
						||
					// 執行 condition 時出錯,throw 時的處置。
 | 
						||
					fatal = e || true;
 | 
						||
					CeL.warn('assert: 執行 condition 時出錯: ' + e.message);
 | 
						||
					if (typeof console === 'object' && console.trace) {
 | 
						||
						// Will show stacks
 | 
						||
						console.trace(e);
 | 
						||
					}
 | 
						||
				}
 | 
						||
			});
 | 
						||
 | 
						||
			// assert: condition =
 | 
						||
			// {Array} [ 純量 value 1, 純量 value 2: expected value ]
 | 
						||
			// condition = The actual value to test.
 | 
						||
 | 
						||
			// --------------------------------
 | 
						||
			var exactly, equal;
 | 
						||
 | 
						||
			if (!fatal
 | 
						||
			// && !options.force_true
 | 
						||
			) {
 | 
						||
				if (type) {
 | 
						||
					// 處理作業: type。
 | 
						||
					condition = condition[0];
 | 
						||
					exactly = equal = typeof type === 'string'
 | 
						||
					//
 | 
						||
					? typeof condition === type || CeL.is_type(condition, type)
 | 
						||
					// TODO: check
 | 
						||
					// String|Function|Object|Array|Boolean|Number|Date|RegExp|Error|undefined
 | 
						||
					: condition.constructor === type
 | 
						||
							|| Object.getPrototypeOf(condition) === type
 | 
						||
							|| (type = CeL.native_name(type))
 | 
						||
							&& CeL.is_type(condition, type);
 | 
						||
				} else if ((equal = +options.error_rate) > 0) {
 | 
						||
					// 容許誤差率 permissible error ratio / rate. e.g.,
 | 
						||
					// Number.EPSILON
 | 
						||
					exactly = equal = Math.abs(1 - +condition[0]
 | 
						||
							/ +condition[1]) <= equal;
 | 
						||
				} else {
 | 
						||
					exactly = equal = Object.is(condition[0], condition[1]);
 | 
						||
					if (!exactly) {
 | 
						||
						// Do not use "==="
 | 
						||
						equal = condition[0] == condition[1];
 | 
						||
					}
 | 
						||
				}
 | 
						||
			}
 | 
						||
 | 
						||
			// --------------------------------
 | 
						||
			// report.
 | 
						||
 | 
						||
			var MAX_LENGTH = 200;
 | 
						||
 | 
						||
			function get_type_of(value, with_quote) {
 | 
						||
				var type, is_native_type;
 | 
						||
				if (Array.isArray(value)) {
 | 
						||
					type = 'Array';
 | 
						||
					is_native_type = true;
 | 
						||
				} else if (CeL.is_Object(value)) {
 | 
						||
					type = 'Object';
 | 
						||
					is_native_type = true;
 | 
						||
				} else if (CeL.is_RegExp(value)) {
 | 
						||
					type = 'RegExp';
 | 
						||
					is_native_type = true;
 | 
						||
				} else if (CeL.is_Date(value)) {
 | 
						||
					type = 'Date';
 | 
						||
					is_native_type = true;
 | 
						||
				} else if (value instanceof Error) {
 | 
						||
					type = 'Error';
 | 
						||
					is_native_type = true;
 | 
						||
				} else {
 | 
						||
					type = typeof value;
 | 
						||
					if (type === 'object') {
 | 
						||
						type = CeL.is_type(value) || type;
 | 
						||
					} else if ([ 'string', 'number', 'boolean', 'function',
 | 
						||
					// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof
 | 
						||
					'bigint', 'symbol' ].includes(type)) {
 | 
						||
						is_native_type = true;
 | 
						||
						type = type.charAt(0).toUpperCase() + type.slice(1);
 | 
						||
					}
 | 
						||
				}
 | 
						||
				return with_quote ? is_native_type ? '{' + type + '}' : '('
 | 
						||
						+ type + ')' : type;
 | 
						||
			}
 | 
						||
 | 
						||
			function quote(message, add_type) {
 | 
						||
				if (add_type &&
 | 
						||
				// 有些 value 沒必要加上 type。
 | 
						||
				message !== null && message !== undefined
 | 
						||
				// is not NaN
 | 
						||
				&& message === message) {
 | 
						||
					add_type = get_type_of(message, true) + ' ';
 | 
						||
				} else {
 | 
						||
					add_type = '';
 | 
						||
				}
 | 
						||
 | 
						||
				if (typeof message === 'string' && add_type
 | 
						||
						&& message.length <= MAX_LENGTH
 | 
						||
						&& typeof JSON === 'object' && JSON.stringify) {
 | 
						||
					message = JSON.stringify(message);
 | 
						||
				} else {
 | 
						||
					message = String(message).replace(/\r/g, '\\r');
 | 
						||
					if (message.length > MAX_LENGTH)
 | 
						||
						message = '[' + message.slice(0, MAX_LENGTH) + ']...'
 | 
						||
								+ message.length;
 | 
						||
					else
 | 
						||
						message = '[' + message + ']';
 | 
						||
				}
 | 
						||
 | 
						||
				return add_type + message;
 | 
						||
			}
 | 
						||
 | 
						||
			var test_name = options.name ? quote(options.name) : 'Assertion';
 | 
						||
 | 
						||
			// --------------------------------
 | 
						||
			// failed.
 | 
						||
 | 
						||
			if (!options.force_true && (!equal || !exactly &&
 | 
						||
			// assert: exactly === true 的條件比 equal === true 嚴苛。
 | 
						||
			(!('exactly' in options) || options.exactly))) {
 | 
						||
				var error_message = options.NG;
 | 
						||
				if (!error_message) {
 | 
						||
					error_message = [ test_name,
 | 
						||
					// if fault, message: 失敗時所要呈現訊息。
 | 
						||
					CeL.to_SGR([ ' ', 'fg=red', 'failed:', '-fg;-bg', ' ' ]) ];
 | 
						||
					if (type) {
 | 
						||
						error_message.push('type of ' + quote(condition)
 | 
						||
								+ ' is not (' + type + ')');
 | 
						||
					} else {
 | 
						||
						if (('original' in condition)
 | 
						||
								&& condition[0] !== condition.original) {
 | 
						||
							var original = '' + condition.original;
 | 
						||
							if (typeof condition.original === 'function') {
 | 
						||
								var matched = original
 | 
						||
										.match(CeL.PATTERN_function);
 | 
						||
								if (matched) {
 | 
						||
									original = matched[1];
 | 
						||
								}
 | 
						||
							}
 | 
						||
							error_message.push(quote(original) + '→');
 | 
						||
						}
 | 
						||
						error_message.push(quote(condition[0], true)
 | 
						||
						//
 | 
						||
						+ ' !== ' + quote(condition[1], true));
 | 
						||
					}
 | 
						||
 | 
						||
					if (equal) {
 | 
						||
						error_message.push(',但 "==" 之關係成立。');
 | 
						||
					}
 | 
						||
 | 
						||
					error_message = error_message.join('');
 | 
						||
				}
 | 
						||
 | 
						||
				CeL.fatal(error_message, CeL.assert.throw_Error ?
 | 
						||
				// exception to throw
 | 
						||
				new Error(error_message) : false);
 | 
						||
 | 
						||
				var ignorable = options.ignorable;
 | 
						||
				return ignorable ? ignorable === true ? 'ignored' : ignorable
 | 
						||
						: fatal ? undefined : false;
 | 
						||
			}
 | 
						||
 | 
						||
			// --------------------------------
 | 
						||
			// passed. 無錯誤發生。
 | 
						||
 | 
						||
			if (!options.hide_OK && CeL.is_debug()) {
 | 
						||
				var passed_message = options.OK;
 | 
						||
				if (!passed_message) {
 | 
						||
					passed_message = [ test_name,
 | 
						||
					//
 | 
						||
					CeL.to_SGR([ ' ', 'fg=green', 'passed', '-fg', ' ' ]),
 | 
						||
					//
 | 
						||
					quote(condition[0], true) ].join('');
 | 
						||
				}
 | 
						||
				CeL.debug(passed_message, 1,
 | 
						||
				// caller: see: CeL.debug
 | 
						||
				has_caller && CeL.get_function_name(arguments.callee.caller));
 | 
						||
			}
 | 
						||
 | 
						||
			return true;
 | 
						||
		}
 | 
						||
 | 
						||
		var log_front_end_test =
 | 
						||
		/**
 | 
						||
		 * 整套測試, unit test 單元測試。
 | 
						||
		 * 
 | 
						||
		 * @example <code>
 | 
						||
 | 
						||
		CeL.test([ [ 'aa', {
 | 
						||
			type : String
 | 
						||
		} ], [ 456, {
 | 
						||
			type : 123
 | 
						||
		} ], [ {}, {
 | 
						||
			type : Object
 | 
						||
		} ], [ false, {
 | 
						||
			type : Boolean
 | 
						||
		} ] ], 'type test');
 | 
						||
 | 
						||
 | 
						||
		// --------------------------------------
 | 
						||
		// TODO:
 | 
						||
 | 
						||
		// may ignore:
 | 
						||
		CeL.setup_test(test_group_name);
 | 
						||
 | 
						||
		CeL.test(test_group_name, conditions, options);
 | 
						||
		// conditions #1: [
 | 
						||
		// [ test value: true / false, 'test_group_name' ],
 | 
						||
		// [ test value: true / false, {name:'test_group_name'} ],
 | 
						||
		// [ test value: true / false, {options} ],
 | 
						||
		// [ test value: true / false ],
 | 
						||
		// test value: true / false,
 | 
						||
		//
 | 
						||
		// [ [ test value 1, test value 2 ], 'test_group_name' ],
 | 
						||
		// [ [ test value 1, test value 2 ], {name:'test_group_name'} ],
 | 
						||
		// [ [ test value 1, test value 2 ], {options} ],
 | 
						||
		// [ [ test value 1, test value 2 ] ],
 | 
						||
		//
 | 
						||
		// [ function tester(), 'test_group_name' ],
 | 
						||
		// [ function tester() ],
 | 
						||
		// [ function tester(callback), {need_callback:true} ],
 | 
						||
		// function tester(),
 | 
						||
		// function tester() { return new Promise },
 | 
						||
		// async function tester(),
 | 
						||
		//
 | 
						||
		// ]
 | 
						||
 | 
						||
		// conditions #2:
 | 
						||
		// function async_tester(assert)
 | 
						||
		// async function async_tester(assert)
 | 
						||
		//
 | 
						||
		// CeL.test(test_group_name, function async_tester(assert, callback), {need_callback:true});
 | 
						||
		//
 | 
						||
		// assert(test value: true / false, 'test_group_name');
 | 
						||
		// assert(test value: true / false, options);
 | 
						||
		// assert(test value: true / false);
 | 
						||
		// assert([ test value 1, test value 2 ], 'test_group_name');
 | 
						||
		// assert([ test value 1, test value 2 ]);
 | 
						||
 | 
						||
		CeL.test_finished();
 | 
						||
 | 
						||
 | 
						||
		</code>
 | 
						||
		 * 
 | 
						||
		 * @param {String}[test_group_name]
 | 
						||
		 *            test name 此次測試名稱。
 | 
						||
		 * @param {Array|Function}conditions
 | 
						||
		 *            condition list passed to assert(): [ [ condition / test
 | 
						||
		 *            value, options ], [], ... ].<br />
 | 
						||
		 *            允許 {Function}condition(assert, test_handler)
 | 
						||
		 * @param {Object}[options]
 | 
						||
		 *            附加參數/設定選擇性/特殊功能與選項。 {<br />
 | 
						||
		 *            {String}name: test name 此次測試名稱。<br />
 | 
						||
		 *            {Object}options: default options for running CeL.assert().<br />
 | 
						||
		 *            {Function}callback: 回調函數。 callback(recorder,
 | 
						||
		 *            test_group_name)<br /> }
 | 
						||
		 * 
 | 
						||
		 * @returns {Integer}有錯誤發生的數量。
 | 
						||
		 * 
 | 
						||
		 * @since 2012/9/19 00:20:49, 2015/10/18 23:8:9 refactoring 重構
 | 
						||
		 */
 | 
						||
		function log_front_end_test(test_group_name, conditions, options) {
 | 
						||
			if ((Array.isArray(test_group_name) || typeof test_group_name === 'function')
 | 
						||
					&& !options) {
 | 
						||
				// shift arguments: 跳過 test_group_name。
 | 
						||
				options = conditions;
 | 
						||
				conditions = test_group_name;
 | 
						||
			}
 | 
						||
 | 
						||
			if (!Array.isArray(conditions) && typeof conditions !== 'function') {
 | 
						||
				throw new Error(CeL.Class
 | 
						||
						+ '.test: Please input {Array} or {Function}!');
 | 
						||
				return;
 | 
						||
			}
 | 
						||
 | 
						||
			var assert = CeL.assert,
 | 
						||
			// default options for running CeL.assert().
 | 
						||
			default_options;
 | 
						||
 | 
						||
			if (options) {
 | 
						||
				if (typeof options === 'function') {
 | 
						||
					// console.log('Set callback: ' + options);
 | 
						||
					options = {
 | 
						||
						callback : options
 | 
						||
					};
 | 
						||
				} else if (typeof options === 'string') {
 | 
						||
					if (!test_group_name)
 | 
						||
						test_group_name = options;
 | 
						||
					options = undefined;
 | 
						||
				} else if ('options' in options)
 | 
						||
					default_options = options.options;
 | 
						||
			}
 | 
						||
 | 
						||
			default_options = Object.assign({
 | 
						||
				hide_OK : true,
 | 
						||
				no_cache : true
 | 
						||
			}, default_options);
 | 
						||
 | 
						||
			var recorder = {
 | 
						||
				// OK
 | 
						||
				passed : [],
 | 
						||
				// skipped
 | 
						||
				ignored : [],
 | 
						||
				// value is not the same.
 | 
						||
				failed : [],
 | 
						||
				// 執行 condition 時出錯,throw。
 | 
						||
				fatal : [],
 | 
						||
				//
 | 
						||
				all : []
 | 
						||
			};
 | 
						||
 | 
						||
			function handler(condition_arguments) {
 | 
						||
				if (!condition_arguments) {
 | 
						||
					// skip this one.
 | 
						||
					return;
 | 
						||
				}
 | 
						||
 | 
						||
				recorder.all.push(condition_arguments);
 | 
						||
 | 
						||
				var result;
 | 
						||
				try {
 | 
						||
					if (typeof condition_arguments === 'function') {
 | 
						||
						result = condition_arguments(assert_proxy);
 | 
						||
					} else {
 | 
						||
						// assert: Array.isArray(condition_arguments) or
 | 
						||
						// arguments
 | 
						||
						var options = condition_arguments[1],
 | 
						||
						//
 | 
						||
						condition = condition_arguments[0];
 | 
						||
						// 前置處理作業: condition, options。
 | 
						||
						// copy from log_front_end_assert()
 | 
						||
						// 目的在將輸入轉成 {Object},以添入 options。
 | 
						||
						if (!options) {
 | 
						||
							if (CeL.is_Object(condition)) {
 | 
						||
								// 直接將之當作 options
 | 
						||
								options = condition;
 | 
						||
								condition = options.eval;
 | 
						||
							}
 | 
						||
						} else if (typeof options === 'string') {
 | 
						||
							options = {
 | 
						||
								name : options
 | 
						||
							};
 | 
						||
						} else if (typeof options === 'function') {
 | 
						||
							options = {
 | 
						||
								callback : options
 | 
						||
							};
 | 
						||
						}
 | 
						||
 | 
						||
						// 不汙染 default_options, options
 | 
						||
						options = Object.assign(Object.clone(default_options),
 | 
						||
								options);
 | 
						||
 | 
						||
						result = assert(condition, options);
 | 
						||
					}
 | 
						||
 | 
						||
				} catch (e) {
 | 
						||
					if (typeof console === 'object' && console.trace) {
 | 
						||
						// Will show stacks
 | 
						||
						console.trace(e);
 | 
						||
					}
 | 
						||
					recorder.fatal.push(condition_arguments);
 | 
						||
					return;
 | 
						||
				}
 | 
						||
 | 
						||
				switch (result) {
 | 
						||
				case true:
 | 
						||
					recorder.passed.push(condition_arguments);
 | 
						||
					break;
 | 
						||
				case false:
 | 
						||
					recorder.failed.push(condition_arguments);
 | 
						||
					break;
 | 
						||
				default:
 | 
						||
					recorder.ignored.push(condition_arguments);
 | 
						||
					break;
 | 
						||
				}
 | 
						||
 | 
						||
				return result === true;
 | 
						||
			}
 | 
						||
 | 
						||
			// 模擬 CeL.assert()
 | 
						||
			function assert_proxy() {
 | 
						||
				var sub_test_data = assert_proxy.tests_left[assert_proxy.latest_sub_test_name];
 | 
						||
				if (sub_test_data)
 | 
						||
					sub_test_data.assert_count++;
 | 
						||
				return handler.call(null, arguments);
 | 
						||
			}
 | 
						||
 | 
						||
			// --------------------------------
 | 
						||
			// report. 當有多個 setup_test(),report() 可能執行多次!
 | 
						||
			var report = function(sub_test_name) {
 | 
						||
				var messages;
 | 
						||
				if (test_group_name) {
 | 
						||
					messages = [ 'Test '
 | 
						||
					// asynchronous operations
 | 
						||
					+ (assert_proxy.asynchronous ? 'asynchronous ' : '') + '[',
 | 
						||
							'fg=cyan', test_group_name ];
 | 
						||
					if (sub_test_name) {
 | 
						||
						messages.push('-fg',
 | 
						||
						// ']=>[', ': ', ']→[', ':', ': '
 | 
						||
						': ', 'fg=cyan', sub_test_name);
 | 
						||
					}
 | 
						||
					messages.push('-fg', ']: ');
 | 
						||
					messages = [ CeL.to_SGR(messages) ];
 | 
						||
				} else {
 | 
						||
					messages = [];
 | 
						||
				}
 | 
						||
 | 
						||
				function join() {
 | 
						||
					if (recorder.ignored.length > 0)
 | 
						||
						messages.push(CeL.to_SGR([
 | 
						||
								', ' + recorder.ignored.length + ' ',
 | 
						||
								'fg=yellow', 'ignored', '-fg' ]));
 | 
						||
 | 
						||
					// Time elapsed, 使用/耗費時間。cf. eta, Estimated Time of Arrival
 | 
						||
					var elapsed = Date.now() - assert_proxy.starts;
 | 
						||
					if (elapsed >= 1000)
 | 
						||
						messages.push(', ' + (elapsed / 1000).to_fixed(2)
 | 
						||
								+ ' s');
 | 
						||
					messages.push(elapsed === 0 ? ', 0 s elapsed.' : ', '
 | 
						||
							+ ((elapsed = recorder.all.length / elapsed) < 1
 | 
						||
							// Hz
 | 
						||
							? (1000 * elapsed).to_fixed(2) + ' tests/s.'
 | 
						||
									: elapsed.to_fixed(2) + ' tests/ms.'));
 | 
						||
					// console.trace(messages);
 | 
						||
					return messages.join('');
 | 
						||
				}
 | 
						||
 | 
						||
				var error_count = recorder.failed.length
 | 
						||
						+ recorder.fatal.length;
 | 
						||
				function finish() {
 | 
						||
					// 確保 callback 會在本函數之後執行。
 | 
						||
					// 最後執行setTimeout(options.callback),使options.callback在訊息顯示完之後才執行。
 | 
						||
					// 因為已 callback,自此後不應改變 recorder,否則不會被 callback 處理。
 | 
						||
					if (options && typeof options.callback === 'function') {
 | 
						||
						setTimeout(function() {
 | 
						||
							options.callback(recorder, error_count,
 | 
						||
									test_group_name);
 | 
						||
						}, 0);
 | 
						||
					}
 | 
						||
					return error_count;
 | 
						||
				}
 | 
						||
 | 
						||
				if (error_count === 0) {
 | 
						||
					// all passed. 測試通過。
 | 
						||
					messages.push(CeL.to_SGR([
 | 
						||
							'All ' + recorder.passed.length + ' ', 'fg=green',
 | 
						||
							'passed', '-fg' ]));
 | 
						||
 | 
						||
					log_front_end_info(join());
 | 
						||
					return finish();
 | 
						||
				}
 | 
						||
 | 
						||
				// not all passed.
 | 
						||
				messages.push(recorder.failed.length + '/'
 | 
						||
				//
 | 
						||
				+ (recorder.failed.length + recorder.passed.length));
 | 
						||
				if (recorder.failed.length + recorder.passed.length !== recorder.all.length)
 | 
						||
					messages.push('/' + recorder.all.length);
 | 
						||
				messages.push(CeL.to_SGR([ ' ', 'fg=red', 'failed', '-fg' ]));
 | 
						||
				if (recorder.fatal.length > 0) {
 | 
						||
					// fatal exception error 致命錯誤
 | 
						||
					messages.push(CeL.to_SGR([
 | 
						||
							', ' + recorder.fatal.length + ' ',
 | 
						||
							'fg=red;bg=white', 'fatal', '-fg;-bg' ]));
 | 
						||
				}
 | 
						||
 | 
						||
				// 不採用 log_controller,在 console 會出現奇怪的著色。
 | 
						||
				// e.g., @ Travis CI
 | 
						||
				if (recorder.passed.length > 0) {
 | 
						||
					// CeL.warn(join());
 | 
						||
					log_controller[2](join());
 | 
						||
				} else {
 | 
						||
					// CeL.error(join());
 | 
						||
					log_controller[3](join()
 | 
						||
					// hack: fg=whilte → fg=red
 | 
						||
					.replace(/\x1B\[37m/g, '\x1B[31m'));
 | 
						||
				}
 | 
						||
 | 
						||
				return finish();
 | 
						||
			}
 | 
						||
 | 
						||
			assert_proxy.test_group_name = test_group_name;
 | 
						||
			assert_proxy.report = report;
 | 
						||
			assert_proxy.options = default_options;
 | 
						||
			assert_proxy.starts = Date.now();
 | 
						||
 | 
						||
			// --------------------------------
 | 
						||
			// ready to go
 | 
						||
 | 
						||
			if (Array.isArray(conditions)) {
 | 
						||
				conditions.forEach(handler);
 | 
						||
			} else {
 | 
						||
				var tests_count = 0,
 | 
						||
				// assert: tests_count === Object.keys(tests_left).length
 | 
						||
				tests_left = assert_proxy.tests_left = Object.create(null),
 | 
						||
				// assert: typeof conditions === 'function'
 | 
						||
				setup_test = function setup_test(sub_test_name, test_function) {
 | 
						||
					// need wait (pending)
 | 
						||
					assert_proxy.asynchronous = true;
 | 
						||
					if (sub_test_name) {
 | 
						||
						assert_proxy.latest_sub_test_name = sub_test_name;
 | 
						||
						if (sub_test_name in tests_left) {
 | 
						||
							CeL.warn('已登記過任務組 [' + sub_test_name + ']。');
 | 
						||
							return true;
 | 
						||
						}
 | 
						||
						tests_count++;
 | 
						||
						tests_left[sub_test_name] = {
 | 
						||
							// sub_test_data
 | 
						||
							assert_count : 0
 | 
						||
						};
 | 
						||
						CeL.debug('增加任務組 [' + sub_test_name + ']。尚餘 '
 | 
						||
								+ tests_count + ' 個任務組測試中。', 1, 'CeL.log.test');
 | 
						||
					}
 | 
						||
					if (typeof test_function === 'function') {
 | 
						||
						try {
 | 
						||
							test_function(finish_test);
 | 
						||
						} catch (e) {
 | 
						||
							if (typeof console === 'object' && console.trace) {
 | 
						||
								// Will show stacks
 | 
						||
								console.trace(e);
 | 
						||
							}
 | 
						||
							recorder.fatal.push(sub_test_name);
 | 
						||
						}
 | 
						||
						if (sub_test_name)
 | 
						||
							finish_test(sub_test_name);
 | 
						||
					}
 | 
						||
				},
 | 
						||
				//
 | 
						||
				finish_test = function finish_test(sub_test_name) {
 | 
						||
					if (sub_test_name in tests_left) {
 | 
						||
						delete tests_left[sub_test_name];
 | 
						||
						tests_count--;
 | 
						||
					} else {
 | 
						||
						// 重複呼叫?
 | 
						||
						CeL.warn('已登記過完成測試任務組 [' + sub_test_name + ']。');
 | 
						||
						return;
 | 
						||
					}
 | 
						||
					CeL.debug('完成測試 [' + sub_test_name + ']。尚餘 ' + tests_count
 | 
						||
							+ ' 個任務組測試中。', 1, 'CeL.log.test');
 | 
						||
					// console.trace([ tests_count, tests_left ]);
 | 
						||
					if (tests_count === 0
 | 
						||
					// && CeL.is_empty_object(tests_left)
 | 
						||
					) {
 | 
						||
						if (assert_proxy.tests_loaded)
 | 
						||
							report(sub_test_name);
 | 
						||
						else
 | 
						||
							delete assert_proxy.asynchronous;
 | 
						||
					}
 | 
						||
				}, conditions_error = function(error) {
 | 
						||
					assert_proxy.asynchronous = false;
 | 
						||
					handler([ [ error, "OK" ], test_group_name ]);
 | 
						||
				};
 | 
						||
 | 
						||
				try {
 | 
						||
					var result = conditions(assert_proxy, setup_test,
 | 
						||
							finish_test, {
 | 
						||
								test_name : test_group_name
 | 
						||
							});
 | 
						||
					// allow async functions
 | 
						||
					if (CeL.is_thenable(result)) {
 | 
						||
						result.then(function() {
 | 
						||
							assert_proxy.tests_loaded = true;
 | 
						||
							// CeL.log('CeL.test: All tests loaded.');
 | 
						||
							if (tests_count > 0) {
 | 
						||
								CeL.error('CeL.test: ' + tests_count
 | 
						||
										+ ' sub test(s) still running: '
 | 
						||
										+ Object.keys(tests_left));
 | 
						||
							}
 | 
						||
							report();
 | 
						||
						}, function(error) {
 | 
						||
							console.trace(error);
 | 
						||
							conditions_error(error);
 | 
						||
						});
 | 
						||
						// Waiting...
 | 
						||
						return;
 | 
						||
					}
 | 
						||
 | 
						||
					assert_proxy.tests_loaded = true;
 | 
						||
					if (tests_count > 0) {
 | 
						||
						// console.trace([ tests_count, tests_left ]);
 | 
						||
						CeL.debug('尚餘 ' + tests_count + ' 個任務組測試中。', 1,
 | 
						||
								'CeL.log.test');
 | 
						||
					}
 | 
						||
				} catch (e) {
 | 
						||
					// has_console
 | 
						||
					if (typeof console === 'object' && console.error) {
 | 
						||
						// Warning: console.error() won't show stacks @ node
 | 
						||
						// v0.10
 | 
						||
						// console.trace(e) will show a wrong one.
 | 
						||
						if (e && e.stack && CeL.platform.nodejs
 | 
						||
								&& !CeL.platform('node', 8)) {
 | 
						||
							console.error(e.stack);
 | 
						||
						} else {
 | 
						||
							// Will show stacks
 | 
						||
							console.error(e);
 | 
						||
						}
 | 
						||
					}
 | 
						||
					conditions_error(e);
 | 
						||
				}
 | 
						||
			}
 | 
						||
 | 
						||
			if (!assert_proxy.asynchronous) {
 | 
						||
				return report();
 | 
						||
			} else {
 | 
						||
				// Waiting...
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		// 在 console 則沿用舊 function。
 | 
						||
		// 這裡的判別式與 base.js 中的相符: "_.to_SGR = is_WWW ? SGR_to_plain : to_SGR;"
 | 
						||
		// 因為 base.js 中的 styled log 也需要此條件才能發動。
 | 
						||
		// TODO: 增加 console 的 style (color)
 | 
						||
		if (CeL.is_WWW()) {
 | 
						||
			// 這裡列出的是 base.js 中即已提供,不設定也會由原先之預設函式處理的函式。
 | 
						||
			Object.assign(CeL, {
 | 
						||
				log : log_controller[1],
 | 
						||
				warn : log_controller[2],
 | 
						||
				error : log_controller[3],
 | 
						||
 | 
						||
				info : log_front_end_info,
 | 
						||
 | 
						||
				debug : log_front_end_debug
 | 
						||
			});
 | 
						||
		}
 | 
						||
 | 
						||
		Object.assign(CeL, {
 | 
						||
			em : function log_front_end_em(message, clean) {
 | 
						||
				// emphasis
 | 
						||
				CeL.Log.log.call(CeL.Log, message, clean, 'em');
 | 
						||
			},
 | 
						||
 | 
						||
			// 致命錯誤。
 | 
						||
			fatal : log_front_end_fatal,
 | 
						||
 | 
						||
			// 增加 debug 訊息。
 | 
						||
			trace : function log_front_end_trace() {
 | 
						||
				// 使用 .apply() 預防 override。
 | 
						||
				// trace: the least serious
 | 
						||
				CeL.debug.apply(CeL, arguments);
 | 
						||
			},
 | 
						||
 | 
						||
			// 切換(顯示/隱藏)個別訊息。
 | 
						||
			toggle_log : log_front_end_toggle_log,
 | 
						||
 | 
						||
			assert : log_front_end_assert,
 | 
						||
			// CeL.test()
 | 
						||
			test : log_front_end_test
 | 
						||
		});
 | 
						||
 | 
						||
		// 處理 loading 本 module 前即已 log 之 message。
 | 
						||
		if (Array.isArray(log_buffer) && log_buffer.length > 0) {
 | 
						||
			CeL.debug({
 | 
						||
				em : 'Before loading ' + module_name
 | 
						||
						+ ', there are some debugging message.'
 | 
						||
			});
 | 
						||
			log_buffer.forEach(function(message) {
 | 
						||
				CeL.debug(message);
 | 
						||
			});
 | 
						||
 | 
						||
			CeL.debug('<em>' + module_name + ' loaded.<\/em>');
 | 
						||
		}
 | 
						||
 | 
						||
	}
 | 
						||
}
 |