/** * @name CeL data.Convert_Pairs function * @fileoverview 本檔案包含了 data.Convert_Pairs 處理的 functions。 * * TODO: Should use Map() * * @since 2022/2/10 8:43:7 */ 'use strict'; // 'use asm'; // -------------------------------------------------------------------------------------------- typeof CeL === 'function' && CeL.run({ // module name name : 'data.Convert_Pairs', require : 'data.|data.code.compatibility.|data.native.', // 設定不匯出的子函式。 no_extend : '*', // 為了方便格式化程式碼,因此將 module 函式主體另外抽出。 code : module_code }); function module_code(library_namespace) { // requiring // ---------------------------------------------------------------------// // see data.native.String_to_RegExp /** * 主要目的:解析文字 source 成 Object,以及用來作 convert。 * * TODO:
* 整合 application.OS.Windows.file.cacher * * @example // example 1 var conversion_pair = new CeL.data.Convert_Pairs(CeL.get_file(path)); text = conversion_pair.convert(text); // example 2 CeL.run([ 'data.file', 'application.OS.Windows.file' ]); var cache_file = 'work.codes/get_work_information.cache.txt', cache_pair, // target encoding target_encoding = 'UTF-8'; cache_pair = new CeL.data.Convert_Pairs(null, { path : cache_file, encoding : target_encoding, remove_comments : true }); cache_pair.add([ [ 'key 1', 'value 1' ] ]); CeL.log(cache_pair.select('key 1')); cache_pair.save_new(); * * * @param {Object|Array}source * @param {Object}options */ function Convert_Pairs(source, options) { if (!is_Convert_Pairs(this)) { library_namespace .warn('Convert_Pairs: Please use (pair = new Convert_Pairs()) instead of (pair = Convert_Pairs())!'); return new Convert_Pairs(source, options); } // 前置處理。 options = library_namespace.setup_options(options); var _this = this; function copy_properties_from_options() { // 單一 instance 僅能設定一個 replace_flags。 // Convert_Pairs.prototype.add() 不設定 this.flags。 [ 'flags', 'may_remove_pair_Map' ].forEach(function(property) { if (options[property]) { _this[property] = options[property]; } }); } if (is_Convert_Pairs(source)) { // is_clone // library_namespace.is_Object(source) @ Firefox // assert: library_namespace.is_Object(source.pair); // assert: Array.isArray(source.keys); Object.assign(this, source); this.pair_Map = new Map(source.pair_Map); copy_properties_from_options(); } else { copy_properties_from_options(); // Warning: 手動設定 this.pair_Map 非常危險! // initialization. this.pair_Map = new Map; if (source) this.add(source, options); } if (options.path) { this.add_path(options); } } // if the value is instance of Convert_Pairs function is_Convert_Pairs(value) { return value && value.constructor === Convert_Pairs; } /** * 排除/移除注解。 strip/remove javascript comments. * CeL.data.Convert_Pairs.remove_comments(text) * * @see http://vrana.github.io/JsShrink/ * @see http://trinithis.awardspace.com/commentStripper/stripper.html * @see http://upshots.org/javascript/javascript-regexp-to-remove-comments * @see http://marijnhaverbeke.nl//uglifyjs */ function remove_comments(text) { // 僅作最簡單之處理,未考量: "// .. /*", "// .. */", "// /* .. */", // 以及 RegExp, "", '' 中注解的情況! return String(text) // /* ... */ .replace(/\/\*[\s\S]*?\*\//g, '') // // ... .replace(/\/\/[^\r\n]*/g, ''); // text.replace(//g, ''); // TODO: /^#/ } library_namespace.set_method(Convert_Pairs, { remove_comments : remove_comments, is_Convert_Pairs : is_Convert_Pairs, KEY_REMOVE : typeof Symbol === 'function' ? Symbol('remove the key') : { KEY_REMOVE : true } }); // ---------------------------------------------------------------------------------- function get_pair_Map_of_key(key) { // assert: typeof key === 'string' var pair_Map_by_length = this.special_keys_Map && this.pair_Map_by_length, pair_Map; if (pair_Map_by_length) { pair_Map = pair_Map_by_length[key.length]; if (!pair_Map) return; } else { pair_Map = this.pair_Map; } } function Convert_Pairs__get_value(key) { // assert: typeof key === 'string' var pair_Map = get_pair_Map_of_key.call(this, key); return pair_Map && pair_Map.get(key); } function Convert_Pairs__add(source, options) { // 前置處理。 options = library_namespace.setup_options(options); var pair_Map = this.pair_Map; if (library_namespace.is_Object(source)) { for ( var key in source) { if (value === Convert_Pairs.KEY_REMOVE) pair_Map['delete'](key, source[key]); else pair_Map.set(key, source[key]); } delete this.special_keys_Map; return this; } if (typeof source === 'string') { if (options.remove_comments) source = Convert_Pairs.remove_comments(source); // console.trace([ source ]); if (!source.trim()) return; // 順便正規化。 var separator = options.field_separator // || this.field_separator; if (!separator) { // 偵測是 key=value,key=value, // 或 key \t value \n key \t value if (/[\r\n]/.test(source)) separator = /[\r\n]+/; else if (separator = source.match(/[,;|]/)) separator = separator[0]; if (separator) library_namespace.debug('Use field separator: [' + separator + ']', 2, 'Convert_Pairs.add'); } if (separator) source = source.split(separator); else { library_namespace .warn('Convert_Pairs.add: Cannot determine the field separator! ' + source); source = [ source ]; } } if (!Array.isArray(source) || source.length === 0) { return this; } // -------------------------------------------------------------------- var length = source.length, // options: 僅納入 key 與 value 不同之 pair。 no_the_same_key_value = options.no_the_same_key_value, // key / value 分隔符號。 separator = options.separator || this.separator, // key_is_number = options.key_is_number, // value_is_number = options.value_is_number, // item_processor = typeof options.item_processor === 'function' && options.item_processor, // dictionary_path = this.path; if (!separator && typeof source[0] === 'string') { // 遍歷 source 以偵測是 key=value,key=value, // 或 key \t value \n key \t value for (var i = 0; i < length; i++) { if (typeof source[i] === 'string' && (separator = source[i].match(/[^\n]([\t=])[^\n]/))) { separator = separator[1]; library_namespace.debug('Use assignment sign: ' + new RegExp(separator), 3, 'Convert_Pairs.add'); break; } } if (!separator) { // console.trace(source); throw new Error( 'Convert_Pairs.add: No assignment sign detected! 請手動指定!'); } } library_namespace.debug('Add ' + source.length + ' pairs...', 3, 'Convert_Pairs.add'); source.forEach(function(item) { if (item_processor) { item = item_processor(/* {String} */item, options); } if (!item) return; if (false && typeof item === 'string' && !item.trim()) { console.log(item.charCodeAt(0)); console.trace(JSON.stringify(item)); } if (typeof item === 'string') item = item.split(separator); var key = item[0], value = item[1]; if (!key) { library_namespace.warn('Convert_Pairs.add: 未設定 key: ' + item.join(separator)); return; } if (!value) { var matched = key .match(library_namespace.PATTERN_RegExp_replacement); if (matched) { key = '/' + matched[1] + '/' + matched[3]; value = matched[2]; } if (!value) { library_namespace.warn('Convert_Pairs.add: 轉換時將刪除 ' + JSON.stringify(key)); } } library_namespace.debug('adding [' + key + '] → [' // Cannot convert a Symbol value to a string + String(value) + ']', source.length > 200 ? 3 : 2, 'Convert_Pairs.add'); if (key === value) { library_namespace.debug('key 與 value 相同,項目沒有改變:[' + key + ']' // + (dictionary_path ? ' (' + dictionary_path + ')' : ''), 2, 'Convert_Pairs.add'); if (no_the_same_key_value) { return; } // 長度為 1 的沒有轉換必要。 if (no_the_same_key_value !== false && key.length === 1) { // 後來的會覆蓋前面的。 if (pair_Map.has(key)) pair_Map['delete'](key); return; } // 可能是為了確保不被改變而設定。 } if (value === Convert_Pairs.KEY_REMOVE) { library_namespace.debug('Remove [' + key + ']: ' + pair_Map[key], 0, 'Convert_Pairs.add'); // if (pair_Map.has(key)) { } pair_Map['delete'](key); return; } if (key_is_number && !isNaN(key)) key = +key; if (value_is_number && !isNaN(value)) value = +value; if (pair_Map.has(key)) { if (value === pair_Map.get(key)) { library_namespace.info('Convert_Pairs.add: 重複設定相同的[' // + key + ']=[' + value + ']' // + (dictionary_path ? ' (' + dictionary_path + ')' : '')); return; } // 後來的會覆蓋前面的。 if (library_namespace.is_debug(2)) { library_namespace.warn( // 'Convert_Pairs.add: Duplicated key [' + key // + '], value will be changed: [' + pair_Map.get(key) // + '] → [' + String(value) + ']'); } } pair_Map.set(key, value); }); delete this.special_keys_Map; return this; } function Convert_Pairs__remove(key_hash, options) { if (!key_hash) return this; if (typeof key_hash === 'string') key_hash = [ key_hash ]; if (Array.isArray(key_hash)) { var tmp = key_hash; key_hash = Object.create(null); for (var i = 0; i < tmp.length; i++) key_hash[tmp[i]] = null; } var remove_matched_path = options && options.remove_matched_path; var pair_Map = this.pair_Map, path = this.path, changed; // console.trace([ path, key_hash ]); for ( var search_key in key_hash) { // key_hash[key]: ignore path if (key_hash[search_key] === path) { if (remove_matched_path) delete key_hash[search_key]; continue; } var pattern = search_key.match(library_namespace.PATTERN_RegExp); if (pattern) { pattern = new RegExp(pattern[1], pattern[2] || options.flags); library_namespace.debug('Remove pattern: ' + pattern + ' of ' + path, 2, 'Convert_Pairs__remove'); var keys_to_remove = []; pair_Map.forEach(function(value, key) { if (pattern.test(key) || pattern.test(value)) keys_to_remove.push(key); }); if (keys_to_remove.length > 0) { library_namespace.debug(path + '\tRemove ' + keys_to_remove.length + ' keys.', 2, 'Convert_Pairs__remove'); // console.trace(keys_to_remove); keys_to_remove.forEach(function(key) { pair_Map['delete'](key); }); changed = true; } } else { changed = pair_Map['delete'](search_key); } } if (changed) delete this.special_keys_Map; return this; } // add the content of file path function Convert_Pairs__add_path(options) { // console.trace(options); // 前置處理。 options = library_namespace.setup_options(options); var path = options.path; if (Array.isArray(path)) { if (path.length < 2) { path = path[0]; } else { path.forEach(function(file_path) { var _options = library_namespace.new_options(options); if (library_namespace.is_Object(file_path)) { // e.g., for .remove_comments Object.assign(_options, file_path); file_path = file_path.file_path || file_path.path; } _options.path = file_path; this.add_path(_options); }, this); return this; } } // assert: typeof path === 'string' // `path` is file path if (!path || typeof options.file_filter === 'function' && !options.file_filter(path)) { return this; } var source; try { // 注意:此方法不可跨 domain! source = library_namespace.get_file(path); } catch (e) { // TODO: handle exception } if (source) { this.path = path; // 載入 resources。 this.add(source, options); } else { library_namespace .warn('Convert_Pairs.add_path: Cannot get contents of [' + path + ']!'); } return this; } function Convert_Pairs__save(path, encoding, save_new) { if (!library_namespace.write_file) throw new Error('Please include CeL.application.storage first!'); if (path !== this.path) { path = this.path; } else if (!save_new && this.remove_comments) { library_namespace.warn('移除注解後再存檔,會失去原先的注解!請考慮設定 save_new flag。'); } if (!encoding) { encoding = library_namespace.guess_encoding && library_namespace.guess_encoding(path) || library_namespace.open_format.TristateTrue; } library_namespace.debug([ '(' + encoding, ') [', path, ']' ], 2, 'Convert_Pairs.save'); var pair_Map = this.pair_Map; if (pair_Map.size > 0) { var line, data = [], separator = this.separator || '\t'; pair_Map.forEach(function(pair) { var value = pair[1]; if (Array.isArray(value)) value = value.join(separator); data.push(pair[0] + separator + value); }) library_namespace.debug([ save_new ? 'Appending ' : 'Writing ', data.length, ' data to (' + encoding, ') [', path, ']' ], 2, 'Convert_Pairs.save'); library_namespace.debug(data.join('
'), 3, 'Convert_Pairs.save'); library_namespace.write_file(path, // data.join(this.field_separator || library_namespace.env.line_separator), encoding, // save_new ? library_namespace.IO_mode.ForAppending : undefined); } library_namespace.log([ data.length, ' new records saved. [', { // 重新紀錄. a : 'save again', href : '#', onclick : function() { this.save(path, encoding, save_new); return false; }.bind(this) }, ']' ]); return this; } function Convert_Pairs__save_new(path, encoding) { return this.save(path, encoding, true); } // re-generate pattern, this.get_sorted_keys() function Convert_Pairs__pattern(options) { // 前置處理。 options = library_namespace.setup_options(options); var normal_keys = [], flags = options.flags || this.flags, // 若 key 為 RegExp 之 source 時,.length 不代表可能 match 之長度。 // e.g., '([\d〇一二三四五六七八九])米' // 因此特殊 keys 必須放在 special_keys_Map。 special_keys_Map = this.special_keys_Map = new Map, // pair_Map = this.pair_Map; pair_Map.forEach(function(value, key) { // 必須排除掉 "(﹁﹁)" → "(﹁﹁)" if (!library_namespace.PATTERN_RegExp.test(key)) { normal_keys.push(key); return; } try { // console.trace([ key.to_RegExp(flags), value ]); special_keys_Map.set(key, [ key.to_RegExp(flags), value ]); } catch (e) { library_namespace.error('Convert_Pairs__pattern: ' // Error key? + '[' + key + '] → [' // Cannot convert a Symbol value to a string + String(value) + ']: ' + e.message); // normal_keys.push(key); } }); normal_keys.sort(this.comparator); // reset delete this.pair_Map_by_length; delete this.convert_pattern; if (normal_keys.length === 0) { ; } else if (options.generate_pair_Map_by_length) { var pair_Map_by_length = this.pair_Map_by_length = []; normal_keys.forEach(function(key) { var length = key.length; var map = pair_Map_by_length[length]; if (!map) map = pair_Map_by_length[length] = new Map; map.set(key, pair_Map.get(key)); }); } else { try { this.convert_pattern = new RegExp( normal_keys.join('|') || '^$', flags); } catch (e) { // @IE,當 keys 太多太長時, // 若是使用 new RegExp(keys.join('|'), 'g') 的方法, // 可能出現 "記憶體不足" 之問題。 } } // console.log(this.convert_pattern); // 2022/2/13: 17 // console.trace('最長的轉換 key.length=' + pair_Map_by_length.length); if (options.get_normal_keys) return normal_keys; return this.convert_pattern; } function Convert_Pairs__for_each(operator, options) { this.pair_Map.forEach(function(value, key) { operator(key, value); }); return this; } // select the first fitted function Convert_Pairs__select(selector, options) { if (typeof selector !== 'function') { var target = selector || options && options.target; if (!target) return; library_namespace.debug('target: ' + target + ', options: ' + options, 3); if (options === true) { return this.get_value(target); } if (library_namespace.is_RegExp(target)) { selector = function(key, value) { return target.test(key) && value; }; } else { var replace_flags = this.flags; selector = function(key, value) { var pattern; try { pattern = typeof replace_flags === 'function' // ? replace_flags(key) // : new RegExp(key, replace_flags); } catch (e) { // Error key? library_namespace.error('Convert_Pairs.select: key ' + (pattern || '[' + key + ']') + ': ' + e.message); } return pattern.test(target) && value; }; } } // TODO: use `for (const key of this.pair_Map.keys())` for (var pair_Map = this.pair_Map, keys = Array.from(pair_Map.keys()), index = 0; index < keys.length; index++) { var key = keys[index], value = selector(key, pair_Map.get(key)); if (value) return value; } } // @inner function generate_demarcation_points(text_list) { var index = 0, demarcation_points = []; text_list.forEach(function(text_slice) { demarcation_points.push(index += text_slice.length); }); return demarcation_points; } // @inner function convert_using_pair_Map_by_length(text, options) { var pair_Map_by_length = this.pair_Map_by_length, max_key_length = pair_Map_by_length.length, // node.js v17.4.0 採用字串的方法 converted_text_slice += '' 與採用陣列的方法 .push() // 速度差不多。 converted_text_list, converted_text_slice = '', // show_hitted show_matched = options && options.show_matched, // 分界點。 demarcation_points; if (Array.isArray(text)) { demarcation_points = generate_demarcation_points(text); // console.log([ text, demarcation_points ]); converted_text_list = []; text = text.join(''); } else { text = String(text); } // @see // https://github.com/tongwentang/tongwen-core/blob/master/src/converter/map/convert-phrase.ts for (var index = 0, length = text.length; index < length;) { // 本次要測試的文字。 var text_to_convert = text.slice(index, Math.min(length, index + max_key_length)); var text_to_convert_length = text_to_convert.length; while (true) { var map = pair_Map_by_length[text_to_convert_length]; if (map && map.has(text_to_convert)) { // Found. if (show_matched) { library_namespace.info(text_to_convert + '→' + map.get(text_to_convert)); } converted_text_slice += map.get(text_to_convert); break; } if (text_to_convert_length === 1) { // Nothing matched. converted_text_slice += text_to_convert; break; } // 長先短後 詞先字後 text_to_convert = text_to_convert.slice(0, // 截短1字元再做下一輪測試。 --text_to_convert_length); } // console.log(index + '→' + (index + text_to_convert_length)); index += text_to_convert_length; if (!demarcation_points) { continue; } // 依照分界點分割 converted_text_slice。 while (true) { // 先計算還不夠的長度。 var _length = demarcation_points[converted_text_list.length] - index; if (!(_length <= 0)) { break; } // 已經累積足夠的 converted_text_slice。 _length += converted_text_slice.length; converted_text_list .push(converted_text_slice.slice(0, _length)); converted_text_slice = converted_text_slice.slice(_length); } } // console.trace(converted_text_list || converted_text_slice); if (converted_text_list) { // assert: converted_text_slice === '' return converted_text_list; } return converted_text_slice; } // @inner function split_text_by_demarcation_points(text_String, converted_text, demarcation_points) { // 分割 converted_text: 因為 converted_text 可能經過多重 this.special_keys_Map 轉換, // 在 this.special_keys_Map.forEach() 裡面處理 indexes 將會非常複雜。是故採用 CeL.LCS()。 var diff_list = library_namespace.LCS(text_String, converted_text, { line : false, diff : true }); var index_of_demarcation_points = 0, increased_index = 0; // console.trace(diff_list); // console.trace(demarcation_points); diff_list.forEach(function(diff_pair) { var from_index = diff_pair.index[0]; var to_index = diff_pair.index[1]; // console.trace(from_index, to_index, diff_pair.last_index); var increased_in_this_diff = to_index && from_index // ? to_index[1] - from_index[1] - increased_index // : to_index ? to_index[1] - diff_pair.last_index[1] : -(from_index[1] - diff_pair.last_index[0]); // console.trace(increased_in_this_diff, from_index, to_index, // diff_pair.last_index); // 開始的索引差距應該跟上一個結尾的索引差距相同。 // assert: increased_index === to_index[0] - from_index[0] var from_start = from_index ? from_index[0] : diff_pair.last_index[0] + 1; var from_end = from_index ? from_index[1] + 1 : diff_pair.last_index[0] + 1; while (demarcation_points[index_of_demarcation_points] // < from_end) { if (increased_in_this_diff // && demarcation_points[index_of_demarcation_points] // > from_start) { // e.g., a,a → bbb;不能決定到底是 b,bb 還是 bb,b。 var old_index // = demarcation_points[index_of_demarcation_points]; var _diff = old_index - from_start; // converted_text 切割的 index。 var to_i = to_index[0] + _diff; library_namespace.info('Convert_Pairs__convert: 將 [' // + text_String.slice(from_start, old_index) + ',' // + text_String.slice(old_index, from_end) + '] 分割成 [' // + converted_text.slice(to_index[0], to_i) + ',' // + converted_text.slice(to_i, to_index[1] + 1) + ']'); } demarcation_points[index_of_demarcation_points++] // 這樣會將本次增加的 (increased_in_this_diff) 全部排到最後一個。 += increased_index; } // 結尾的索引差距。 increased_index += increased_in_this_diff; }); // console.trace(increased_index, index_of_demarcation_points, // demarcation_points); if (increased_index) { while (index_of_demarcation_points < demarcation_points.length) demarcation_points[index_of_demarcation_points++] += increased_index; } // console.trace(demarcation_points); text_String = demarcation_points.map(function(i, index) { return converted_text.slice( index > 0 ? demarcation_points[index - 1] : 0, i); }); return text_String; } // @inner function adapt_special_keys_Map(text_Array, options) { // show_hitted var show_matched = options && options.show_matched; var demarcation_points = generate_demarcation_points(text_Array); var text_String = text_Array.join(''); var converted_text = text_String; this.special_keys_Map.forEach(function(value, key) { // var pattern = value[0], replace_to = value[1]; if (show_matched && value[0].test(converted_text)) { library_namespace.info(value[0] + ': ' // + converted_text + '→' + converted_text.replace(value[0], value[1])); } // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#specifying_a_string_as_a_parameter converted_text = converted_text.replace(value[0], value[1]); // 在這裡呼叫 split_text_by_demarcation_points() 可增加精準度,但大大降低效能。 }); // assert: demarcation_points.at(-1) === text_String.length if (text_String === converted_text) { return text_Array; } return split_text_by_demarcation_points(text_String, converted_text, demarcation_points); } // @see function LCS_length() // https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Errors/Invalid_array_length // 設定小一點,LCS() 的時候才不會浪費太多時間。 var SOFT_SLICE_LIMIT = 1000; // HARD_SLICE_LIMIT < 65536 但必須預防有些轉換把長度拉長了。 var HARD_SLICE_LIMIT = 65530; // console.assert(SOFT_SLICE_LIMIT < HARD_SLICE_LIMIT); // console.assert(HARD_SLICE_LIMIT ** 2 < 2 ** 32 - 1); var using_pair_Map_by_length = true; function Convert_Pairs__convert(text, options) { if (false && this.pair_Map) { library_namespace.debug( // 'Convert ' + String(text).length + ' characters, using ' + this.pair_Map.size + ' pairs with replace_flags [' + this.replace_flags + '].', 3, // Convert_Pairs.convert 'Convert_Pairs__convert'); } if (!this.special_keys_Map) { this.pattern({ flags : this.replace_flags, generate_pair_Map_by_length : using_pair_Map_by_length }); if (this.may_remove_pair_Map) { library_namespace.debug('在開始轉換之後就不會再修改辭典檔,因此可移除 .pair_Map。', 1, 'Convert_Pairs__convert'); delete this.pair_Map; } } // console.trace(this.convert_pattern); // console.trace(this.special_keys_Map); // show_hitted var show_matched = options && options.show_matched; // 長先短後 詞先字後 if (this.pair_Map_by_length) { // console.trace(text); text = convert_using_pair_Map_by_length.call(this, text, options); } else if (this.convert_pattern) { text = String(text).replace(this.convert_pattern, function(token) { // library_namespace.info(token + '→' + // pair_Map.get(token)); return pair_Map.get(token); }); } // ---------------------------------------------------------- // console.trace([ text, this.special_keys_Map ]); if (!Array.isArray(text) // Nothing to do. || this.special_keys_Map.size === 0) { // assert: typeof text === 'string' this.special_keys_Map.forEach(function(value, key) { // var pattern = value[0], replace_to = value[1]; if (show_matched && value[0].test(text)) { library_namespace.info(value[0] + ': ' + text + '→' + text.replace(value[0], value[1])); } text = text.replace(value[0], value[1]); }); return text; } // assert: Array.isArray(text) // console.trace([ text, this.special_keys_Map ]); var converted_text = []; // 避免 RangeError: Invalid typed array length: // @see function LCS_length() for (var index = 0; index < text.length;) { var latest_index = index, this_slice_length = 0; // 限制長度在這個之內。 while (index < text.length && this_slice_length < SOFT_SLICE_LIMIT) this_slice_length += text[index++].length; while (index < text.length) { this_slice_length += text[index].length; if (this_slice_length >= HARD_SLICE_LIMIT) { // 真的太長還是得強制截斷。警告: 這可能造成沒有辦法匹配的錯誤情況。 break; } // 延伸到句子結尾。 if (/(?:[。?!…」]|\/>)[\s\n]*$/.test(text[index])) break; index++; } if (false) { console.log(text.slice(index - 20, index)); console.trace(latest_index + '-' + index + '/' + text.length + ': ' + this_slice_length); } converted_text.append(adapt_special_keys_Map.call(this, text.slice( latest_index, index), options)); } // console.assert(converted_text.length === text.length); return converted_text; } // reverse conversion, 改成 value → key function Convert_Pairs__reverse(options) { // 前置處理。 options = library_namespace.new_options(options); options.ignore_null_value = true; this.pair_Map = this.pair_Map.reverse_key_value(options); delete this.special_keys_Map; return this; } function Convert_Pairs__clone(options) { return new Convert_Pairs(this, options); } function Convert_Pairs__to_Object(source) { var object = Object.create(null); this.pair_Map.forEach(function(value, key) { object[key] = value; }); return object; } function Convert_Pairs__comparator(key_1, key_2) { // 排序:長的 key 排前面。 long → short var diff = key_2.length - key_1.length; return diff !== 0 ? diff // assert: key_1 !== key_2 : key_1 < key_2 ? -1 : 1; } // ---------------------------------------------------------------------// // export 導出. library_namespace.set_method(Convert_Pairs.prototype, { get_value : Convert_Pairs__get_value, add : Convert_Pairs__add, remove : Convert_Pairs__remove, add_path : Convert_Pairs__add_path, save : Convert_Pairs__save, save_new : Convert_Pairs__save_new, pattern : Convert_Pairs__pattern, // for each pair for_each : Convert_Pairs__for_each, select : Convert_Pairs__select, // convert from key to value. convert : Convert_Pairs__convert, reverse : Convert_Pairs__reverse, clone : Convert_Pairs__clone, to_Object : Convert_Pairs__to_Object, comparator : Convert_Pairs__comparator, /** * {String} key-value 分隔符號. */ // separator : '\t', /** * {String} 欄位分隔符號. */ // field_separator : /[\r\n]+/, /** default RegExp replace flags: global match, or 'ig' */ flags : 'g' }); return Convert_Pairs; }