/**
 * @name	CeL log function
 * @fileoverview
 * 本檔案包含了記錄用 functions。
 * 
 * @since	2009/11/17
 * @see
 * Firebug Lite,
 * Venkman JavaScript Debugger project page
 */
//	http://blogs.msdn.com/b/webdevtools/archive/2007/03/02/jscript-intellisense-in-orcas.aspx
///	
 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
 
 */
/**
 * 
 to include:
 include code_for_including
 
 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/
 
 */
'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)
	/**
	 * 
	_ = this
	TODO:
	set class in each input
	input array
	show file path & directory functional	可從 FSO operation.hta 移植。
	count
	c.f.: GLog
	dependency:
	
	 */
	/**
	 * initial a log tool's instance/object
	 * 
	 * @class log function
	 * @_see usage: _module_.extend
	 * @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 : '!! Error !! '
		};
		/**
		 * 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) {
		/**
		 * 
		這段應該只在 module namespace 重複定義時才會發生
		var I = p[id];
		if (!I) {
			alert('.do_log: not exist: [' + id + ']');
			return;
		}
		I = I.instance;
		
		 */
		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
				+ ': '
				/**
				 * 
					http://msdn.microsoft.com/en-us/library/cc231198(PROT.10).aspx
					Winerror.h: error code definitions for the Win32 API functions
					(e.number & 0xFFFF): See 錯誤代碼 /錯誤提示碼 System Error Codes
					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)
				
				 */
				+ (e.number & 0xFFFF)
				+ (e.name ? ' [' + e.name + '] ' : ' ')
				+ '(facility code '
				+ (e.number >> 16 & 0x1FFF)
				+ '): '
				+ line_separator
				+ (e.message || '').replace(/\r?\n/g, '
')
				// .message 為主,.description 是舊的。
				+ (!e.description || e.description === e.message ? ''
						: line_separator
								+ line_separator
								+ ('' + e.description).replace(/\r?\n/g,
										'
'))
		: 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 += '
 '
							+ T
							+ ': '
							+ (typeof e[T] === 'string' && T === 'stack' ? e[T]
									.replace(/[\r\n]+$/, '')
									.replace(/(@)([a-z\-]+:\/\/.+)(:)(\d+)$/gm,
											'$1$2$3$4')
									.replace(/\n/g, '
- ')
									: typeof e[T] === 'string'
											&& T === 'fileName' ? ''
											+ e[T] + ''
											: 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(/
	// 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];
	
	 * @_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);
		} ];
	};
	/**
	 * 
	_.option_open=function(p){
	};
	_.option_file=function(p){
	};
	_.option_folder=function(p){
	};
	
	 */
	// class constructor ---------------------------
	_// JSDT:_module_
	.prototype = {
		// instance public interface -------------------
		/**
		 * 當執行寫檔案或任何錯誤發生時之錯誤訊息。
		 * 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 則直接顯示。
		 * e.g., 即時顯示,不延遲顯示: CeL.Log.interval = 0;
		 * (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;
			}
			/**
			 * 
					var message_head = (arguments.callee.caller + '')
							.match(/function\do_save_log([^\(]+)/);
					if (message_head)
						message_head = message_head[1] + ' ';
			
			 */
			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 */);
		},
		/**
		 * 
		 ** important ** 這邊不能作 object 之 initialization,否則因為 object 只會 copy reference,因此 new 時東西會一樣。initialization 得在 _() 中作!
		 
		 */
		// 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 '['
					+ diff_ms + '] ';
		},
		/**
		 * 當記錄太長時,限制記錄數目在 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;
		},
		/**
		 * 設定寫入到哪
		 * 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).
			 * http://codepoints.net/U+1F4DD http://wiki.livedoor.jp/qvarie/
			 */
			log : '📝',
			/**
			 * emphasized text
			 * U+2383 EMPHASIS SYMBOL
			 * http://codepoints.net/U+2383
			 */
			em : '⎃',
			/**
			 * 資訊,消息,報告,通知,情報
			 * WARNING SIGN (U+26A0) @ Miscellaneous Symbols.
			 */
			warn : '⚠',
			/**
			 * error / fault
			 * U+2620 SKULL AND CROSSBONES
			 */
			error : '☠',
			/**
			 * U+2139 INFORMATION SOURCE
			 * 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,會造成:
		 * 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
							+ '
stack:
		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();
		
		 * 
		 * @param {String}[test_group_name]
		 *            test name 此次測試名稱。
		 * @param {Array|Function}conditions
		 *            condition list passed to assert(): [ [ condition / test
		 *            value, options ], [], ... ].