mirror of
				https://scm.univ-tours.fr/22107988t/rappaurio-sae501_502.git
				synced 2025-11-04 01:55:21 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			1701 lines
		
	
	
		
			46 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			1701 lines
		
	
	
		
			46 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/**
 | 
						||
 * @name CeL file system functions
 | 
						||
 * @fileoverview 本檔案包含了 file system functions。
 | 
						||
 * @since 2013/1/5 9:38:34
 | 
						||
 * @see <a href="http://en.wikipedia.org/wiki/Filesystem" accessdate="2013/1/5
 | 
						||
 *      9:44">File system</a>
 | 
						||
 */
 | 
						||
 | 
						||
'use strict';
 | 
						||
 | 
						||
// --------------------------------------------------------------------------------------------
 | 
						||
 | 
						||
// 不採用 if 陳述式,可以避免 Eclipse JSDoc 與 format 多縮排一層。
 | 
						||
typeof CeL === 'function' && CeL.run({
 | 
						||
	// module name
 | 
						||
	name : 'data.file',
 | 
						||
 | 
						||
	// includes() @ data.code.compatibility.
 | 
						||
	require : 'data.code.compatibility.|application.OS.Windows.new_COM'
 | 
						||
	//
 | 
						||
	+ '|data.code.thread.Serial_execute',
 | 
						||
 | 
						||
	// 設定不匯出的子函式。
 | 
						||
	no_extend : '*',
 | 
						||
 | 
						||
	// 為了方便格式化程式碼,因此將 module 函式主體另外抽出。
 | 
						||
	code : module_code
 | 
						||
});
 | 
						||
 | 
						||
