/**
 * @name CeL function for Comma-separated values (CSV) and tab-separated values
 *       (TSV) data
 * @fileoverview 本檔案包含了處理 CSV, TSV data 的 functions。 TODO: delimiter-separated
 *               values (DSV)
 * @since
 */

'use strict';
// 'use asm';

// --------------------------------------------------------------------------------------------

// 不採用 if 陳述式，可以避免 Eclipse JSDoc 與 format 多縮排一層。
typeof CeL === 'function' && CeL.run({
	// module name
	name : 'data.CSV',

	// require : '',

	// 設定不匯出的子函式。
	// no_extend : '*',

	// 為了方便格式化程式碼，因此將 module 函式主體另外抽出。
	code : module_code
});

function module_code(library_namespace) {

	// nothing required

	/**
	 * null module constructor
	 * 
	 * @class CSV data 的 functions
	 */
	var _// JSDT:_module_
	= function() {
		// null module constructor
	};

	/**
	 * for JSDT: 有 prototype 才會將之當作 Class
	 */
	_// JSDT:_module_
	.prototype = {};

	// ----------------------------------------------------------------------------------------------------------------------------------------------------------//

	/**
	 * default config/設定/選項/options/flag.
	 */
	var default_config = {
		/**
		 * {Boolean} is there a title line? (TODO: Should use has_header)
		 */
		has_title : false,
		/**
		 * {Boolean} 是否將 [0] 設定為第一筆資料。若為 true，則 [0] 為 title。
		 */
		skip_title : false,
		// data[title_word]=title row array
		title_word : 'title',
		// data[title_index_word]={title:column index}
		title_index_word : 'index',

		/**
		 * {Integer} 將每筆資料依 title column index 存成 Object。<br />
		 * 設定之後 table = {title_name: [row data]}
		 */
		to_Object : undefined,
		/**
		 * {Boolean} 如何存每筆資料。<br />
		 * select_column=true<br />
		 * 設定之後 data[row index] = {title_name: data}<br />
		 * TODO:<br />
		 * select_column=column_index<br />
		 * select_column=[column_index]<br />
		 * select_column={title_name:column_index}<br />
		 * select_column={title_index:column_index}<br />
		 */
		select_column : false,

		// table[row index][column index] 將以 handle_array[column index] 預先處理。
		handle_array : undefined,

		// 處理 every row。row = handle_row(row, row_count);
		handle_row : undefined,

		// bool|'auto'
		/**
		 * {Boolean} 是否使用 text delimiter。<br />
		 * for to_CSV_String()，表示是否強制加上 text delimiter。
		 */
		no_text_qualifier : 'auto',
		/**
		 * {String} 欄位文字辨識符號。<br />
		 * Excel 之 Unicode 文字輸出不包括 "'"。<br />
		 * //text_qualifier : '"\'',
		 */
		text_qualifier : '"',
		/**
		 * {String} 欄位分隔符號。<br />
		 * adding ';:\\s'
		 */
		field_delimiter : '\t,',
		/**
		 * {String} row delimiter (row 分隔符號) / record delimiters: usually only
		 * \n.<br />
		 * '\r' will be replaced to '\n' and will ignored!
		 */
		line_separator : '\n',

		/**
		 * {Number} 僅取首起之筆數
		 */
		row_limit : undefined
	};

	// ----------------------------------------------------------------------------------------------------------------------------------------------------------//

	/**
	 * parse Comma-separated values/Delimiter-separated values data.<br />
	 * 讀入 CSV 檔。<br />
	 * TODO:<br />
	 * mix data. e.g., [data 1, data 2, "data 3", data 4]<br />
	 * 可一筆一筆處理，不佔記憶體。<br />
	 * DoEvents
	 * 
	 * @param {String}data
	 *            CSV text data
	 * @param {Object}config
	 *            自訂設定/選項。 see parse_CSV.config.
	 * 
	 * @return {Array} data array = [<br />
	 *         [R1F1,R1F2,... ],<br />
	 *         [R2F1,R2F2,... ],<br />
	 *         ... ]
	 * 
	 * @_memberOf _module_
	 * 
	 * @example <code>

	 //	to use:
	 CeL.run('data.CSV',function(){ 'code' ;});
	 var data=parse_CSV('data');
	 //data[_line_][_field_]

	 //	there's a title line:
	 var data = parse_CSV('data',{has_title:true});
	 //data[_line_][data.t[_title_]]

	 //	then:
	 data[title_word]	=	{title_field_name : field number of title};
	 data.title_array	=	[title_field_name];
	 data.it	=	ignored title array
	 data[num]	=	the num-th line (num: 0,1,2,..)

	 // More examples: see /_test suite/test.js

	 * </code>
	 * 
	 * @see <a href="http://www.jsdb.org/" accessdate="2010/1/1 0:53">JSDB:
	 *      JavaScript for databases</a>,<br />
	 *      <a
	 *      href="http://hax.pie4.us/2009/05/lesson-of-regexp-50x-faster-with-just.html"
	 *      accessdate="2010/1/1 0:53">John Hax: A lesson of RegExp: 50x faster
	 *      with just one line patch</a>,<br />
	 *      http://misoproject.com/dataset/
	 * 
	 * @since 2012/4/2 15:26:06 重寫 parse_CSV()，功能加強。效能恐降低。
	 */
	function parse_CSV(data, config) {
		if (!data || !/[^\n]/.test(data = ('' + data).replace(/\r\n?/g, '\n')))
			return;

		var tmp, table = [], row = [], matched, value, max_column_count, min_column_count,
		// 自訂設定
		config = new library_namespace.setting_pair({}, parse_CSV.config,
				config),
		// cache
		data_length = data.length,

		to_Object = parseInt(config('to_Object')),

		// cache
		select_column = config('select_column'), title_row_for_Object,
		// cache
		title_row_for_Object_length, column_count = 0, title_passed,

		row_count = 0, row_limit = config('row_limit'),

		auto_text_qualifier, field_pattern = [ '(.*?)' ], text_qualifier = config('text_qualifier'), text_qualifier_replace_from, last_index_of_data = 0, handle_array = config('handle_array'), handle_row = config('handle_row'), has_title = config('has_title'), preserve_blank_row = config('preserve_blank_row'),
		// 設定 title.
		set_title = function(row) {
			if (has_title && row
			// && to_Object === undefined
			) {
				if (typeof config('title_word') === 'string') {
					table[config('title_word')] = row;
				}
				if (typeof config('title_index_word') === 'string') {
					var i, j = table[config('title_index_word')] = {};
					for (i in row) {
						j[row[i]] = +i;
					}
				}
			}
			set_title = undefined;
		};

		function add_row(row) {
			var use_row;
			// 包含 title 與末列(undefined)皆會被傳入。
			if (!handle_row
					|| (use_row = handle_row(row, row_count)) === undefined)
				use_row = row;

			if (title_passed || !has_title || !config('skip_title')) {
				if (isNaN(to_Object)) {
					// 去除空白列，包括檔尾。
					if (column_count > 0 || preserve_blank_row) {
						table.push(use_row);
						row_count++;
					} else if (library_namespace.is_debug()) {

						library_namespace.warn('parse_CSV: 空白列 @ record '
								+ row_count + '('
								+ library_namespace.is_type(row) + ')');
					}
				} else if (title_row_for_Object) {
					var column_index = title_row_for_Object[to_Object];
					if (column_index in row) {
						if (row[column_index] in table) {
							library_namespace
									.warn('parse_CSV: conflict record name: ['
											+ row[column_index] + '], index: '
											+ column_index);
						}
						table[row[column_index]] = use_row;
						row_count++;
					}
				} else if (to_Object in row) {
					table[row[to_Object]] = use_row;
					row_count++;
				}
			}
		}

		if (isNaN(row_limit))
			row_limit = undefined;

		if (isNaN(to_Object))
			to_Object = undefined;
		else {
			table = {};
			// 既然指定 to_Object，蘊含應當有 title。
			has_title = true;
			config('skip_title', true);
		}

		if (select_column !== undefined && select_column !== false) {
			library_namespace.debug('select_column: ' + select_column, 2,
					'parse_CSV');
			if (Array.isArray(select_column)) {
				title_row_for_Object = select_column;
				// cache
				title_row_for_Object_length = title_row_for_Object.length;
				row = {};
			} else {
				// 既然指定 select_column，蘊含應當有 title。
				has_title = true;
			}
		}

		if (!Array.isArray(handle_array)
				&& (!select_column || !library_namespace
						.is_Object(handle_array)))
			handle_array = undefined;
		library_namespace
				.debug('handle_array: ' + handle_array, 2, 'parse_CSV');

		if (typeof handle_row !== 'function')
			handle_row = undefined;

		if (typeof config('no_text_qualifier') === 'undefined') {
			if (matched = data.match(new RegExp('[^' + config('line_separator')
					+ ']+'))) {
				matched = matched[0];
				matched = (new RegExp('^[' + text_qualifier + ']'))
						.test(matched)
						&& (new RegExp('[' + text_qualifier + ']$'))
								.test(matched);
				library_namespace.debug('auto detect: '
						+ (matched ? 'using' : 'no') + ' text_qualifier.', 1,
						'parse_CSV');
				config('no_text_qualifier', !matched);
			}
		} else if (config('no_text_qualifier') === 'auto' && text_qualifier) {
			auto_text_qualifier = true;
		}

		// build field pattern
		if (!config('no_text_qualifier') && text_qualifier) {
			// e.g., /(.*?)["']((?:[^"']|["']{2})*)["'](?:[\t,]|([\n]))?/g .
			field_pattern.push(
			// ["']
			'[', text_qualifier, ']',
			// ((?:[^"']|["']{2})*)
			'((?:[^', text_qualifier, ']|[', text_qualifier, ']{2})*)',
			// ["']
			'[', text_qualifier, ']');
		} else if (auto_text_qualifier) {
			// 對**每個** column 都作偵測。
			// e.g.,
			// /(.*?)(?:["']((?:[^"']|["']{2})*)["']|[^\t,\n]*)(?:[\t,]|([\n]))?/g
			// .
			field_pattern.push('(?:',
			// ["']
			'[', text_qualifier, ']',
			// ((?:[^"']|["']{2})*)
			'((?:[^', text_qualifier, ']|[', text_qualifier, ']{2})*)',
			// ["']
			'[', text_qualifier, ']',
			// |: 無 text qualifier 的部分。
			'|',
			// 無 text qualifier 的部分。
			'(',
			// [^\t,\n]*
			'[^', config('field_delimiter'), config('line_separator'), ']*',
			// ending of 無 text qualifier 的部分。
			')',
			// ending
			')');
		} else {
			text_qualifier = '';
			// e.g., /(.*?)([^\t,\n]*)(?:[\t,]|([\n]))?/g .
			field_pattern.push(
			// 無 text qualifier 的部分。
			'(',
			// [^\t,\n]*
			'[^', config('field_delimiter'), config('line_separator'), ']*',
			// ending of 無 text qualifier 的部分。
			')');
		}
		field_pattern.push('(?:[', config('field_delimiter'), ']|([',
				config('line_separator'), ']))?');
		field_pattern = new RegExp(field_pattern.join(''), 'g');
		if (text_qualifier)
			// e.g., /(["']){2}/g
			text_qualifier_replace_from = new RegExp('([' + text_qualifier
					+ ']){2}', 'g');
		library_namespace.debug('use field pattern: ' + field_pattern, 2,
				'parse_CSV');

		// main loop
		while (last_index_of_data < data_length
				&& (matched = field_pattern.exec(data))) {
			last_index_of_data = field_pattern.lastIndex;
			// matched = [fill pattern, extra(error?), field data[, field data],
			// line separator]
			library_namespace.debug(last_index_of_data + '/' + data_length
					+ ' ' + row_count + '<br />' + matched.join('<br />→ '), 4,
					'parse_CSV');

			if (library_namespace.is_debug() && matched[1]) {
				// check if data is valid.
				library_namespace
						.warn('parse_CSV: Cannot parse data (無法辨識的資料): ['
								+ matched[1] + ']');
			}

			if (value = matched[2]) {
				if (text_qualifier)
					value = value.replace(text_qualifier_replace_from, '$1');
			} else if (auto_text_qualifier) {
				value = matched[3];
			}

			if (handle_array
					&& title_passed
					&& typeof (tmp = column_count in handle_array ? handle_array[column_count]
							: title_row_for_Object
									&& ((tmp = title_row_for_Object[column_count]) in handle_array)
									&& handle_array[tmp]) === 'function') {
				library_namespace.debug('handle_array(' + value + ')', 2,
						'parse_CSV');
				value = tmp.call(row, value);
			}

			if (title_row_for_Object) {
				if (column_count < title_row_for_Object_length)
					row[title_row_for_Object[column_count]] = value;
				library_namespace.debug('[' + (to_Object === undefined
				//
				|| column_count < to_Object ? row_count
				//
				: row[title_row_for_Object[to_Object]] + '(' + row_count + ')')
						+ '].' + title_row_for_Object[column_count] + ' = ['
						+ value + ']', 3, 'parse_CSV');
			} else {
				row.push(value);
				library_namespace.debug('[' + (to_Object === undefined
				//
				|| column_count < to_Object ? row_count
				//
				: row[to_Object] + '(' + row_count + ')') + '][' + column_count
						+ '] = [' + value + ']', 3, 'parse_CSV');
			}
			column_count++;

			if (matched[auto_text_qualifier ? 4 : 3]) {
				library_namespace.debug('已到行末。', 4, 'parse_CSV');
				if (title_passed) {
					library_namespace.debug('紀錄資料欄數 ' + column_count + '。', 4,
							'parse_CSV');
					if (max_column_count < column_count) {
						max_column_count = column_count;
					} else if (min_column_count > column_count) {
						min_column_count = column_count;
					}

				} else {
					library_namespace.debug('初始化資料欄數紀錄 ' + column_count + '。',
							4, 'parse_CSV');
					min_column_count = max_column_count = column_count;

					if (select_column && !title_row_for_Object) {
						set_title(title_row_for_Object = row);
						// cache
						title_row_for_Object_length = title_row_for_Object.length;
						library_namespace.debug(
								'title_row_for_Object_length = '
										+ title_row_for_Object_length, 3,
								'parse_CSV');
					}
				}

				if (row_limit && row_limit <= row_count)
					break;

				add_row(row);

				// 因為 add_row() 要用到 title_passed，因此這邊才設定。
				if (!title_passed)
					title_passed = row;

				column_count = 0;
				if (title_row_for_Object) {
					row = {};
				} else {
					row = [];
				}

			}
		}
		// add the last row
		add_row(row);

		if (library_namespace.is_debug() && last_index_of_data < data_length) {
			library_namespace
					.warn('parse_CSV: Cannot parse data (無法/尚未辨識的資料): ('
							+ (data_length - last_index_of_data) + ')<br />\n['
							+ data.slice(last_index_of_data) + ']');
		}
		if (library_namespace.is_debug()
				&& min_column_count !== max_column_count) {
			library_namespace.warn('parse_CSV: 資料欄數不同: range: '
					+ min_column_count + '－' + max_column_count);
		}

		if (set_title) {
			// TODO: 自動判別是否有 title。
			set_title(title_passed || row);
		}
		table.max_column_count = max_column_count;

		if (has_title) {
			Object.assign(table, {
				each : each_record,
				map_key : map_key,
				merge : merge_table
			});
		}

		return table;
	}

	parse_CSV.config = default_config;

	_// JSDT:_module_
	.parse_CSV = parse_CSV;

	/**
	 * @example <code>

	var table = CeL.parse_CSV('t1	t2' + 'v11	v12' + 'v21	v22', {
		has_title : true
	});

	table.each(function(get, record, index) {
		console.log(get('t2') + get('t1'));
	});
	// v12v11
	// v22v21

	console.log(table.map_key(function(get) {
		return get('t2') + get('t1');
	}, true));
	// table.key_to_index = {v12v11 : 1, v22v21 : 2};

	table.map_key(function key_mapper(get) { });
	table2.map_key(function key_mapper(get) { });
	table.merge_table(table2);


	 </code>
	 */

	// type : forEach, some, every, map, ...
	// processor(get_field_value, record, index)
	function each_record(processor, _this, type) {
		var table = this, table_index = this.index, is_set_value = type === true;
		return table
		//
		[!is_set_value && type || 'forEach'](function(record, index) {
			if (/* table.has_title && */index === 0)
				return type === 'every' ? true : undefined;

			return processor(is_set_value ? function set_field_value(title,
					value) {
				record[table_index[title]] = value;
			} : function get_field_value(title, allow_undefined) {
				var value = record[table_index[title]];
				return allow_undefined ? value : value || '';
			}, record, index);
		}, _this);
	}

	function map_key(key_mapper, using_index) {
		var key_to_record = {};
		this.each(function(get_field_value, record, index) {
			var key = key_mapper(get_field_value, record, index);
			if (key in key_to_record) {
				library_namespace.error('map_key: Duplicate key [' + key
						+ ']:\n' + key_to_record[key] + '\n→\n' + record);
			}

			// cache key
			record.key = key;
			// record.index = index;
			key_to_record[key] = using_index ? index : record;
		});

		this[using_index ? 'key_to_record' : 'key_to_index'] = key_to_record;
		return key_to_record;
	}

	// 依照欄位標題, key 合併兩資料表格. title_mapper 可選
	// title_mapper[ title of table ] = title of this_table
	function merge_table(table, title_mapper, merge_title_list) {
		var this_table = this;
		// assert: table.has_title && this_table.has_title
		if (!merge_title_list)
			merge_title_list = table.title;

		// merge_title_list: 僅合併這些標題
		merge_title_list.forEach(function(title) {
			if (!(title in this_table.index)) {
				this_table.index[title] = table.max_column_count;
				this_table.title[table.max_column_count++] = title;
			}
		});

		table.each(function(get_field_value, record, index) {
			var key = record.key;
			var this_record = this_table.key_to_record[key];
			merge_title_list.forEach(function(title) {
				var value = get_field_value(title, true);
				if (value === undefined)
					return;

				var this_index = this_table.index[title_mapper[title]]
						|| this_table.index[title],
				//
				old_value = this_record[this_index];
				if (old_value !== undefined && old_value !== value) {
					library_namespace.error('merge_table: "' + key + '"['
							+ title + ']: [' + old_value + '] → [' + value
							+ ']');
				}
				this_record[this_index] = value;
			});
		});
	}

	// ----------------------------------------------------------------------------------------------------------------------------------------------------------//

	/**
	 * 將 {Array|Object} 依設定轉成 CSV text。<br />
	 * 
	 * @example <code>

	 // More examples: see /_test suite/test.js

	 * </code>
	 * 
	 * @param {Array|Object}CSV_object
	 * @param {Object}config
	 *            自訂設定/選項。 see to_CSV_String.config.
	 * 
	 * @returns {String} CSV data
	 * 
	 * @see <a href="http://tools.ietf.org/html/rfc4180">Common Format and MIME
	 *      Type for Comma-Separated Values (CSV) Files</a>
	 * 
	 * @since 2012/4/3 18:26:06 重寫 to_CSV()，改名 to_CSV_String()，功能加強。效能恐降低。
	 */
	function to_CSV_String(CSV_object, config) {
		if (!CSV_object)
			return;

		var i, l,
		//
		CSV = [],
		// 自訂設定
		config = new library_namespace.setting_pair({}, to_CSV_String.config,
				config),
		//
		field_delimiter = config('field_delimiter'),
		//
		select_column = config('select_column'),
		//
		text_qualifier = config('text_qualifier'),
		// [, text_qualifier ]
		text_qualifier_replace_from = text_qualifier !== undefined
				&& new RegExp('([' + text_qualifier + '])', 'g'),
		//
		text_qualifier_replace_to = '$1$1',
		//
		text_qualifier_tester = text_qualifier !== undefined
				&& new RegExp('[' + text_qualifier + ']'),
		//
		field_replace_from = new RegExp('[' + field_delimiter
				+ config('line_separator') + ']', 'g'),
		//
		field_replace_chars = {
			'\t' : '\\t',
			'\n' : '\\n',
			'\r' : '\\r'
		},
		//
		field_replace_to = function($0) {
			library_namespace.debug('field replace: [' + $0 + ']', 2);
			return ($0 in field_replace_chars) ? field_replace_chars[$0] : '\\'
					+ $0;
		},
		//
		handle_field = config('no_text_qualifier') && text_qualifier ? config('no_text_qualifier') === 'auto' ? function(
				field, i) {
			library_namespace.debug('add field (auto): [' + field + ']', 3);
			if (field === undefined || field === null)
				return '';

			if (handle_array && (i in handle_array)
					&& typeof handle_array[i] === 'function')
				field = handle_array[i](field);

			if (typeof field !== 'string')
				field = '' + field;
			return text_qualifier_tester.test(field) ? text_qualifier
					+ field.replace(text_qualifier_replace_from,
							text_qualifier_replace_to) + text_qualifier : field
					.replace(field_replace_from, field_replace_to);
		}
				: function(field, i) {
					library_namespace.debug('add field (no text qualifier): ['
							+ field + ']', 3);
					if (field === undefined || field === null)
						return '';

					if (handle_array && (i in handle_array)
							&& typeof handle_array[i] === 'function')
						field = handle_array[i](field);

					if (typeof field !== 'string')
						field = '' + field;
					return field.replace(field_replace_from, field_replace_to);
				}
				: function(field, i) {
					library_namespace.debug('add field (add text qualifier): ['
							+ field + ']', 3);
					if (field === undefined || field === null)
						return text_qualifier + text_qualifier;

					if (handle_array && (i in handle_array)
							&& typeof handle_array[i] === 'function')
						field = handle_array[i](field);

					if (typeof field !== 'string')
						field = '' + field;
					return text_qualifier
							+ field.replace(text_qualifier_replace_from,
									text_qualifier_replace_to) + text_qualifier;
				},
		//
		add_row,
		//
		handle_array = config('handle_array'),
		//
		handle_row = config('handle_row'),
		// cache
		select_column_length;

		if (typeof select_column === 'number'
				|| typeof select_column === 'string')
			select_column = [ select_column ];
		else if (!Array.isArray(select_column))
			select_column = undefined;
		if (select_column)
			select_column_length = select_column.length;

		if (!Array.isArray(handle_array))
			handle_array = undefined;

		if (typeof handle_row !== 'function')
			handle_row = undefined;

		function add_row_by_selectd(row) {
			var i = 0, array = [], index;
			library_namespace.debug('push ' + select_column_length + ' fields',
					2);
			if (handle_row)
				row = handle_row(row, CSV.length);
			for (; i < select_column_length; i++) {
				index = select_column[i];
				if (library_namespace.is_debug() && !(index in row))
					library_namespace.warn('unknown index: [' + index
							+ '] @ record #' + CSV.length);
				array.push(handle_field(row[index], i));
			}
			library_namespace.debug('push selected: ['
					+ array.join(field_delimiter) + ']', 2);
			CSV.push(array.join(field_delimiter));
		}

		function add_row_by_data(row) {
			var i, l, array = [];
			if (handle_row)
				row = handle_row(row, CSV.length);
			if (Array.isArray(row)) {
				library_namespace.debug('push ' + row.length + ' fields: ['
						+ row + ']', 2);
				for (i = 0, l = row.length; i < l; i++)
					array.push(handle_field(row[i], i));
			} else {
				for (i in row)
					array.push(handle_field(row[i], i));
			}
			library_namespace.debug('push: [' + array.join(field_delimiter)
					+ ']', 2);
			CSV.push(array.join(field_delimiter));
		}

		add_row = select_column ? add_row_by_selectd : add_row_by_data;

		library_namespace.debug('select_column: ' + select_column, 2);
		library_namespace.debug('field_delimiter: ' + field_delimiter, 2);
		library_namespace.debug('no_text_qualifier: '
				+ config('no_text_qualifier'), 2);
		library_namespace.debug('text_qualifier_replace_from: '
				+ text_qualifier_replace_from, 2);
		library_namespace.debug('field_replace_from: ' + field_replace_from, 2);

		if (select_column && config('has_title')) {
			add_row_by_data(select_column);
		}

		if (Array.isArray(CSV_object)) {
			for (i = 0, l = CSV_object.length; i < l; i++) {
				library_namespace.debug('add []: ' + CSV_object[i], 2);
				add_row(CSV_object[i]);
			}

		} else if (library_namespace.is_Object(CSV_object)) {
			for (i in CSV_object) {
				library_namespace.debug('add {}: ' + CSV_object[i], 2);
				add_row(CSV_object[i]);
			}

		} else {
			library_namespace.warn('to_CSV_String: unknown CSV data type: '
					+ CSV_object);
			return;
		}

		return CSV.join(config('line_separator'));
	}

	to_CSV_String.config = (new library_namespace.setting_pair({},
			default_config, {
				title_array : undefined,
				no_text_qualifier : false,
				text_qualifier : '"',
				field_delimiter : ',',
				line_separator : library_namespace.env.line_separator
			}))({});

	_// JSDT:_module_
	.to_CSV_String = to_CSV_String;

	// ----------------------------------------------------------------------------------------------------------------------------------------------------------//

	function speedy_TSV(data, field_delimiter, text_qualifier) {
		data = data.split(text_qualifier || /\r?\n/);

		if (!field_delimiter)
			field_delimiter = '\t';

		for (var i = 0, length = data.length; i < length; i++)
			data[i] = data[i].split(field_delimiter);

		return data;
	}

	speedy_TSV.to_TSV = function(data, field_delimiter) {
		for (var i = 0, length = data.length; i < length; i++)
			data[i] = data[i].join(field_delimiter || '\t');

		return data;
	};

	_.speedy_TSV = speedy_TSV;

	// ----------------------------------------------------------------------------------------------------------------------------------------------------------//

	/**
	 * @example <code>

	 http://hax.pie4.us/2009/05/lesson-of-regexp-50x-faster-with-just.html
	 GetKeywords: function(str) {
		 o: return '\\b(' + str.replace(/\s+/g, '|') + ')\\b';
		 x: return '\\b' + str.replace(/\s+/g, '\\b|\\b') + '\\b';
	 },


	 http://www.jsdb.org/
	 jsdb.from_array
	 jsdb.from_CSV
	 jsdb.from_CSV_file
	 jsdb.select=function(
	 field	//	[1,0,1,1,1] || '1010100' || 'a,b,c,d' || {a:0,b:1,c:1}
	 ,where	//	function(o={a:,b:,c:}){..;return select;} || {a:3} || {a:function(a){..;return select;}} || {a://} || {op:'a&&b||c',a:[3,4,6,11],b:[4,5,6],c:32}
	 )
	 jsdb.concat(table1, table2, id filed/[id fileds] = auto detect)
	 jsdb.from_HTML_TABLE(data,for_every_cell)
	 jsdb.transpose	//	轉置
	 jsdb.to_CSV
	 jsdb.to_HTML_TABLE
	 jsdb.to_array(row_first)
	 jsdb.to_object(row_first)

	 </code>
	 */

	/**
	 * parse CSV data to JSON 讀入 CSV 檔
	 * 
	 * @deprecated 廢棄/棄用: use parse_CSV() instead.
	 * @param {String}
	 *            _t CSV text data
	 * @param {Boolean}
	 *            doCheck check if data is valid
	 * @param {Boolean}
	 *            has_title there's a title line
	 * @return {Array} [ [ L1_1, L1_2, ... ], [ L2_1, L2_2, ... ], ... ]
	 * @_memberOf _module_
	 * @example <code>

	 //	to use:
	 var data=parse_CSV('~');
	 data[_line_][_field_]

	 //	has_title:
	 var data = parse_CSV('~',0,1);
	 //data[_line_][data.t[_title_]]

	 //	then:
	 data.tA	=	title line
	 data.t[_field_name_]	=	field number of title
	 data.it	=	ignored title array
	 data[num]	=	the num-th line (num: 0,1,2,..)

	 * </code>
	 * 
	 * @see <a href="http://www.jsdb.org/" accessdate="2010/1/1 0:53">JSDB:
	 *      JavaScript for databases</a>, <a
	 *      href="http://hax.pie4.us/2009/05/lesson-of-regexp-50x-faster-with-just.html"
	 *      accessdate="2010/1/1 0:53">John Hax: A lesson of RegExp: 50x faster
	 *      with just one line patch</a>
	 */
	function old_parse_CSV(_t, doCheck, has_title) {
		if (!_t || !/[^\n]/.test(_t = _t.replace(/\r\n?/g, '\n')))
			return;
		// _t+=_t.slice(-1)!='\n\n'?'\n':'\n';//if(_t.slice(-1)!='\n')_t+='\n';//if(!/\n/.test(_t))_t+='\n';
		// // 後面一定要[\n]是bug?

		var _f = old_parse_CSV, _r = [], _a, _b = {}, _i = 0, _m = _f.fd,

		/**
		 * <code>
		 Here is a workaround for Opera 10.00 alpha build 1139 bug

		 '\u10a0'.match(/[^\u10a1]+/)
		 and
		 '\u10a0'.match(/[^"]+/)
		 gives different result.
		 The latter should '\u10a0' but it gives null.

		 But
		 '\u10a0'.match(/[^"\u109a]+/)
		 works.

		 </code>
		 */
		c = '\u10a0'.match(/[^"]+/) ? '' : '\u109a';

		_m = '((|[^' + _f.text_qualifier + _m
		// +c: for Opera bug
		+ c + '\\n][^' + _m
		// +c: for Opera bug
		+ c + '\\n]*';

		for (; _i <
		// 這裡不加 _f.text_qualifier 可以 parse 更多狀況
		_f.text_qualifier.length; _i++) {
			_a = _f.text_qualifier.charAt(_i);
			_b[_a] = new RegExp(_a + _a, 'g');
			_m += '|' + _a + '(([^' + _a
			// +c: for Opera bug
			+ c
			// 不用 [^'+_a+']+| 快很多
			+ ']|' + _a + _a + '|\\n)*)' + _a;
		}
		_m += ')[' + _f.fd + '\\n])';
		/**
		 * <code>
		 _m=
		 '((|[^\'"'+_m+'\\n][^'+_m+'\\n]*|"((""|[^"]|\\n)*)"|\'((\'\'|[^\']|\\n)*)\')['+_m+'\\n])'
		 '((|[^\'"'+_m+'\\n$][^'+_m+'\\n$]*|"((""|[^"]|\\n)*)"|\'((\'\'|[^\']|\\n)*)\')['+_m+'\\n$])'
		 _a='((|[^"\''+_f.fd+'\\n][^'+_f.fd+'\\n]*|"((""|[^"]|\\n)*)"|\'((\'\'|[^\']|\\n)*)\')['+_f.fd+'\\n])',alert(_m+'\n'+_a+'\n'+(_m==_a));
		 </code>
		 */
		if (false) {
			alert('now:\n'
					+ new RegExp(_m, 'g').source
					+ '\n\nfull:\n'
					+ /((|[^'",;\t\n$][^,;\t\n$]*|'((''|[^']|\n)*)'|"((""|[^"]|\n)*)")[,;\t\n$])/.source);
		}
		if (doCheck
				&& !new RegExp('^(' + _m + ')+$')
						.test(_t.slice(-1) === '\n' ? _t : _t + '\n'))
			throw new Error(1, "parse_CSV(): Can't parse data!\npattern: /^"
					+ _m + "$/g");

		for (_a = [], _i = 0, _m = (_t.slice(-1) === '\n' ? _t : _t + '\n')
				.match(new RegExp(_m, 'g')); _i < _m.length; _i++) {
			_a.push(_b[_t = _m[_i].charAt(0)] ? _m[_i].slice(1, -2).replace(
					_b[_t], _t) : _m[_i].slice(0, -1));
			// alert('['+_i+'] '+_m[_i]+'|\n'+_a.slice(-1));
			if (_m[_i].slice(-1) === '\n')
				_r.push(_a), _a = [];
		}
		// if(_a.length)_r.push(_a);

		if (typeof has_title === 'undefined')
			has_title = _f.has_title === null ? 0 : _f.has_title;
		if (has_title) {
			// ignored title array
			// library_namespace.debug('parse_CSV(): ');
			_r.it = [];
			while (_a = _r.shift(), _a.length < _r[0].length)
				// 預防 title 有許多行
				_r.it.push(_a);
			for (_r.tA = _a, _b = _r.t = {}, _i = 0; _i < _a.length; _i++)
				_b[_a[_i]] = _i;
		}

		// _r = [ [ L1_1, L1_2, ... ], [ L2_1, L2_2, ... ], ... ]
		return _r;
	}

	/**
	 * field delimiter
	 */
	old_parse_CSV.fd = '\\t,;';// :\s
	/**
	 * text qualifier
	 */
	old_parse_CSV.text_qualifier = '"\'';
	// old_parse_CSV.ld line delimiter: only \n, \r will be ignored.
	/**
	 * auto detect.. no title
	 */
	old_parse_CSV.has_title = null;
	if (false) {
		// data[old_parse_CSV.title_word]=title row array
		old_parse_CSV.title_word = 't';
				old_parse_CSV.fd = ';',
				old_parse_CSV.text_qualifier = '"',
				alert(parse_CSV(
						'"dfdf\nsdff";"sdf""sadf\n""as""dfsdf";sdfsadf;"dfsdfdf""dfsadf";sfshgjk',
						1).join('\n'));
		WScript.Quit();
	}

	// 2007/8/6 17:53:57-22:11:22

	/**
	 * <code>
	 test:
	 'dfgdfg,"fgd",dfg'
	 'dfgdfg,"fgd",dfg'

	 'sdfsdf','ssdfdf'',''sdf'

	 </code>
	 */
	/**
	 * 讀入CSV檔<br /> !! slow !!
	 * 
	 * @since 2007/8/6 17:53:57-22:11:22
	 * @see 可參考 JKL.ParseXML.CSV.prototype.parse_CSV 2007/11/4 15:49:4
	 * @deprecated 廢棄: use parse_CSV() instead.
	 * @param FP
	 *            file path
	 * @param FD
	 *            field delimiter([,;: ]|\s+)
	 * @param text_qualifier
	 *            text qualifier['"]
	 * @param has_title
	 *            the data has a title line
	 * @return Array contains data
	 */
	// readCSVdata[generateCode.dLK]='autodetectEncode,simpleRead,simpleFileAutodetectEncode';
	function readCSVdata(FP, FD, text_qualifier, has_title, enc) {
		var t = simpleRead(FP, enc || simpleFileAutodetectEncode).replace(
				/^[\r\n\s]+/, ''), r = [], reg = {
			'"' : /"?(([^"]+|"")+)"?([,;:	]|[ \r\n]+)/g,
			"'" : /'?(([^']+|'')+)'?([,;:	]|[ \r\n]+)/g
		};
		// detect delimiter
		/**
		 * <code>
		if (!FD || !text_qualifier) {
			var a, b, i = 0, F = '[,;:	\s]', T = '[\'"]', r = new RegExp('(^'
					+ (text_qualifier || T) + '|(' + (text_qualifier || T) + ')('
					+ (FD || F) + ')(' + (text_qualifier || T) + ')|'
					+ (text_qualifier || T) + '$)', 'g');
			F = {}, T = {};
			try {
				t.replace(/(^['"]|(['"])([,;:	\s])(['"])|['"]$)/g, function($0, $1, $2,
						$3, $4) {
					if (!$2)
						T[$0] = (T[$0] || 0) + 1;
					else if ($2 == $4)
						T[$2] = (T[$2] || 0) + 1, F[$3] = (F[$3] || 0) + 1;
					if (i++ > 20)
						break;
					return $0;
				});
			} catch (e) {
			}
			if (!FD) {
				a = b = 0;
				for (i in F)
					if (F[i] > a)
						a = F[b = i];
				FD = b;
			}
			if (!text_qualifier) {
				a = b = 0;
				for (i in T)
					if (T[i] > a)
						a = T[b = i];
				text_qualifier = b;
			}
		}
		</code>
		 */
		if (!text_qualifier) {
			l = t.indexOf('\n');
			if (l === -1)
				t.indexOf('\r');
			l = (l === -1 ? t : t.slice(0, l));
			if (!l.replace(reg['"'], ''))
				text_qualifier = '"';
			else if (!l.replace(reg["'"], ''))
				text_qualifier = "'";
			else
				return;
		}
		reg = reg[text_qualifier];

		l = [];
		if (!has_title)
			r.length = 1;
		(t + '\n').replace(reg, function($0, $1, $2, $3) {
			l.push($1);
			if (/\r\n/.test($3))
				r.push(l), l = [];
			return '';
		});
		if (has_title)
			for (l = 0, r.t = {}; l < r[0].length; l++)
				r.t[r[0][l]] = l;
		return r;
	}

	var default_config_to_CSV;
	if (false) {
		default_config_to_CSV = {
			/**
			 * 標題列
			 */
			title_array : [],
			/**
			 * 欄位分隔符號
			 */
			field_delimiter : ',',
			/**
			 * 文字辨識符號
			 */
			text_qualifier : '"',
			/**
			 * 是否強制加上 text delimiter
			 */
			force_add_text_qualifier : true,
			/**
			 * line delimiter
			 */
			line_delimiter : '\n'
		};
	}

	_// JSDT:_module_
	.
	/**
	 * @deprecated 廢棄: use to_CSV_String() instead.<br />
	 *             config =
	 *             {field_delimiter:',',title_array:[],text_qualifier:'"',force_text_qualifier:true}
	 */
	to_CSV = function(csv_object, config) {
		library_namespace
				.warn('to_CSV is deprecated. Please using to_CSV_String.');
		config = library_namespace.setup_options(config);
		var CSV = [], i = 0, text_qualifier = config.text_qualifier || '"', force_add_text_qualifier = config.force_add_text_qualifier
				&& text_qualifier, line_delimiter = config.line_delimiter
				|| '\n', field_delimiter = config.field_delimiter || ',', text_qualifier_tester, text_qualifier_RegExp, text_qualifier_replace, add_line = function(
				line_array) {
			var i = 0, field = [];
			if (force_add_text_qualifier) {
				for (; i < line_array.length; i++) {
					// 預防 cell 為 null, undefined 等。
					var cell = line_array[i] && String(line_array[i]) || '';
					field.push(cell.replace(text_qualifier_RegExp,
							text_qualifier_replace));
				}
				CSV.push(text_qualifier
						+ field.join(text_qualifier + field_delimiter
								+ text_qualifier) + text_qualifier);
			} else {
				for (; i < line_array.length; i++) {
					// 預防 cell 為 null, undefined 等。
					var cell = line_array[i] && String(line_array[i]) || '';
					field.push(text_qualifier_tester
							&& cell.indexOf(text_qualifier_tester) !== -1
					//
					? text_qualifier
							+ cell.replace(text_qualifier_RegExp,
									text_qualifier_replace) + text_qualifier
					//
					: cell);
				}
				CSV.push(field.join(field_delimiter));
			}
		};

		if (text_qualifier)
			text_qualifier_tester = text_qualifier,
					text_qualifier_RegExp = new RegExp('\\' + text_qualifier,
							'g'), text_qualifier_replace = text_qualifier
							+ text_qualifier;
		else if (line_delimiter === '\n')
			text_qualifier_tester = line_delimiter,
					text_qualifier_RegExp = /\n/g,
					text_qualifier_replace = '\\n';

		if (Array.isArray(config.title_array)
		// && config.title_array.length
		)
			add_line(config.title_array);

		for (; i < csv_object.length; i++)
			add_line(csv_object[i]);

		return CSV.join(line_delimiter);
	};

	/**
	 * <code>
	//	old:
	function quoteCSVfield(t, d) {
		if (!d)
			d = '"';
		for (var i = 0, j, rd = new RegExp(d, 'g'), d2 = d + d; i < t.length; i++) {
			for (j = 0; j < t[i].length; j++)
				if (typeof t[i][j] == 'string')
					t[i][j] = d + t[i][j].replace(rd, d2) + d;
			if (Array.isArray(t[i]))
				t[i] = t[i].join(',');
		}
		return t.join('\n') + '\n';
	}
	 </code>
	 */

	return (_// JSDT:_module_
	);
}