function module_code(library_namespace) {
 | 
						||
 | 
						||
	// requiring.
 | 
						||
	var new_COM = this.r('new_COM'), Serial_execute = this.r('Serial_execute');
 | 
						||
 | 
						||
	// ---------------------------------------------------------------------//
 | 
						||
	// 基本宣告與定義。
 | 
						||
 | 
						||
	var
 | 
						||
	// cache.
 | 
						||
	path_separator = library_namespace.env.path_separator,
 | 
						||
	/**
 | 
						||
	 * FileSystemObject
 | 
						||
	 * 
 | 
						||
	 * @inner
 | 
						||
	 * @ignore
 | 
						||
	 * @see <a
 | 
						||
	 *      href="http://msdn.microsoft.com/en-us/library/z9ty6h50(v=VS.84).aspx"
 | 
						||
	 *      accessdate="2010/1/9 8:10">FileSystemObject Object</a>, Scripting
 | 
						||
	 *      Run-Time Reference/FileSystemObject
 | 
						||
	 *      http://msdn.microsoft.com/en-US/library/hww8txat%28v=VS.84%29.aspx
 | 
						||
	 */
 | 
						||
	FSO = new_COM("Scripting.FileSystemObject");
 | 
						||
 | 
						||
	// 可 test FSO.
 | 
						||
 | 
						||
	var
 | 
						||
	// const flag enumeration: 用於指示 type.
 | 
						||
	// assert: (!!type) MUST true!
 | 
						||
	FILE = 1, FOLDER = 2,
 | 
						||
 | 
						||
	// const flag enumeration: file/folder callback index.
 | 
						||
	FILE_HANDLER_INDEX = 0, FOLDER_HANDLER_INDEX = 1,
 | 
						||
 | 
						||
	// const: sub files.
 | 
						||
	// 必須是不會被用於目錄名之值。
 | 
						||
	FILES = '',
 | 
						||
	// TODO: file/directory status/infomation, even contents.
 | 
						||
	// 必須是不會被用於目錄名之值。
 | 
						||
	DATA = '.',
 | 
						||
 | 
						||
	// 預設最多處理之 folder 層數。
 | 
						||
	// directory depth limit.
 | 
						||
	default_depth = 64,
 | 
						||
 | 
						||
	// const flag enumeration: default property data.
 | 
						||
	fso_property = {
 | 
						||
		file : {
 | 
						||
			Attributes : '',
 | 
						||
			DateCreated : '',
 | 
						||
			DateLastAccessed : '',
 | 
						||
			DateLastModified : '',
 | 
						||
			Drive : '',
 | 
						||
			Name : '',
 | 
						||
			ParentFolder : '',
 | 
						||
			Path : '',
 | 
						||
			ShortName : '',
 | 
						||
			ShortPath : '',
 | 
						||
			Size : '',
 | 
						||
			Type : ''
 | 
						||
		},
 | 
						||
		folder : {
 | 
						||
			Attributes : '',
 | 
						||
			DateCreated : '',
 | 
						||
			DateLastAccessed : '',
 | 
						||
			DateLastModified : '',
 | 
						||
			Drive : '',
 | 
						||
			Name : '',
 | 
						||
			ParentFolder : '',
 | 
						||
			Path : '',
 | 
						||
			ShortName : '',
 | 
						||
			ShortPath : '',
 | 
						||
			Size : '',
 | 
						||
			Type : '',
 | 
						||
			Files : '',
 | 
						||
			IsRootFolder : '',
 | 
						||
			SubFolders : ''
 | 
						||
		},
 | 
						||
		driver : {
 | 
						||
			AvailableSpace : '',
 | 
						||
			DriveLetter : '',
 | 
						||
			DriveType : '',
 | 
						||
			FileSystem : '',
 | 
						||
			FreeSpace : '',
 | 
						||
			IsReady : '',
 | 
						||
			Path : '',
 | 
						||
			RootFolder : '',
 | 
						||
			SerialNumber : '',
 | 
						||
			ShareName : '',
 | 
						||
			TotalSize : '',
 | 
						||
			VolumeName : ''
 | 
						||
		}
 | 
						||
	};
 | 
						||
 | 
						||
	// a network drive.
 | 
						||
	// http://msdn.microsoft.com/en-us/library/ys4ctaz0(v=vs.84).aspx
 | 
						||
	// NETWORK_DRIVE = 3,
 | 
						||
 | 
						||
	/**
 | 
						||
	 * 取得指定 path 之檔名/資料夾名稱。
 | 
						||
	 * 
 | 
						||
	 * @param {String}path
 | 
						||
	 *            指定之目標路徑。
 | 
						||
	 * @returns {String} 檔名/資料夾名稱。
 | 
						||
	 * @inner
 | 
						||
	 */
 | 
						||
	function name_of_path(path) {
 | 
						||
		var match = typeof path === 'string' && path.match(/[^\\\/]+/);
 | 
						||
		return match && match[0] || path || '';
 | 
						||
	}
 | 
						||
 | 
						||
	/**
 | 
						||
	 * 傳回新的資料夾結構。
 | 
						||
	 * 
 | 
						||
	 * @returns 新的資料夾結構。
 | 
						||
	 * @inner
 | 
						||
	 */
 | 
						||
	function new_folder() {
 | 
						||
		var folder = Object.create(null);
 | 
						||
		// 檔案, sub-files.
 | 
						||
		folder[FILES] = Object.create(null);
 | 
						||
		// directory status/infomation.
 | 
						||
		folder[DATA] = Object.create(null);
 | 
						||
		return folder;
 | 
						||
	}
 | 
						||
 | 
						||
	// ---------------------------------------------------------------------//
 | 
						||
	// filter 處理。
 | 
						||
 | 
						||
	/**
 | 
						||
	 * options 所使用到的 filter name。
 | 
						||
	 */
 | 
						||
	var regular_filter_name = {
 | 
						||
		// path filter.
 | 
						||
		// 通常我們輸入的只會指定 path filter。
 | 
						||
		filter : 0,
 | 
						||
		// file name filter. 篩選檔案.
 | 
						||
		// WARNING: 若有設定,只要檔名不符合,即使 folder name 符合亦一樣會被剔除!
 | 
						||
		file_filter : 1,
 | 
						||
		// folder name filter. 篩選資料夾.
 | 
						||
		folder_filter : 2
 | 
						||
	};
 | 
						||
 | 
						||
	/**
 | 
						||
	 * 判別是否為可接受之字串篩選器。<br />
 | 
						||
	 * filter should has NO global flag.
 | 
						||
	 * 
 | 
						||
	 * @param {undefined|String|RegExp|Function}filter
 | 
						||
	 *            字串篩選器。
 | 
						||
	 * 
 | 
						||
	 * @returns {Boolean} 為可接受之字串篩選器。
 | 
						||
	 */
 | 
						||
	function is_regular_filter(filter) {
 | 
						||
		return !filter || typeof filter === 'string'
 | 
						||
		// RegExp
 | 
						||
		|| library_namespace.is_RegExp(filter)
 | 
						||
		// function
 | 
						||
		|| typeof filter === 'function';
 | 
						||
	}
 | 
						||
 | 
						||
	/**
 | 
						||
	 * 檢測 options 的 filter。
 | 
						||
	 * 
 | 
						||
	 * @param {Object}options
 | 
						||
	 *            optional flag. e.g., filter.
 | 
						||
	 */
 | 
						||
	function check_filter_of_options(options) {
 | 
						||
		for ( var filter_name in regular_filter_name)
 | 
						||
			if ((filter_name in options)
 | 
						||
					&& !is_regular_filter(options[filter_name]))
 | 
						||
				delete options[filter_name];
 | 
						||
	}
 | 
						||
 | 
						||
	/** {Number}未發現之index。 const: 基本上與程式碼設計合一,僅表示名義,不可更改。(=== -1) */
 | 
						||
	var NOT_FOUND = ''.indexOf('_');
 | 
						||
 | 
						||
	/**
 | 
						||
	 * 判斷指定字串是否合乎篩選。
 | 
						||
	 * 
 | 
						||
	 * @param {undefined|String|RegExp|Function}filter
 | 
						||
	 *            字串篩選器。
 | 
						||
	 * @param {String}string
 | 
						||
	 *            欲測試之指定字串。string to test.
 | 
						||
	 * @param [argument]
 | 
						||
	 *            當篩選器為 function 時之附加引數。
 | 
						||
	 * 
 | 
						||
	 * @returns {Boolean} 為可接受之字串篩選器。
 | 
						||
	 */
 | 
						||
	function match_filter(filter, string, argument) {
 | 
						||
		return !filter
 | 
						||
		//
 | 
						||
		|| (typeof filter === 'string'
 | 
						||
		// String
 | 
						||
		? string.includes(filter)
 | 
						||
		// RegExp
 | 
						||
		: filter.test ? filter.test(string)
 | 
						||
		// function
 | 
						||
		: filter(string, argument));
 | 
						||
	}
 | 
						||
 | 
						||
	// ---------------------------------------------------------------------//
 | 
						||
	// 檔案系統模擬結構。
 | 
						||
 | 
						||
	/**
 | 
						||
	 * 建立模擬檔案系統結構 (file system structure) 之 Class。取得檔案列表。<br />
 | 
						||
	 * 注意: 在此設定的 callback,不具有防呆滯功能。<br />
 | 
						||
	 * TODO: fs.readdir or fs.readdirSync @ node.js<br />
 | 
						||
	 * TODO: follow link
 | 
						||
	 * 
 | 
						||
	 * @example <code>
 | 
						||
	//	列出指定目錄下所有壓縮檔。
 | 
						||
	CeL.run('data.file', function() {
 | 
						||
		var folder = new CeL.file_system_structure('D:\\a', { file_filter : /\.(zip|rar|7z|exe)$/i });
 | 
						||
		folder.each(function(fso, info) {
 | 
						||
			CeL[info.is_file ? 'log' : 'info']([ info.index, '/', info.length, '[', fso.Path, ']' ]); }, {
 | 
						||
				// filter : 'f',
 | 
						||
				max_count : 5,
 | 
						||
				'final' : function() { CeL.log([ this.count.filtered_file, ' done']); }
 | 
						||
			}
 | 
						||
		);
 | 
						||
	});
 | 
						||
	</code>
 | 
						||
	 * 
 | 
						||
	 * @param {String|Array}path
 | 
						||
	 *            指定之目標路徑。<br />
 | 
						||
	 *            使用相對路徑,如 '..' 開頭時,須用 get_file_path() 調整過。
 | 
						||
	 * @param {Object}[options]
 | 
						||
	 *            optional flag. e.g., filter.
 | 
						||
	 * 
 | 
						||
	 * @constructor
 | 
						||
	 * 
 | 
						||
	 * @see <a
 | 
						||
	 *      href="http://msdn.microsoft.com/library/en-US/script56/html/0fa93e5b-b657-408d-9dd3-a43846037a0e.asp">FileSystemObject</a>
 | 
						||
	 * 
 | 
						||
	 * @since 2013/1/6 18:57:16 堪用。
 | 
						||
	 */
 | 
						||
	function file_system_structure(path, options) {
 | 
						||
		if (this === file_system_structure)
 | 
						||
			throw 'Please use "new file_system_structure()"'
 | 
						||
					+ ' instead of "file_system_structure()"!';
 | 
						||
 | 
						||
		// private properties.
 | 
						||
		this.structure = new_folder();
 | 
						||
		this.count = Object.create(null);
 | 
						||
		this.path_list = [];
 | 
						||
 | 
						||
		this.add(path, options);
 | 
						||
	}
 | 
						||
 | 
						||
	/**
 | 
						||
	 * 解析/取得模擬結構中,指定 path 所在位置。
 | 
						||
	 * 
 | 
						||
	 * @param {String}path
 | 
						||
	 *            指定之目標路徑。
 | 
						||
	 * @param {Number}create_type
 | 
						||
	 *            以 FILE or FOLDER 創建此標的。
 | 
						||
	 * 
 | 
						||
	 * @returns {Object} 檔案系統模擬結構中,指定 path 所在位置。
 | 
						||
	 * @returns undefined error occurred.
 | 
						||
	 */
 | 
						||
	function resolve_path(path, create_type) {
 | 
						||
		var base = this.structure;
 | 
						||
 | 
						||
		if (path && typeof path === 'string') {
 | 
						||
			var name, i = 0, list = library_namespace.simplify_path(path)
 | 
						||
					.replace(/[\\\/]+$/, '');
 | 
						||
			if (name = list.match(/^\\\\[^\\]+/)) {
 | 
						||
				// a network drive.
 | 
						||
				name = name[0];
 | 
						||
				list = list.slice(name.length).split(/[\\]+/);
 | 
						||
				list[0] = name;
 | 
						||
			} else
 | 
						||
				list = list.split(/[\\\/]+/);
 | 
						||
 | 
						||
			for (; i < list.length; i++) {
 | 
						||
				name = list[i];
 | 
						||
				if (name in base)
 | 
						||
					base = base[name];
 | 
						||
				else if (!create_type)
 | 
						||
					return undefined;
 | 
						||
 | 
						||
				else if (create_type === FOLDER
 | 
						||
				// 不是最後一個。
 | 
						||
				|| i + 1 < list.length)
 | 
						||
					base = base[name] = new_folder();
 | 
						||
				else
 | 
						||
					base[FILES][name] = null;
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		return base;
 | 
						||
	}
 | 
						||
 | 
						||
	/**
 | 
						||
	 * 將 fso 所有 data_fields 中的項目 extend 到 data 中。
 | 
						||
	 * 
 | 
						||
	 * @inner
 | 
						||
	 * @private
 | 
						||
	 * 
 | 
						||
	 * @param {file_system_object}fso
 | 
						||
	 *            file system object.
 | 
						||
	 * @param {Object}data_fields
 | 
						||
	 *            欲 extend 之項目。
 | 
						||
	 * @param {檔案系統模擬結構}data
 | 
						||
	 *            所在位置。
 | 
						||
	 * 
 | 
						||
	 * @returns {Number} extend 之項目數。
 | 
						||
	 */
 | 
						||
	function fill_data(fso, data_fields, data) {
 | 
						||
		var name,
 | 
						||
		// {Number} extend 之項目數。
 | 
						||
		// count = 0,
 | 
						||
		item;
 | 
						||
		for (name in data_fields)
 | 
						||
			try {
 | 
						||
				// Get the infomation/status of fso.
 | 
						||
				if ((item = fso[name]) !== undefined) {
 | 
						||
					data[name]
 | 
						||
					//
 | 
						||
					= typeof data_fields[name] === 'function'
 | 
						||
					//
 | 
						||
					? data_fields[name](item) : item;
 | 
						||
					// count++;
 | 
						||
				}
 | 
						||
			} catch (e) {
 | 
						||
				// 在取得 RootFolder 的 .DateCreated,
 | 
						||
				// .DateLastAccessed, .DateLastModified 時,會
 | 
						||
				// throw。
 | 
						||
				if (library_namespace.is_debug(3)) {
 | 
						||
					library_namespace.warn('fill_data: 擷取資訊 [' + name
 | 
						||
							+ '] 時發生錯誤!');
 | 
						||
					library_namespace.error(e);
 | 
						||
				}
 | 
						||
			}
 | 
						||
		// return count;
 | 
						||
		return data;
 | 
						||
	}
 | 
						||
 | 
						||
	/**
 | 
						||
	 * 將指定 path 加入模擬結構。<br />
 | 
						||
	 * 注意: base path 本身不受 filter 限制!
 | 
						||
	 * 
 | 
						||
	 * @param {String|Array}path
 | 
						||
	 *            指定之目標路徑。<br />
 | 
						||
	 *            使用相對路徑,如 '..' 開頭時,須用 get_file_path() 調整過。
 | 
						||
	 * @param {Object}[options]
 | 
						||
	 *            optional flag. e.g., filter.
 | 
						||
	 */
 | 
						||
	function add_path(path, options) {
 | 
						||
 | 
						||
		library_namespace.debug('初始化+正規化。', 2, 'add_path');
 | 
						||
		// 前置處理。
 | 
						||
		if (!library_namespace.is_Object(options))
 | 
						||
			options = Object.create(null);
 | 
						||
		if (isNaN(options.depth) || options.depth < 0
 | 
						||
		// || options.depth > default_depth
 | 
						||
		)
 | 
						||
			options.depth = default_depth;
 | 
						||
 | 
						||
		check_filter_of_options(options);
 | 
						||
 | 
						||
		var callback_Array;
 | 
						||
		if ('callback' in options) {
 | 
						||
			callback_Array = options.callback;
 | 
						||
			if (typeof callback_Array === 'function')
 | 
						||
				options.callback = callback_Array = [ callback_Array,
 | 
						||
						callback_Array ];
 | 
						||
			else if (!Array.isArray(callback_Array)
 | 
						||
					|| callback_Array.length === 0) {
 | 
						||
				delete options.callback;
 | 
						||
				callback_Array = undefined;
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		// for Array [path].
 | 
						||
		if (Array.isArray(path)) {
 | 
						||
			path.forEach(function(p) {
 | 
						||
				this.add(p, options);
 | 
						||
			}, this);
 | 
						||
			return;
 | 
						||
		}
 | 
						||
 | 
						||
		var base = this.structure;
 | 
						||
		if (!path) {
 | 
						||
			library_namespace.debug(
 | 
						||
			// 省略 path 會當作所有 Drivers。
 | 
						||
			'取得各個 driver code。', 2, 'add_path');
 | 
						||
			for (var driver, drivers = new Enumerator(FSO.Drives);
 | 
						||
			//
 | 
						||
			!drivers.atEnd(); drivers.moveNext()) {
 | 
						||
				driver = drivers.item();
 | 
						||
				// http://msdn.microsoft.com/en-us/library/ts2t8ybh(v=vs.84).aspx
 | 
						||
				if (driver.IsReady)
 | 
						||
					base[driver.Path] = new_folder();
 | 
						||
				else
 | 
						||
					library_namespace.warn('add_path: Drive [' + driver.Path
 | 
						||
							+ '] 尚未就緒!');
 | 
						||
			}
 | 
						||
 | 
						||
			return;
 | 
						||
		}
 | 
						||
 | 
						||
		var fso,
 | 
						||
		// 類型
 | 
						||
		type;
 | 
						||
		// 轉換輸入之 path 成 FSO object。
 | 
						||
		try {
 | 
						||
			if (typeof path === 'string') {
 | 
						||
				// 注意: 輸入 "C:" 會得到 C: 的工作目錄。
 | 
						||
				if (FSO.FolderExists(path)) {
 | 
						||
					fso = FSO.GetFolder(path);
 | 
						||
					type = FOLDER;
 | 
						||
				} else if (FSO.FileExists(path)) {
 | 
						||
					fso = FSO.GetFile(path);
 | 
						||
					type = FILE;
 | 
						||
				}
 | 
						||
 | 
						||
			} else if (typeof path === 'object' && path.Path) {
 | 
						||
				fso = isNaN(path.DriveType) ? path : FSO.GetFolder(path.Path);
 | 
						||
				type = fso.SubFolders ? FOLDER : FILE;
 | 
						||
			}
 | 
						||
		} catch (e) {
 | 
						||
			library_namespace.error(e);
 | 
						||
		}
 | 
						||
 | 
						||
		if (typeof fso !== 'object' || !(path = fso.Path)) {
 | 
						||
			library_namespace.warn('add_path: 無法判別 path [' + path
 | 
						||
					+ ']!指定的文件不存在?');
 | 
						||
			return;
 | 
						||
		}
 | 
						||
 | 
						||
		var list = this.path_list;
 | 
						||
		for (var i = 0; i < list.length; i++)
 | 
						||
			if (list[i].startsWith(path)) {
 | 
						||
				library_namespace.debug('已處理過 path [' + path + ']。', 2,
 | 
						||
						'add_path');
 | 
						||
				return;
 | 
						||
			}
 | 
						||
 | 
						||
		library_namespace.debug(
 | 
						||
		//
 | 
						||
		'Adding [' + path + ']', 2, 'add_path');
 | 
						||
		list.push(path);
 | 
						||
 | 
						||
		if (base = callback_Array && callback_Array[
 | 
						||
		//
 | 
						||
		type === FOLDER ? FOLDER_HANDLER_INDEX : FILE_HANDLER_INDEX])
 | 
						||
			try {
 | 
						||
				base(fso, {
 | 
						||
					depth : 0,
 | 
						||
					is_file : type !== FOLDER
 | 
						||
				});
 | 
						||
			} catch (e) {
 | 
						||
				library_namespace.error(e);
 | 
						||
			}
 | 
						||
 | 
						||
		base = this.get(path, type);
 | 
						||
 | 
						||
		var filter = options.filter,
 | 
						||
		//
 | 
						||
		file_filter = options.file_filter,
 | 
						||
		//
 | 
						||
		folder_filter = options.folder_filter,
 | 
						||
		//
 | 
						||
		folder_first = !!options.folder_first,
 | 
						||
 | 
						||
		//
 | 
						||
		count = this.count,
 | 
						||
		// 注意: assert(undefined|0===0)
 | 
						||
		total_size = count.size | 0,
 | 
						||
		//
 | 
						||
		total_file_count = count.file | 0,
 | 
						||
		//
 | 
						||
		total_folder_count = count.folder | 0,
 | 
						||
		//
 | 
						||
		filtered_total_file_count = count.filtered_file | 0,
 | 
						||
		//
 | 
						||
		filtered_total_folder_count = count.filtered_folder | 0,
 | 
						||
 | 
						||
		//
 | 
						||
		file_data_fields = library_namespace.is_Object(options.data)
 | 
						||
		//
 | 
						||
		? options.data : options.data === true ? fso_property.file : false,
 | 
						||
		//
 | 
						||
		folder_data_fields = library_namespace.is_Object(options.folder_data) ? options.folder_data
 | 
						||
				//
 | 
						||
				: options.folder_data === true || options.data === true
 | 
						||
				//
 | 
						||
				? fso_property.folder : file_data_fields,
 | 
						||
 | 
						||
		// 巡覽/遍歷子目錄與所包含的檔案。
 | 
						||
		traverse = function(fso, base, depth) {
 | 
						||
			var item, collection, name, callback,
 | 
						||
			//
 | 
						||
			folder_data = base[DATA],
 | 
						||
			// 有無加入 file count 功能,在 JScript 10.0.16438 差別不到 4%。
 | 
						||
			// 為求方便,不如皆加入。
 | 
						||
			size = 0,
 | 
						||
			//
 | 
						||
			file_count = 0, folder_count = 0,
 | 
						||
			//
 | 
						||
			filtered_file_count = 0, filtered_folder_count = 0,
 | 
						||
 | 
						||
			// list files of folder. 所包含的檔案.
 | 
						||
			each_file = function() {
 | 
						||
				library_namespace.debug('巡覽 [' + fso.Path + '] 之 sub-files。',
 | 
						||
						3, 'add_path');
 | 
						||
				try {
 | 
						||
					for (collection = new Enumerator(fso.Files);
 | 
						||
					//
 | 
						||
					!collection.atEnd(); collection.moveNext()) {
 | 
						||
						item = collection.item();
 | 
						||
						file_count++;
 | 
						||
						size += item.Size;
 | 
						||
 | 
						||
						if (match_filter(filter, item.Path, depth)
 | 
						||
								&& match_filter(file_filter, name = item.Name,
 | 
						||
										depth)) {
 | 
						||
							++filtered_file_count;
 | 
						||
							library_namespace.debug(
 | 
						||
							//
 | 
						||
							'Adding sub-file [' + filtered_file_count + '/'
 | 
						||
									+ file_count + '] [' + item.Path + ']', 4,
 | 
						||
									'add_path');
 | 
						||
							base[FILES][name] = file_data_fields
 | 
						||
							//
 | 
						||
							? fill_data(item, file_data_fields, Object
 | 
						||
									.create(null)) : null;
 | 
						||
							// 預防 callback 動到 item,排在最後才處理。
 | 
						||
							if (callback = callback_Array
 | 
						||
							//
 | 
						||
							&& callback_Array[FILE_HANDLER_INDEX])
 | 
						||
								try {
 | 
						||
									callback(item, {
 | 
						||
										depth : depth,
 | 
						||
										is_file : true
 | 
						||
									});
 | 
						||
								} catch (e) {
 | 
						||
									library_namespace.error(e);
 | 
						||
								}
 | 
						||
						}
 | 
						||
					}
 | 
						||
				} catch (e) {
 | 
						||
					// TODO: handle exception
 | 
						||
				}
 | 
						||
				total_size += size;
 | 
						||
				total_file_count += file_count;
 | 
						||
				filtered_total_file_count += filtered_file_count;
 | 
						||
			},
 | 
						||
 | 
						||
			// 處理子目錄。
 | 
						||
			each_folder = function() {
 | 
						||
				library_namespace.debug('巡覽 [' + fso.Path + '] 之 sub-folders。',
 | 
						||
						3, 'add_path');
 | 
						||
				// 執行途中可能更動/刪除到此項目。try: 以防 item 已經被刪除。
 | 
						||
				try {
 | 
						||
					// 因為檔案可能因改名等改變順序,因此用 .moveNext()
 | 
						||
					// 的方法可能有些重複,有些漏掉未處理。
 | 
						||
					for (collection = new Enumerator(fso.SubFolders);
 | 
						||
					//
 | 
						||
					!collection.atEnd(); collection.moveNext()) {
 | 
						||
						item = collection.item();
 | 
						||
						folder_count++;
 | 
						||
						if (match_filter(filter, item.Path, depth)
 | 
						||
						//
 | 
						||
						&& match_filter(folder_filter, name = item.Name, depth)) {
 | 
						||
							filtered_folder_count++;
 | 
						||
							library_namespace.debug(
 | 
						||
							//
 | 
						||
							'Adding sub-folder [' + filtered_folder_count + '/'
 | 
						||
									+ folder_count + '] [' + item.Path + ']',
 | 
						||
									4, 'add_path');
 | 
						||
							name = base[name] = new_folder();
 | 
						||
 | 
						||
							if (callback = callback_Array
 | 
						||
							//
 | 
						||
							&& callback_Array[FOLDER_HANDLER_INDEX])
 | 
						||
								try {
 | 
						||
									callback(item, {
 | 
						||
										depth : depth,
 | 
						||
										is_file : false
 | 
						||
									});
 | 
						||
								} catch (e) {
 | 
						||
									library_namespace.error(e);
 | 
						||
								}
 | 
						||
 | 
						||
							if (depth < options.depth)
 | 
						||
								traverse(item, name, depth);
 | 
						||
						}
 | 
						||
					}
 | 
						||
				} catch (e) {
 | 
						||
					// TODO: handle exception
 | 
						||
				}
 | 
						||
				// 可加上次一層: 會連這次一層之檔案都加上去。
 | 
						||
				total_folder_count += folder_count;
 | 
						||
				filtered_total_folder_count += filtered_folder_count;
 | 
						||
			};
 | 
						||
 | 
						||
			if (folder_data_fields)
 | 
						||
				fill_data(fso, folder_data_fields, folder_data);
 | 
						||
 | 
						||
			// 自身已經處理完,接下來 sub-files/sub-folders 之 depth 皆 +1。
 | 
						||
			depth++;
 | 
						||
 | 
						||
			if (folder_first) {
 | 
						||
				each_folder();
 | 
						||
				each_file();
 | 
						||
			} else {
 | 
						||
				each_file();
 | 
						||
				each_folder();
 | 
						||
			}
 | 
						||
 | 
						||
			library_namespace.debug('巡覽 [' + fso.Path + ']: ' + file_count
 | 
						||
					+ '/' + total_file_count + ' files, ' + size + '/'
 | 
						||
					+ total_size + ' bytes, ' + folder_count + '/'
 | 
						||
					+ total_folder_count + ' folders。', 2, 'add_path');
 | 
						||
 | 
						||
			folder_data.count = {
 | 
						||
				size : size,
 | 
						||
 | 
						||
				file : file_count,
 | 
						||
				folder : folder_count,
 | 
						||
 | 
						||
				filtered_file : filtered_file_count,
 | 
						||
				filtered_folder : filtered_folder_count
 | 
						||
			};
 | 
						||
		};
 | 
						||
 | 
						||
		if (type === FOLDER) {
 | 
						||
			if (options.depth > 0) {
 | 
						||
				library_namespace.debug('開始巡覽 [' + path + ']。', 2, 'add_path');
 | 
						||
				traverse(fso, base, 0);
 | 
						||
			}
 | 
						||
 | 
						||
			count.size = total_size;
 | 
						||
			count.file = total_file_count;
 | 
						||
			// +1: base path 本身。
 | 
						||
			count.folder = total_folder_count + 1;
 | 
						||
			count.filtered_file = filtered_total_file_count;
 | 
						||
			count.filtered_folder = filtered_total_folder_count + 1;
 | 
						||
 | 
						||
		} else {
 | 
						||
			count.size = total_size + fso.Size;
 | 
						||
			// +1: base path 本身。
 | 
						||
			count.file = total_file_count + 1;
 | 
						||
			count.folder = total_folder_count;
 | 
						||
			// +1: base path 本身。
 | 
						||
			count.filtered_file = filtered_total_file_count + 1;
 | 
						||
			count.filtered_folder = filtered_total_folder_count;
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	/**
 | 
						||
	 * 重新整理/同步模擬結構。
 | 
						||
	 * 
 | 
						||
	 * @param {String|Array}path
 | 
						||
	 *            指定之目標路徑。
 | 
						||
	 * @param {Object}[options]
 | 
						||
	 *            optional flag. e.g., filter.
 | 
						||
	 */
 | 
						||
	function refresh_structure(path, options) {
 | 
						||
		// TODO: 勿 reset。
 | 
						||
		this.structure = new_folder();
 | 
						||
 | 
						||
		this.path_list.forEach(function(p) {
 | 
						||
			this.add(p, options);
 | 
						||
		}, this);
 | 
						||
	}
 | 
						||
 | 
						||
	/**
 | 
						||
	 * 當呼叫 JSON.stringify() 時的前置處理。
 | 
						||
	 */
 | 
						||
	function structure_to_JSON(key) {
 | 
						||
		// hierarchy:
 | 
						||
		// {
 | 
						||
		// FILES:[],
 | 
						||
		// DATA:{},
 | 
						||
		// 資料夾名稱(sub directory name):{}
 | 
						||
		// };
 | 
						||
		var structure = Object.create(null),
 | 
						||
		//
 | 
						||
		traverse = function(folder, base) {
 | 
						||
			if (folder[FILES].length)
 | 
						||
				base[FILES] = folder[FILES];
 | 
						||
 | 
						||
			for ( var name in folder)
 | 
						||
				if (name !== FILES && name !== DATA)
 | 
						||
					traverse(folder[name], base[name] = Object.create(null));
 | 
						||
		};
 | 
						||
 | 
						||
		traverse(this.structure, structure);
 | 
						||
 | 
						||
		return structure;
 | 
						||
	}
 | 
						||
 | 
						||
	// ---------------------------------------------------------------------//
 | 
						||
	// 巡覽/遍歷檔案系統模擬結構之功能。
 | 
						||
 | 
						||
	/**
 | 
						||
	 * 處理執行 callback 發生錯誤時的函數。
 | 
						||
	 * 
 | 
						||
	 * @inner
 | 
						||
	 * @private
 | 
						||
	 * 
 | 
						||
	 * @since 2013/1/7 22:38:47。
 | 
						||
	 */
 | 
						||
	function callback_error_handler(controller, error_Object, options, stack) {
 | 
						||
		var message = [ '執行 callback 時發生錯誤!您可' ],
 | 
						||
		//
 | 
						||
		skip_error = options.skip_error;
 | 
						||
		if (skip_error)
 | 
						||
			message.push({
 | 
						||
				a : '停止執行',
 | 
						||
				href : '#',
 | 
						||
				onclick : function() {
 | 
						||
					controller.stop();
 | 
						||
				}
 | 
						||
			}, ',或者', {
 | 
						||
				// 忽略錯誤
 | 
						||
				a : '不再提醒錯誤',
 | 
						||
				href : '#',
 | 
						||
				onclick : function() {
 | 
						||
					delete options.skip_error;
 | 
						||
				}
 | 
						||
			});
 | 
						||
		else
 | 
						||
			message.push({
 | 
						||
				a : '重試',
 | 
						||
				href : '#',
 | 
						||
				onclick : function() {
 | 
						||
					stack.index--;
 | 
						||
					controller.start();
 | 
						||
				}
 | 
						||
			}, '、', {
 | 
						||
				a : '跳過並繼續執行',
 | 
						||
				href : '#',
 | 
						||
				onclick : function() {
 | 
						||
					controller.start();
 | 
						||
				}
 | 
						||
			}, ',或者', {
 | 
						||
				a : '忽略所有錯誤',
 | 
						||
				href : '#',
 | 
						||
				title : '★若忽略所有錯誤,之後發生錯誤將不會停止,而會直接忽略。',
 | 
						||
				onclick : function() {
 | 
						||
					options.skip_error = true;
 | 
						||
					controller.start();
 | 
						||
				}
 | 
						||
			});
 | 
						||
		message.push('。');
 | 
						||
		CeL.warn(message);
 | 
						||
 | 
						||
		if (error_Object)
 | 
						||
			library_namespace.error(error_Object);
 | 
						||
 | 
						||
		if (!skip_error) {
 | 
						||
			controller.stop();
 | 
						||
			return true;
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	/**
 | 
						||
	 * 巡覽/遍歷檔案系統模擬結構時,實際處理的函數。
 | 
						||
	 * 
 | 
						||
	 * @param {Array}LIFO_stack
 | 
						||
	 *            處理堆疊。
 | 
						||
	 * @param {file_system_structure}_this
 | 
						||
	 *            file_system_structure instance.
 | 
						||
	 * @param {Array}callback_Array
 | 
						||
	 *            callback_Array[]
 | 
						||
	 * @param {Object}options
 | 
						||
	 *            optional flag. e.g., filter.
 | 
						||
	 * 
 | 
						||
	 * @inner
 | 
						||
	 * @private
 | 
						||
	 * 
 | 
						||
	 * @since 2013/1/6 18:57:16 堪用。
 | 
						||
	 */
 | 
						||
	function travel_handler(LIFO_stack, _this,
 | 
						||
	//
 | 
						||
	callback_Array, options) {
 | 
						||
 | 
						||
		// 每個 folder 會跑一次 travel_handler。
 | 
						||
 | 
						||
		// 處理順序:
 | 
						||
		// 處理/執行 folder 本身
 | 
						||
		// → expand sub-files
 | 
						||
		// → 處理/執行 sub-files (若檔案過多會跳出至下一循環處理)
 | 
						||
		// → expand sub-folders
 | 
						||
		// → 下一循環 from sub-folder[0]。
 | 
						||
 | 
						||
		var stack, base_space, base_path,
 | 
						||
		//
 | 
						||
		path, name, depth, path_exists;
 | 
						||
 | 
						||
		if (LIFO_stack.length > 0) {
 | 
						||
			var callback, index;
 | 
						||
			stack = LIFO_stack.at(-1);
 | 
						||
			// 若有設定 stack.path,必以 path_separator 作結。
 | 
						||
			base_path = stack.path || '';
 | 
						||
 | 
						||
			if (!stack.is_file)
 | 
						||
				while (stack.index < stack.length) {
 | 
						||
					name = stack[stack.index++];
 | 
						||
					if (FSO.FolderExists(path = base_path
 | 
						||
					//
 | 
						||
					+ name
 | 
						||
					// 若有設定 stack.path,必以 path_separator 作結。
 | 
						||
					+ path_separator)) {
 | 
						||
						base_path = path;
 | 
						||
						path_exists = true;
 | 
						||
						base_space = stack.base[name];
 | 
						||
						library_namespace.debug('處理/執行 folder [' + path
 | 
						||
								+ '] 本身。', 2, 'travel_handler');
 | 
						||
						// 判別是否在標的區域內。
 | 
						||
						// depth: 正處理之 folder 本身的深度。
 | 
						||
						if (isNaN(depth = stack.depth))
 | 
						||
							if (stack.length > 1) {
 | 
						||
								stack.depth = depth = 0;
 | 
						||
							} else
 | 
						||
								for (var i = 0,
 | 
						||
								//
 | 
						||
								path_list = _this.path_list;
 | 
						||
								//
 | 
						||
								i < path_list.length; i++)
 | 
						||
									if (path.startsWith(path_list[i])) {
 | 
						||
										stack.depth = depth = 0;
 | 
						||
										break;
 | 
						||
									}
 | 
						||
 | 
						||
						if (!isNaN(depth)) {
 | 
						||
							// 無論有無 match filter,皆應計數。
 | 
						||
							index = options.single_count
 | 
						||
							//
 | 
						||
							? options.index++ : options.folder_index++;
 | 
						||
 | 
						||
							if ((callback
 | 
						||
							//
 | 
						||
							= callback_Array[FOLDER_HANDLER_INDEX])
 | 
						||
									&& match_filter(options.filter, path, depth)
 | 
						||
									&& match_filter(options.folder_filter,
 | 
						||
											name, depth))
 | 
						||
								try {
 | 
						||
									/**
 | 
						||
									 * 處理/執行資料夾本身。 用 folder.Size==0 可判別是否為 empty
 | 
						||
									 * folder。
 | 
						||
									 * 
 | 
						||
									 * @see <a
 | 
						||
									 *      href="http://msdn.microsoft.com/en-us/library/1c87day3(v=vs.84).aspx"
 | 
						||
									 *      accessdate="2013/1/5 12:43">Folder
 | 
						||
									 *      Object</a>
 | 
						||
									 */
 | 
						||
									callback.call(this, FSO.GetFolder(path), {
 | 
						||
										is_file : false,
 | 
						||
										depth : depth,
 | 
						||
										data : base_space[DATA],
 | 
						||
										index : index,
 | 
						||
										length : options.folder_count
 | 
						||
									});
 | 
						||
								} catch (e) {
 | 
						||
									if (callback_error_handler(this, e,
 | 
						||
											options, stack))
 | 
						||
										return;
 | 
						||
								}
 | 
						||
						}
 | 
						||
 | 
						||
						// expand sub-files.
 | 
						||
						// TODO: check fso.SubFolders
 | 
						||
						if (callback_Array[FILE_HANDLER_INDEX]
 | 
						||
								&& depth < options.depth) {
 | 
						||
							stack = [];
 | 
						||
							for (name in
 | 
						||
							//
 | 
						||
							(stack.base = base_space[FILES]))
 | 
						||
								stack.push(name);
 | 
						||
 | 
						||
							if (stack.length > 0) {
 | 
						||
								if (options.sort)
 | 
						||
									if (typeof options.sort
 | 
						||
									//
 | 
						||
									=== 'function')
 | 
						||
										stack.sort(options.sort);
 | 
						||
									else
 | 
						||
										stack.sort();
 | 
						||
 | 
						||
								library_namespace.debug('開始處理 [' + base_path
 | 
						||
										+ '] 之' + stack.length + ' sub-files ['
 | 
						||
										+ stack + '].', 2, 'travel_handler');
 | 
						||
								stack.index = 0;
 | 
						||
								stack.path = path;
 | 
						||
								stack.depth = depth + 1;
 | 
						||
								stack.is_file = true;
 | 
						||
								LIFO_stack.push(stack);
 | 
						||
							}
 | 
						||
						}
 | 
						||
						// 預防有 sub-folder,還是先 break;
 | 
						||
						break;
 | 
						||
 | 
						||
					} else {
 | 
						||
						if (path.startsWith('\\\\')) {
 | 
						||
							// a network drive.
 | 
						||
							base_path = path;
 | 
						||
							path_exists = true;
 | 
						||
						} else
 | 
						||
							library_namespace.warn([ 'travel_handler: ',
 | 
						||
									'無法 access folder [', path, ']!',
 | 
						||
									'或許是操作期間,檔案有所更動。您可能需要 refresh?' ]);
 | 
						||
					}
 | 
						||
				}
 | 
						||
 | 
						||
			// depth: 正處理之 files 的深度。
 | 
						||
			depth = stack.depth;
 | 
						||
			callback = callback_Array[FILE_HANDLER_INDEX];
 | 
						||
			if (stack.is_file) {
 | 
						||
				library_namespace.debug('處理/執行 folder [' + base_path
 | 
						||
						+ '] 的 sub-files。', 2, 'travel_handler');
 | 
						||
				// main loop of file.
 | 
						||
				for (var max_count = options.max_count,
 | 
						||
				//
 | 
						||
				filter = options.filter,
 | 
						||
				//
 | 
						||
				base = stack.base, pass_data = {
 | 
						||
					is_file : true,
 | 
						||
					depth : depth,
 | 
						||
					// data : stack.base[DATA],
 | 
						||
					// index : options.index,
 | 
						||
					length : options.count
 | 
						||
				};;)
 | 
						||
					if (stack.index < stack.length) {
 | 
						||
						name = stack[stack.index++];
 | 
						||
						if (match_filter(options.filter, path = base_path
 | 
						||
								+ name, depth)
 | 
						||
								&& match_filter(filter, name, depth)) {
 | 
						||
							/**
 | 
						||
							 * 處理/執行 sub-files。
 | 
						||
							 * 
 | 
						||
							 * @see <a
 | 
						||
							 *      href="http://msdn.microsoft.com/en-us/library/1ft05taf(v=vs.84).aspx"
 | 
						||
							 *      accessdate="2013/1/5 12:43">File Object</a>
 | 
						||
							 */
 | 
						||
							if (FSO.FileExists(path))
 | 
						||
								try {
 | 
						||
									// sub-file 存在,則 parent
 | 
						||
									// folder 亦存在。
 | 
						||
									path_exists = true;
 | 
						||
									pass_data.index = options.index++;
 | 
						||
									pass_data.data = base[name];
 | 
						||
									callback.call(this, FSO.GetFile(path),
 | 
						||
											pass_data);
 | 
						||
									if (--max_count === 0
 | 
						||
									//
 | 
						||
									&& stack.index < stack.length)
 | 
						||
										return;
 | 
						||
								} catch (e) {
 | 
						||
									if (callback_error_handler(this, e,
 | 
						||
											options, stack))
 | 
						||
										return;
 | 
						||
								}
 | 
						||
							else
 | 
						||
								library_namespace.warn([
 | 
						||
										'travel_handler: 檔案 [', path,
 | 
						||
										'] 不存在或無法 access!', '或許是操作期間,檔案有所更動。',
 | 
						||
										'您可能需要 refresh?' ]);
 | 
						||
						}
 | 
						||
						// 無論有無 match filter,皆應計數。
 | 
						||
						pass_data.index++;
 | 
						||
 | 
						||
					} else {
 | 
						||
						// 去掉 sub-files 之stack。
 | 
						||
						LIFO_stack.pop();
 | 
						||
						options.index = pass_data.index;
 | 
						||
						break;
 | 
						||
					}
 | 
						||
			}
 | 
						||
 | 
						||
			library_namespace.debug('已處理過 folder [' + base_path
 | 
						||
					+ '] 本身與 sub-files。expand sub-folders.', 2,
 | 
						||
					'travel_handler');
 | 
						||
			stack = LIFO_stack.at(-1);
 | 
						||
			// depth: 正處理之 folder 本身的深度。
 | 
						||
			depth = stack.depth;
 | 
						||
			if (!base_space) {
 | 
						||
				base_space = stack.base[stack[stack.index - 1]];
 | 
						||
				library_namespace.debug(
 | 
						||
						'可能因為 file list 長度超過 options.max_count,已經被截斷過,因此需要重設 base_space ['
 | 
						||
								+ stack[stack.index - 1] + '] → ' + base_space
 | 
						||
								+ '。', 2, 'travel_handler');
 | 
						||
			}
 | 
						||
		} else {
 | 
						||
			base_path = '';
 | 
						||
			base_space = _this.structure;
 | 
						||
		}
 | 
						||
 | 
						||
		if (isNaN(depth) || depth < options.depth) {
 | 
						||
			if (LIFO_stack.length === 0
 | 
						||
			// 確認所在的 folder 存在。
 | 
						||
			// 若不存在,也毋須 expand 了。
 | 
						||
			|| path_exists
 | 
						||
			// 亦可以下列檢測 base_path 的方式判別。
 | 
						||
			// || base_path !== LIFO_stack[LIFO_stack.length -
 | 
						||
			// 1].path
 | 
						||
			) {
 | 
						||
				library_namespace.debug('expand sub-folders.', 3,
 | 
						||
						'travel_handler');
 | 
						||
				for (name in ((stack = []).base = base_space))
 | 
						||
					if (name !== FILES && name !== DATA)
 | 
						||
						stack.push(name);
 | 
						||
 | 
						||
				if (stack.length > 0) {
 | 
						||
					if (options.sort)
 | 
						||
						if (typeof options.sort === 'function')
 | 
						||
							stack.sort(options.sort);
 | 
						||
						else
 | 
						||
							stack.sort();
 | 
						||
 | 
						||
					// sub-folders / sub-directory.
 | 
						||
					library_namespace.debug([ '開始處理 [', base_path, '] 之 ',
 | 
						||
							stack.length, ' 個子資料夾 [<span style="color:#25a;">',
 | 
						||
							stack.join('<b style="color:#47e;">|</b>'),
 | 
						||
							'</span>].' ], 2, 'travel_handler');
 | 
						||
					stack.index = 0;
 | 
						||
					stack.path = base_path;
 | 
						||
					if (!isNaN(depth))
 | 
						||
						stack.depth = depth + 1;
 | 
						||
					LIFO_stack.push(stack);
 | 
						||
					return;
 | 
						||
				}
 | 
						||
			}
 | 
						||
 | 
						||
			if (!base_path) {
 | 
						||
				// 應該只有 structure 為空時會用到。
 | 
						||
				library_namespace.warn(
 | 
						||
				//		
 | 
						||
				'travel_handler: structure 為空?');
 | 
						||
				return this.finish();
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		// 檢查本 stack 是否已處理完畢。
 | 
						||
		while ((stack = LIFO_stack.at(-1))
 | 
						||
		//
 | 
						||
		.index === stack.length) {
 | 
						||
			library_namespace.debug('Move up. stack.length = '
 | 
						||
					+ LIFO_stack.length, 2, 'travel_handler');
 | 
						||
			LIFO_stack.pop();
 | 
						||
			if (LIFO_stack.length === 0)
 | 
						||
				return this.finish();
 | 
						||
		}
 | 
						||
 | 
						||
	}
 | 
						||
 | 
						||
	/**
 | 
						||
	 * travel structure.<br />
 | 
						||
	 * 巡覽/遍歷檔案系統模擬結構的 public 公用函數。
 | 
						||
	 * 
 | 
						||
	 * TODO:<br />
 | 
						||
	 * 不區分大小寫。ignore case<br />
 | 
						||
	 * 與 dir/a/s 相較,network drive 的速度過慢。
 | 
						||
	 * 
 | 
						||
	 * @param {Function|Array}callback
 | 
						||
	 *            file system handle function / Array [file handler, folder
 | 
						||
	 *            handler].<br />
 | 
						||
	 *            可以安排僅對folder或是file作用之function。<br />
 | 
						||
	 *            handle function 應有的長度: 2<br />
 | 
						||
	 *            callback(fso, info = {is_file, depth, data : fso data, index,
 | 
						||
	 *            length}); index = 0 to (length - 1)
 | 
						||
	 * @param {Object}[options]
 | 
						||
	 *            optional flag. e.g., filter.<br />
 | 
						||
	 *            options 之內容會被改動!
 | 
						||
	 * 
 | 
						||
	 * @returns {Serial_execute} controller
 | 
						||
	 * @returns undefined error occurred.
 | 
						||
	 * 
 | 
						||
	 * @since 2013/1/6 18:57:16 堪用。
 | 
						||
	 */
 | 
						||
	function for_each_FSO(callback, options) {
 | 
						||
 | 
						||
		var path_length = this.path_list.length;
 | 
						||
		if (path_length === 0) {
 | 
						||
			if (library_namespace.is_debug())
 | 
						||
				library_namespace.warn(
 | 
						||
				//		
 | 
						||
				'for_each_FSO: 尚未設定可供巡覽之 path。');
 | 
						||
			return undefined;
 | 
						||
		}
 | 
						||
 | 
						||
		library_namespace.debug('初始化+正規化。', 2, 'for_each_FSO');
 | 
						||
		if (typeof callback === 'function')
 | 
						||
			callback = [ callback, callback ];
 | 
						||
		else if (!Array.isArray(callback) || callback.length === 0)
 | 
						||
			return;
 | 
						||
 | 
						||
		// 前置處理。
 | 
						||
		if (!library_namespace.is_Object(options))
 | 
						||
			options = Object.create(null);
 | 
						||
 | 
						||
		// 計數 + 進度。
 | 
						||
		options.index = 0;
 | 
						||
		// 至此 options.count 代表 file count.
 | 
						||
		options.count = this.count.filtered_file;
 | 
						||
		options.folder_count = this.count.filtered_folder;
 | 
						||
		if (options.single_count
 | 
						||
		//
 | 
						||
		= callback[FOLDER_HANDLER_INDEX]
 | 
						||
		//
 | 
						||
		=== callback[FILE_HANDLER_INDEX])
 | 
						||
			options.folder_count
 | 
						||
			//
 | 
						||
			= (options.count += options.folder_count);
 | 
						||
		else
 | 
						||
			options.folder_index = 0;
 | 
						||
 | 
						||
		check_filter_of_options(options);
 | 
						||
 | 
						||
		if (typeof options.sort !== 'function'
 | 
						||
				&& !(options.sort = !!options.sort))
 | 
						||
			delete options.sort;
 | 
						||
 | 
						||
		// 限定 traverse 深度幾層。深入/不深入下層子目錄及檔案。
 | 
						||
		if (isNaN(options.depth) || (options.depth |= 0) < 0)
 | 
						||
			options.depth = default_depth;
 | 
						||
 | 
						||
		if (isNaN(options.max_count) || (options.max_count |= 0) < 1
 | 
						||
				|| options.max_count > 1e5)
 | 
						||
			// 預設一次 thread 最多處理之檔案個數。
 | 
						||
			options.max_count = 800;
 | 
						||
 | 
						||
		options.argument = [ [], this, callback, options ];
 | 
						||
 | 
						||
		if (typeof options.final === 'function')
 | 
						||
			options.final = options.final.bind(this);
 | 
						||
 | 
						||
		library_namespace.debug('開始巡覽 ' + path_length + ' paths。', 2,
 | 
						||
				'for_each_FSO');
 | 
						||
		return new Serial_execute(travel_handler, options);
 | 
						||
	}
 | 
						||
 | 
						||
	// ---------------------------------------------------------------------//
 | 
						||
	// public interface of file_system_structure.
 | 
						||
 | 
						||
	Object.assign(file_system_structure.prototype, {
 | 
						||
		get : resolve_path,
 | 
						||
		add : add_path,
 | 
						||
		each : for_each_FSO,
 | 
						||
		refresh : refresh_structure,
 | 
						||
		list : function(options) {
 | 
						||
			var list = [];
 | 
						||
			// TODO: 可用萬用字元。
 | 
						||
			this.each(function(fso, info) {
 | 
						||
				if (info.is_file)
 | 
						||
					list.push(fso.Path);
 | 
						||
			}, options);
 | 
						||
			return list;
 | 
						||
		},
 | 
						||
		toJSON : structure_to_JSON
 | 
						||
	});
 | 
						||
 | 
						||
	// ---------------------------------------------------------------------//
 | 
						||
 | 
						||
	// CeL.run('data.file');
 | 
						||
	// var file_handler = new CeL.file([ 'path', 'path' ]);
 | 
						||
	// file_handler.merge('to_path').add('path').forEach(function(){});
 | 
						||
	function Files(path_list, options) {
 | 
						||
		// private property:
 | 
						||
		// this.data[path] = data of file = {
 | 
						||
		// encoding : encoding cache. NOT absolutely correct.
 | 
						||
		// other data.
 | 
						||
		// }
 | 
						||
		this.data = {};
 | 
						||
		// private property:
 | 
						||
		// this.order = ['path'];
 | 
						||
		if (options) {
 | 
						||
			if (typeof options.comparator === 'function')
 | 
						||
				// e.g., function(a, b){return a-b;};
 | 
						||
				this.comparator = options.comparator;
 | 
						||
		}
 | 
						||
 | 
						||
		this.add(path_list, options);
 | 
						||
	}
 | 
						||
 | 
						||
	function Files_add_object(path, data, order, filter, attributes) {
 | 
						||
		var object, count = 0;
 | 
						||
		// 注意: 輸入 "C:" 會得到 C: 的工作目錄。
 | 
						||
		if (FSO.FolderExists(path)) {
 | 
						||
			library_namespace.debug('Add folder [' + path + ']!', 2);
 | 
						||
			var fso = FSO.GetFolder(path), item, collection;
 | 
						||
			for (collection = new Enumerator(fso.Files);
 | 
						||
			//
 | 
						||
			!collection.atEnd(); collection.moveNext()) {
 | 
						||
				item = collection.item();
 | 
						||
				path = item.Path;
 | 
						||
				if (!match_filter(filter, path))
 | 
						||
					continue;
 | 
						||
				data[path] = object = {
 | 
						||
					size : item.Size
 | 
						||
				};
 | 
						||
				if (attributes)
 | 
						||
					Object.assign(object, attributes);
 | 
						||
				if (order)
 | 
						||
					order.push(path);
 | 
						||
				count++;
 | 
						||
			}
 | 
						||
 | 
						||
		} else if (FSO.FileExists(path)) {
 | 
						||
			library_namespace.debug('Add file [' + path + ']!', 2);
 | 
						||
			if (match_filter(filter, path)) {
 | 
						||
				data[path] = object = {};
 | 
						||
				if (attributes)
 | 
						||
					Object.assign(object, attributes);
 | 
						||
				if (order)
 | 
						||
					order.push(path);
 | 
						||
				count++;
 | 
						||
			}
 | 
						||
 | 
						||
		} else
 | 
						||
			library_namespace.warn('Files_add_object: 無法辨識 path [' + path
 | 
						||
					+ ']!');
 | 
						||
 | 
						||
		library_namespace.debug('Get ' + count + ' files.', 2);
 | 
						||
	}
 | 
						||
 | 
						||
	function Files_add(path_list, options) {
 | 
						||
		var data = this.data,
 | 
						||
		//
 | 
						||
		order = Array.isArray(this.order) && this.order,
 | 
						||
		//
 | 
						||
		filter = is_regular_filter(options.filter) && options.filter;
 | 
						||
 | 
						||
		if (typeof path_list === 'string')
 | 
						||
			Files_add_object(path_list, data, order, filter);
 | 
						||
 | 
						||
		else if (Array.isArray(path_list))
 | 
						||
			path_list.forEach(function(path) {
 | 
						||
				Files_add_object(path, data, order, filter);
 | 
						||
			});
 | 
						||
 | 
						||
		else if (library_namespace.is_Object(path_list))
 | 
						||
			for ( var path in path_list)
 | 
						||
				Files_add_object(path, data, order, filter, path_list[path]);
 | 
						||
 | 
						||
		else {
 | 
						||
			library_namespace.warn('Files: Unknown path list [' + path_list
 | 
						||
					+ ']!');
 | 
						||
			order = false;
 | 
						||
		}
 | 
						||
 | 
						||
		if (order && this.comparator)
 | 
						||
			this.sort(
 | 
						||
			// default: this.comparator
 | 
						||
			);
 | 
						||
 | 
						||
		return this;
 | 
						||
	}
 | 
						||
 | 
						||
	// create and return this 之 list。
 | 
						||
	// 請勿直接使用 this.order,而應該使用 this.list();
 | 
						||
	// 因為 this.order 可能尚未準備好。
 | 
						||
	// 不想改到 this 的,得自己 .slice()。
 | 
						||
	function Files_list(rebuilt) {
 | 
						||
		if (rebuilt || !Array.isArray(this.order)) {
 | 
						||
			var order = [];
 | 
						||
			// 重新創建 order。
 | 
						||
			for ( var path in this.data)
 | 
						||
				order.push(path);
 | 
						||
			this.order = order;
 | 
						||
		}
 | 
						||
 | 
						||
		return this.order;
 | 
						||
	}
 | 
						||
 | 
						||
	function Files_sort(comparator, options) {
 | 
						||
		// 重製 order。
 | 
						||
		var order = this.list(),
 | 
						||
		//
 | 
						||
		comparator = comparator || this.comparator;
 | 
						||
 | 
						||
		if (options && options.no_set_order)
 | 
						||
			order = order.slice();
 | 
						||
 | 
						||
		if (typeof use_function === 'function')
 | 
						||
			order.sort(use_function);
 | 
						||
		else if (library_namespace.is_RegExp(use_function))
 | 
						||
			order.sort(function(key_1, key_2) {
 | 
						||
				if ((key_1 = key_1.match(use_function))
 | 
						||
				//
 | 
						||
				&& (key_2 = key_2.match(use_function)))
 | 
						||
					return key_1[1] - key_2[1];
 | 
						||
			});
 | 
						||
		else
 | 
						||
			order.sort();
 | 
						||
 | 
						||
		library_namespace.debug(order.join('<br />'), 2);
 | 
						||
 | 
						||
		if (options) {
 | 
						||
			if (!options.no_set_order) {
 | 
						||
				this.order = order;
 | 
						||
				if (comparator === 'function' && !options.no_set_sort)
 | 
						||
					this.comparator = comparator;
 | 
						||
			}
 | 
						||
 | 
						||
			if (options.get_list)
 | 
						||
				return order;
 | 
						||
		}
 | 
						||
 | 
						||
		return this;
 | 
						||
	}
 | 
						||
 | 
						||
	var get_file_extension, read_file, write_file, guess_encoding;
 | 
						||
 | 
						||
	function setup_Files(function_body, function_name) {
 | 
						||
		if (!function_name)
 | 
						||
			function_name = library_namespace.get_function_name(function_body);
 | 
						||
		setup_Files.conversion[function_name] = function_body;
 | 
						||
 | 
						||
		return function() {
 | 
						||
			setup_Files.setup();
 | 
						||
			return function_body.apply(this, arguments);
 | 
						||
		};
 | 
						||
	}
 | 
						||
	setup_Files.conversion = {};
 | 
						||
	setup_Files.setup = function() {
 | 
						||
		library_namespace.debug('Trying setup Files.', 1, 'setup_Files.setup');
 | 
						||
		library_namespace.is_included([ 'application.storage.file',
 | 
						||
				'application.OS.Windows.file', 'application.locale.encoding' ],
 | 
						||
				true);
 | 
						||
 | 
						||
		get_file_extension = library_namespace.
 | 
						||
		//
 | 
						||
		application.storage.file.get_file_extension;
 | 
						||
		var file = library_namespace.application.OS.Windows.file;
 | 
						||
		read_file = file.read_file;
 | 
						||
		write_file = file.write_file;
 | 
						||
		guess_encoding = library_namespace.
 | 
						||
		//
 | 
						||
		application.locale.encoding.guess_encoding;
 | 
						||
 | 
						||
		Object.assign(Files.prototype, setup_Files.conversion);
 | 
						||
		library_namespace.debug('Setup Files done.', 1, 'setup_Files.setup');
 | 
						||
	};
 | 
						||
 | 
						||
	function detect_encoding(this_Files, path, force) {
 | 
						||
		var data = this_Files.data.path;
 | 
						||
		if (!data)
 | 
						||
			return;
 | 
						||
		if (force || !data.encoding)
 | 
						||
			// cache encoding
 | 
						||
			data.encoding = guess_encoding(path);
 | 
						||
		library_namespace.debug('(' + data.encoding + ') [' + path + ']');
 | 
						||
		return data.encoding;
 | 
						||
	}
 | 
						||
 | 
						||
	function decide_target(path, options) {
 | 
						||
		var target = typeof options.target === 'function'
 | 
						||
		// default: overwrite / save to original file.
 | 
						||
		? options.target(path) : path;
 | 
						||
 | 
						||
		if (typeof options.target_file === 'function') {
 | 
						||
			// get file name.
 | 
						||
			target = target.match(/^(.*?)([^\\\/]+)$/);
 | 
						||
			target = target[1] + options.target_file(target[2]);
 | 
						||
		}
 | 
						||
 | 
						||
		if (target !== path) {
 | 
						||
			library_namespace.debug('check extension 一致性。', 2);
 | 
						||
			var has_error,
 | 
						||
			//
 | 
						||
			ext1 = get_file_extension(path),
 | 
						||
			//
 | 
						||
			ext2 = get_file_extension(target);
 | 
						||
			if (ext1 !== ext2) {
 | 
						||
				has_error = 1;
 | 
						||
				library_namespace.error('副檔名不同! [' + ext1 + ']→[' + ext2 + ']');
 | 
						||
			}
 | 
						||
 | 
						||
			library_namespace.debug('check target_file 檔案存在與否。', 2);
 | 
						||
			if (FSO.FileExists(target)) {
 | 
						||
				library_namespace.debug('check target_file 檔案大小是否差異太大。', 2);
 | 
						||
				var ratio = FSO.GetFile(target).Size / FSO.GetFile(path).Size;
 | 
						||
				if (ratio > 8) {
 | 
						||
					has_error = 2;
 | 
						||
					library_namespace.error('目標檔案 [' + target_file
 | 
						||
							+ '] 已存在,且檔案大小差異太大!');
 | 
						||
				} else {
 | 
						||
					library_namespace.warn('目標檔案 [' + target_file + '] 已存在!'
 | 
						||
							+ (has_error ? '' : '將覆寫之。'));
 | 
						||
				}
 | 
						||
			}
 | 
						||
 | 
						||
			if (has_error)
 | 
						||
				return;
 | 
						||
		}
 | 
						||
 | 
						||
		library_namespace.debug('['
 | 
						||
				+ path
 | 
						||
				+ ']'
 | 
						||
				+ (path === target ? ': original = decided' : '<br />→ ['
 | 
						||
						+ target + ']'), 2);
 | 
						||
		return target;
 | 
						||
	}
 | 
						||
 | 
						||
	/**
 | 
						||
	 * @example <code>
 | 
						||
		// Files_encode('target encoding')
 | 
						||
		// if set save_to, then backup_to will be ignored.
 | 
						||
		options = {
 | 
						||
			from : 'encoding',
 | 
						||
			save_to : function(path) {
 | 
						||
				return 'save to';
 | 
						||
			},
 | 
						||
			backup_to : function(path) {
 | 
						||
				return 'save to';
 | 
						||
			}
 | 
						||
		}
 | 
						||
	 </code>
 | 
						||
	 */
 | 
						||
	function Files_encode(target_encoding, options) {
 | 
						||
		if (!options)
 | 
						||
			options = Object.create(null);
 | 
						||
 | 
						||
		var modify = typeof options.modify === 'function' && options.modify,
 | 
						||
		//
 | 
						||
		filter = is_regular_filter(options.filter) && options.filter,
 | 
						||
		//
 | 
						||
		handle_file = function(path) {
 | 
						||
			if (!match_filter(filter, path))
 | 
						||
				return;
 | 
						||
			library_namespace.debug('處理 [' + path + ']', 2, 'Files_encode');
 | 
						||
			var source_encoding = options.source_encoding
 | 
						||
					|| detect_encoding(this, path) || this.encoding,
 | 
						||
			//
 | 
						||
			source_text, converted_text,
 | 
						||
			//
 | 
						||
			target_file = decide_target(path, options);
 | 
						||
			if (!target_file)
 | 
						||
				return;
 | 
						||
 | 
						||
			source_text = read_file(path, source_encoding);
 | 
						||
			converted_text = modify ? modify(text) : text;
 | 
						||
 | 
						||
			if (typeof converted_text === 'string'
 | 
						||
			//
 | 
						||
			&& (source_text !== converted_text
 | 
						||
			//
 | 
						||
			|| source_enc && target_encoding
 | 
						||
			//
 | 
						||
			&& source_enc !== target_encoding)) {
 | 
						||
				// TODO: !!workaround!!
 | 
						||
				write_file(target_file, converted_text,
 | 
						||
				// TODO: truncate file
 | 
						||
				target_encoding);
 | 
						||
			} else if (target_file !== path) {
 | 
						||
				// copy file.
 | 
						||
				library_namespace.debug(path + ' → ' + target_file);
 | 
						||
				FSO.CopyFile(path, target_file);
 | 
						||
			}
 | 
						||
 | 
						||
		};
 | 
						||
 | 
						||
		if (target_encoding || modify)
 | 
						||
			this.list().forEach(handle_file, this);
 | 
						||
		else
 | 
						||
			library_namespace.warn(
 | 
						||
			//
 | 
						||
			'Files_encode: Target encoding does not specified.');
 | 
						||
 | 
						||
		return this;
 | 
						||
	}
 | 
						||
 | 
						||
	function Files_replace(RegExp_from, to) {
 | 
						||
		return this.encode(null, {
 | 
						||
			modify : function(text) {
 | 
						||
				return text.replace(RegExp_from, to);
 | 
						||
			}
 | 
						||
		});
 | 
						||
	}
 | 
						||
 | 
						||
	function Files_merge(target, options) {
 | 
						||
		if (!target)
 | 
						||
			// TODO: auto detect.
 | 
						||
			throw new Error('Files_merge: No target specified!');
 | 
						||
 | 
						||
		target = decide_target(target, options);
 | 
						||
		if (!target)
 | 
						||
			return;
 | 
						||
 | 
						||
		if (!options)
 | 
						||
			options = Object.create(null);
 | 
						||
		else if (typeof options === 'string')
 | 
						||
			options = {
 | 
						||
				encoding : options
 | 
						||
			};
 | 
						||
 | 
						||
		var encoding = options.encoding,
 | 
						||
		//
 | 
						||
		modify = typeof options.modify === 'function'
 | 
						||
		//
 | 
						||
		&& options.modify,
 | 
						||
		//
 | 
						||
		filter = is_regular_filter(options.filter) && options.filter,
 | 
						||
		//
 | 
						||
		text_Array = [],
 | 
						||
		//
 | 
						||
		add_text = function(item, path) {
 | 
						||
			item = options[item];
 | 
						||
			if (!item)
 | 
						||
				return;
 | 
						||
 | 
						||
			if (typeof item === 'string')
 | 
						||
				text_Array.push(item);
 | 
						||
			else if (typeof item === 'function')
 | 
						||
				text_Array.push(item(path));
 | 
						||
			else if (Array.isArray(item))
 | 
						||
				Array.prototype.push.apply(
 | 
						||
				//
 | 
						||
				text_Array, item);
 | 
						||
			else
 | 
						||
				library_namespace.debug('Unknown text: [' + item + ']', 3);
 | 
						||
		},
 | 
						||
		//
 | 
						||
		handle_file = function(path) {
 | 
						||
			if (!match_filter(filter, path))
 | 
						||
				return;
 | 
						||
			library_namespace.debug('處理 [' + path + ']', 2, 'Files_merge');
 | 
						||
			var encoding = options.source_encoding
 | 
						||
			//
 | 
						||
			|| detect_encoding(this, path),
 | 
						||
			//
 | 
						||
			text = read_file(path, encoding);
 | 
						||
			library_namespace.debug('(' + encoding + ') [' + path + ']: '
 | 
						||
					+ text.length + ' characters.', 3);
 | 
						||
 | 
						||
			if (modify) {
 | 
						||
				text = modify(text, path);
 | 
						||
				library_namespace.debug(
 | 
						||
				//
 | 
						||
				'→ ' + text.length + ' characters.', 3);
 | 
						||
				if (text.length === 0)
 | 
						||
					library_namespace.warn('[' + path + ']: 經 modify 後,無內容回傳!');
 | 
						||
			}
 | 
						||
 | 
						||
			add_text('subhead', path);
 | 
						||
			text_Array.push(text);
 | 
						||
			// sub-footer
 | 
						||
			add_text('subfooter', path);
 | 
						||
		};
 | 
						||
 | 
						||
		if (!encoding)
 | 
						||
			encoding = FSO.FileExists(target) && detect_encoding(this, target)
 | 
						||
					|| this.encoding;
 | 
						||
		if (library_namespace.is_debug(3))
 | 
						||
			library_namespace.debug('Merge ' + this.list().length
 | 
						||
			//
 | 
						||
			+ ' files to (' + encoding + ') [' + target + '].');
 | 
						||
 | 
						||
		add_text('head', target);
 | 
						||
		this.list().forEach(handle_file, this);
 | 
						||
		add_text('footer', target);
 | 
						||
 | 
						||
		text_Array = text_Array.join('');
 | 
						||
		if (typeof options.modify_all === 'function')
 | 
						||
			text_Array = options.modify_all(text_Array);
 | 
						||
 | 
						||
		library_namespace.debug('Merge ' + this.order.length
 | 
						||
				+ ' files, writing ' + text_Array.length + ' characters to ('
 | 
						||
				+ encoding + ') [' + target + '].');
 | 
						||
		write_file(target, text_Array, encoding);
 | 
						||
 | 
						||
		(this.data = {})[target] = {};
 | 
						||
 | 
						||
		delete this.order;
 | 
						||
 | 
						||
		return this;
 | 
						||
	}
 | 
						||
 | 
						||
	/**
 | 
						||
	 * batch operation
 | 
						||
	 * 
 | 
						||
	 * @param {Function}callback
 | 
						||
	 *            callback(path, data)
 | 
						||
	 * @param {Function}comparator
 | 
						||
	 * @returns {@Files}
 | 
						||
	 */
 | 
						||
	function Files_forEach(callback, comparator) {
 | 
						||
		var data = this.data,
 | 
						||
		//
 | 
						||
		order = comparator ? this.sort(comparator, {
 | 
						||
			no_set_order : true,
 | 
						||
			get_list : true
 | 
						||
		}) : this.list();
 | 
						||
		order.forEach(function(path) {
 | 
						||
			library_namespace.debug('處理 [' + path + ']', 2, 'Files_forEach');
 | 
						||
			callback(path, data[path]);
 | 
						||
		});
 | 
						||
 | 
						||
		return this;
 | 
						||
	}
 | 
						||
 | 
						||
	/**
 | 
						||
	 * get HTML contents.<br />
 | 
						||
	 * get contents between "body" tag.
 | 
						||
	 * 
 | 
						||
	 * @param {String}HTML
 | 
						||
	 *            HTML text
 | 
						||
	 * @returns {String}contents
 | 
						||
	 * @since 2014/7/19 23:9:41
 | 
						||
	 */
 | 
						||
	function HTML_contents(HTML, use_RegExp) {
 | 
						||
		if (use_RegExp)
 | 
						||
			return HTML.replace(/[\s\n]*<\/body>(.+|\n)*$/i, '')
 | 
						||
			// default 不用 RegExp,因為 RegExp 太慢。
 | 
						||
			.replace(/^(.+|\n)*<body>[\s\n]*/i, '');
 | 
						||
 | 
						||
		var foot = HTML.lastIndexOf('</body>'),
 | 
						||
		//
 | 
						||
		head = HTML.indexOf('<body');
 | 
						||
 | 
						||
		// 去尾。
 | 
						||
		if (false && foot === NOT_FOUND)
 | 
						||
			foot = HTML.lastIndexOf('</BODY>');
 | 
						||
		if (foot === NOT_FOUND) {
 | 
						||
			foot = HTML.lastIndexOf('</div>');
 | 
						||
			if (foot !== NOT_FOUND && !/^[\s\n]*$/.test(HTML.slice(foot)))
 | 
						||
				foot = NOT_FOUND;
 | 
						||
		}
 | 
						||
 | 
						||
		// 去頭。
 | 
						||
		if (false && head === NOT_FOUND)
 | 
						||
			head = HTML.indexOf('<BODY');
 | 
						||
		if (head === NOT_FOUND) {
 | 
						||
			head = HTML.indexOf('<div');
 | 
						||
			if (head !== NOT_FOUND && !/^[\s\n]*$/.test(HTML.slice(0, head)))
 | 
						||
				head = NOT_FOUND;
 | 
						||
		}
 | 
						||
		head = HTML.indexOf('>', head) + 1;
 | 
						||
 | 
						||
		// 切割。
 | 
						||
		if (foot === NOT_FOUND) {
 | 
						||
			if (head !== NOT_FOUND)
 | 
						||
				HTML = HTML.slice(head);
 | 
						||
		} else
 | 
						||
			HTML = HTML.slice(head, foot);
 | 
						||
 | 
						||
		return HTML;
 | 
						||
	}
 | 
						||
 | 
						||
	// ---------------------------------------------------------------------//
 | 
						||
	// public interface of Files.
 | 
						||
 | 
						||
	Object.assign(Files.prototype, {
 | 
						||
		add : Files_add,
 | 
						||
		list : Files_list,
 | 
						||
		sort : Files_sort,
 | 
						||
 | 
						||
		// convert encoding
 | 
						||
		encode : setup_Files(Files_encode, 'encode'),
 | 
						||
		merge : setup_Files(Files_merge, 'merge'),
 | 
						||
 | 
						||
		replace : setup_Files(Files_replace, 'replace'),
 | 
						||
 | 
						||
		forEach : Files_forEach,
 | 
						||
		encoding : 'UTF-8'
 | 
						||
	});
 | 
						||
 | 
						||
	// ---------------------------------------------------------------------//
 | 
						||
	// export.
 | 
						||
 | 
						||
	return Object.assign(Files, {
 | 
						||
		HTML_contents : HTML_contents,
 | 
						||
		file_system_structure : file_system_structure
 | 
						||
	});
 | 
						||
 | 
						||
}
 |