Files
rappaurio-sae501_502/app/node_modules/cejs/interact/console.js
2023-09-25 13:27:24 +02:00

991 lines
29 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @name CeL function for console
*
* @fileoverview 本檔案包含了 console 操作用的 functions。<br />
* a.k.a. 一般論壇 (BBS) ANSI color 轉換程式。
*
* @since 2015/3/1 13:16:6
*/
'use strict';
// More examples: see /_test suite/test.js
// ------------------------------------------------------------------------------------------------
typeof CeL === 'function' && CeL.run({
name : 'interact.console',
// includes() @ data.code.compatibility.
require : 'data.code.compatibility.|data.native.to_RegExp_pattern',
// 為了方便格式化程式碼,因此將 module 函式主體另外抽出。
code : module_code
});
function module_code(library_namespace) {
// requiring
var to_RegExp_pattern = this.r('to_RegExp_pattern');
/**
* null module constructor
*
* @class console 處理的 functions
*/
var _// JSDT:_module_
= function() {
// null module constructor
};
/**
* for JSDT: 有 prototype 才會將之當作 Class
*/
_// JSDT:_module_
.prototype = {};
// -------------------------------------------------------------
/**
* 本 module 所有 library_namespace.debug() 可能使用到之相關函數,所須使用之最低 debug level。
*
* @type {ℕ⁰:Natural+0}
*
* @see function new_SGR(messages) @ base.js
*/
var min_debug = 3;
// -------------------------------------------------------------
//
/**
* parse style name
*
* @param {String|Object}style_name
* style name to parse
*
* @returns {String}style name
*
* @see <span title="Select Graphic Rendition">SGR</span> parameters
*/
function SGR_style_name(style_name) {
library_namespace.debug({
// gettext_config:{"id":"search-style-name-$1-in-`sgr_code.style_name_alias`"}
T : [ 'Search style name [%1] in `SGR_code.style_name_alias`...',
style_name ]
}, min_debug, 'SGR_style_name');
if (typeof style_name !== 'object'
&& (style_name in SGR_code.style_name_alias))
style_name = SGR_code.style_name_alias[style_name];
if (library_namespace.is_debug() && (typeof style_name === 'object'
//
|| !(style_name in SGR_code.style_data))) {
library_namespace.warn([ 'SGR_style_name: ', {
// gettext_config:{"id":"unknown-style-name-$1"}
T : [ 'Unknown style name: [%1].', style_name ]
} ]);
}
return style_name;
}
/**
* parse style value
*
* @param style_value
* style value to parse
* @param {String}[style_name]
* style name to referrer
*
* @returns {String} style value
*/
function SGR_style_value(style_value, style_name) {
// TODO: using "brightred", "boldred"
// http://git-scm.com/docs/git-config#Documentation/git-config.txt-color
if (typeof style_value !== 'object'
&& (style_value in SGR_code.color_index)) {
if (typeof style_name !== 'string' || !(style_name in color_shift)) {
// Expects value in color_shift.
library_namespace.warn([ 'SGR_style_value: ', {
// gettext_config:{"id":"invalid-name-of-color-$1"}
T : [ 'Invalid name of color: [%1].', style_name ]
} ]);
return;
}
// color_shift[style_name] +
return SGR_code.color_index[style_value];
}
if (style_value in SGR_style_value.alias)
style_value = SGR_style_value.alias[style_value];
if (typeof style_value === 'boolean') {
if ((style_name in SGR_code.style_data)
&& SGR_code.style_data[style_name][style_value ? 0 : 1] === undefined) {
library_namespace.warn([ {
// gettext_config:{"id":"invalid-value-$1-of-style-$2"}
T : [ 'Invalid value [%1] of style: [%2]:',
//
style_value, style_name ]
}, {
// gettext_config:{"id":"unable-to-convert-the-value-to-a-style"}
T : '無法將真偽值轉為樣式。'
} ]);
return;
}
return style_value;
}
if (isNaN(style_value) || (style_value |= 0) < 0) {
library_namespace.warn([ {
// gettext_config:{"id":"invalid-value-$1-of-style-$2"}
T : [ 'Invalid value [%1] of style: [%2]:',
//
style_value, style_name ]
}, {
// gettext_config:{"id":"the-style-value-is-not-a-number"}
T : '欲設定的樣式值並非數字。'
} ]);
return;
}
// do more check.
if ((style_name in SGR_code.style_data)
&& !SGR_code.style_data[style_name].includes(style_value))
if (style_name in color_shift) {
if (style_value >= color_shift[style_name])
style_value -= color_shift[style_name];
} else {
library_namespace.warn([ {
// gettext_config:{"id":"invalid-value-$1-of-style-$2"}
T : [ 'Invalid value [%1] of style: [%2]:',
//
style_value, style_name ]
}, {
// gettext_config:{"id":"the-style-value-is-not-in-the-style-sheet-$1-that-can-be-set"}
T : [ '樣式值不在可設定的樣式資料[%1]中。',
//
SGR_code.style_data[style_name] ]
} ]);
return;
}
return style_value;
}
SGR_style_value.alias = {
'true' : true,
'false' : false
};
/**
* check if the style value is "reset" or not.
*
* @param style_value
* style value to check
*
* @returns {Boolean} the style value is "reset".
*/
function is_reset_style(style_value) {
return style_value === 0 || style_value === '0';
}
/**
* parse and add style to {SGR_style}(this).<br />
* 注意: 一般論壇 (BBS) 使用 VT100遇到 \x20 (space) 即會終止解析而跳出。 但本函式依然會跳過空白而解析之。
*
* @param style
* style to add
*
* @returns {SGR_style}(this)
*/
function SGR_style_add(style) {
if (!style && !is_reset_style(style))
// skip.
return this;
library_namespace.debug({
// gettext_config:{"id":"searching-style-{$2}-$1-in-sgr_code.style_value_alias"}
T : [ 'Searching style {%2} [%1] in SGR_code.style_value_alias...',
typeof JSON === 'object' ? JSON.stringify(style) : style,
typeof style ]
}, min_debug, 'SGR_style_add');
if (typeof style !== 'object')
while (style in SGR_code.style_value_alias) {
library_namespace.debug({
// gettext_config:{"id":"find-style-$1-normalized-to-→-$2"}
T : [ 'Find style [%1] normalized to → [%2]', style,
SGR_code.style_value_alias[style] ]
}, min_debug, 'SGR_style_add');
style = SGR_code.style_value_alias[style];
}
library_namespace.debug({
// gettext_config:{"id":"parse-{$2}-$1-if-it-is-a-primitive-value"}
T : [ 'Parse {%2} [%1] if it is a primitive value.',
typeof JSON === 'object' ? JSON.stringify(style) : style,
typeof style ]
}, min_debug, 'SGR_style_add');
if (typeof style === 'string') {
if (style.includes(SGR_code.separator)) {
style = style.split(SGR_code.separator);
} else if (isNaN(style)) {
library_namespace.debug({
// gettext_config:{"id":"test-if-$1-is-+-style-name"}
T : [ 'Test if [%1] is "[+-] style name".', style ]
}, min_debug, 'SGR_style_add');
// "+bright"
var matched = style.match(/^([+\-])([^=]*?)$/);
if (matched && (matched[2] = SGR_style_name(matched[2].trim()))) {
matched[1] = matched[1] !== '-';
if (undefined === SGR_style_value(matched[1], matched[2]))
library_namespace.warn([ 'SGR_style_add: ', {
// gettext_config:{"id":"invalid-configuration-of-style-$1"}
T : [ 'Invalid configuration of style: [%1].',
// Expects integer.
matched[2] ]
} ]);
else {
library_namespace.debug({
// gettext_config:{"id":"set-style-$1-=-$2"}
T : [ 'Set style "%1" = %2.', matched[2],
matched[1] ]
}, min_debug, 'SGR_style_add');
this[matched[2]] = matched[1];
}
return this;
}
library_namespace.debug({
T : [
// gettext_config:{"id":"test-if-$1-is-style-name-=-style-value-(0-1-false-true-...)"}
'Test if [%1] is "style name = style value (0, 1, false, true, ...)".'
//
, style ]
}, min_debug, 'SGR_style_add');
matched = style.match(/^([^=]+)=(.+)$/);
if (matched && (matched[1] = SGR_style_name(matched[1].trim()))) {
if (undefined !== (matched[2] = SGR_style_value(matched[2]
.trim(), matched[1])))
this[matched[1]] = matched[2];
return this;
}
// 死馬當活馬醫。
style = SGR_style_name(style);
} else {
style |= 0;
}
} else if (typeof style === 'number') {
style |= 0;
}
if (library_namespace.is_Object(style) || style instanceof SGR_style) {
library_namespace.debug({
T : [
// gettext_config:{"id":"parse-{$2}-$1-if-it-is-a-object"}
'Parse {%2} [%1] if it is a object.',
typeof JSON === 'object' ? JSON.stringify(style)
: style, typeof style ]
}, min_debug, 'SGR_style_add');
Object.keys(style).some(
function(style_name) {
var style_value = style[style_name];
if (!(style_name = SGR_style_name(style_name))) {
// 已於 SGR_style_name() 警告過。
return;
}
style_value = SGR_style_value(style_value, style_name);
if (style_value !== undefined) {
library_namespace.debug({
T : [ 'Set style "%1" = [%2].', style_name,
style_value ]
}, min_debug + 1, 'SGR_style_add');
this[style_name] = style_value;
} else if (is_reset_style(style_value)) {
library_namespace.debug({
// gettext_config:{"id":"reset-style-{$2}-$1"}
T : [ 'Reset style {%2} [%1].', style,
typeof style ]
}, min_debug + 1, 'SGR_style_add');
this.to_reset();
return true;
}
}, this);
} else if (Array.isArray(style)) {
style.forEach(function(value) {
this.add(value);
}, this);
} else if (30 <= style && style <= 37) {
this.foreground = style;
} else if (40 <= style && style <= 47) {
this.background = style;
} else if (is_reset_style(style)) {
library_namespace.debug({
// gettext_config:{"id":"reset-style-{$2}-$1"}
T : [ 'Reset style {%2} [%1].', style, typeof style ]
}, min_debug, 'SGR_style_add');
this.to_reset();
} else if (library_namespace.is_debug()) {
library_namespace.warn([ 'SGR_style_add: ', {
// gettext_config:{"id":"unknown-style-$1"}
T : [ 'Unknown style: [%1].', style ]
} ]);
}
return this;
}
/**
* SGR style Class
*
* @param [style]
* style to set
*
* @returns {SGR_style}(this)
*/
function SGR_style(style, options) {
if (false)
if (library_namespace.is_Object(options))
this.options = options;
// 對於 Object.create(null),這邊會出錯。
library_namespace.debug('Set style ['
+ (typeof JSON === 'object' ? JSON.stringify(style) : style)
+ ']', min_debug, 'SGR_style');
this.add(style);
library_namespace.debug('Set style ['
+ (typeof JSON === 'object' ? JSON.stringify(style) : style)
+ '] finished.', min_debug + 1, 'SGR_style');
}
/**
* indicate the color styles.<br />
* 實際上應該列出所有設定值超過一種的樣式/格式名。
*
* @type {Object}
*/
var color_shift = {
foreground : 30,
background : 40
};
Object.assign(SGR_style.prototype, {
add : SGR_style_add,
forEach : function(callback, thisArg) {
// 下面當前不使用。
if (false) {
var style_names = Object.keys(this);
if (this.options)
// remove 'options'.
style_names.splice(style_names.indexOf('options'), 2);
style_names.forEach(callback, thisArg || this);
}
Object.keys(this).forEach(callback, thisArg || this);
},
to_reset : function() {
library_namespace.debug('Reset style.', min_debug,
'SGR_style.prototype.to_reset');
this.forEach(function(style_name) {
delete this[style_name];
});
this.reset = true;
return this;
},
clone : function(options) {
library_namespace.debug('Clone style [' + this + '].', min_debug,
'SGR_style.prototype.clone');
var result = new SGR_style();
this.forEach(function(style_name) {
result[style_name] = this[style_name];
});
if (false)
if (options = options || this.options)
result.options = options;
return result;
},
to_Array : function() {
var array = [];
if (library_namespace.is_debug(min_debug))
library_namespace.debug('style [' + Object.keys(this) + ']', 1,
'SGR_style.prototype.to_Array');
if (false)
for ( var name in this)
library_namespace.debug('[' + name + ']', min_debug + 1);
this.forEach(function(style_name) {
var value = this[style_name];
library_namespace.debug('style "' + style_name + '" ['
+ SGR_code.style_data[style_name] + '] = ('
+ (typeof value) + ') [' + value + ']', min_debug + 1,
'SGR_style.prototype.to_Array');
if (style_name in color_shift) {
if (typeof value === 'boolean')
value = SGR_code.style_data[style_name]
// 0: on, 1: off.
[value ? 0 : 1];
else if (value < color_shift[style_name])
// 正常情況。
value += color_shift[style_name];
else if (isNaN(value))
library_namespace.warn('Invalid ' + style_name
+ ' color [' + value + ']');
} else if (typeof value !== 'number')
// 正常情況。
value = SGR_code.style_data[style_name]
// 0: on, 1: off.
[value ? 0 : 1];
if (typeof value === 'number')
array.push(value);
// else: e.g., reset.
});
library_namespace.debug('style array [' + array + ']',
min_debug + 1, 'SGR_style.prototype.to_Array');
array.sort();
library_namespace.debug('sorted style array [' + array + ']',
min_debug, 'SGR_style.prototype.to_Array');
return array;
},
// (): use default CSI
// (false): No CSI
toString : function(CSI, end_code) {
var code = this.to_Array().join(SGR_code.separator);
library_namespace.debug('code [' + code + ']', min_debug,
'SGR_style.prototype.toString');
// 假如有設定 .reset那應該會有 '0'。
// 否則代表是空的 style此時不回傳以避免被當作 reset。
if (code && (CSI || CSI === undefined))
code = (CSI || SGR_code.CSI) + code
+ (end_code || SGR_code.end_code);
return code;
}
});
// -------------------------------------------------------------
/**
* cf. String.prototype.split ( separator, limit )
*
* @returns [ {SGR_code} ]
*/
function SGR_split(separator, limit) {
// TODO
throw new Error('SGR_split: Not Yet Implemented!');
}
/**
* cf. String.prototype.slice ( start, end )
*
* @returns {SGR_code}
*/
function SGR_slice() {
var result = new SGR_code(this.text.split(start, end));
result.style = this.style.split(start, end);
result.style.forEach(function(style, index) {
result[index] = result[index].clone();
});
return result;
}
/**
* 會改變 (this)!<br />
* cf. Array.prototype.concat ( ...arguments )
*
* @param {String|SGR_code}value
*
* @returns {SGR_code} (this)
*/
function SGR_concat(value) {
var index = 0, length = argument.length,
//
text_list = [ this.text ], value;
for (; index < length; index++)
if (is_SGR_code(value = argument[index])) {
// treat as {SGR_code}
text_list = text_list.join('');
this.style.forEach(function(style, index) {
this.style[text_list.length + index] = style;
}, this);
text_list = [ text_list, value.text ];
} else if (value)
// treat as {String}
text_list.push(value);
this.text = text_list.join('');
return this;
}
/**
* parse styled text.
*
* @param {String}styled_text
* text with style
* @param {Object}[options]
* options : { CSI : '' }
*
* @returns {Array} [ text, style ]
*/
function SGR_parse(styled_text, options) {
// console.log('to_RegExp_pattern: ' + typeof to_RegExp_pattern);
// console.log('to_RegExp_pattern: ' + to_RegExp_pattern);
var text_now = '', style = [], matched, lastIndex = 0,
//
pattern = new RegExp(to_RegExp_pattern(options.CSI || SGR_code.CSI)
+ '([\\d{1,3}\\s\\' + (options.separator || SGR_code.separator)
+ ']*)'
+ to_RegExp_pattern(options.end_code || SGR_code.end_code), 'g');
while (matched = pattern.exec(styled_text)) {
library_namespace.debug(matched.join(), min_debug, 'SGR_parse');
text_now += styled_text.slice(lastIndex, matched.index);
style[text_now.length] = new SGR_style(matched[1] || 0);
lastIndex = pattern.lastIndex;
}
return [ text_now + styled_text.slice(lastIndex), style ];
}
/**
* parse text token with style.
*
* @param {Array}text_Array
* text token and style.<br /> [ {String}text, {Object}style,
* {String}text, .. ]<br />
* 預設 [0] 為 text之後 style, text, style, ... 交替
* @param {Object}[options]
* options : { CSI : '' }
*
* @returns {Array} [ text, style ]
*/
function SGR_parse_list(text_Array, options) {
var text_now = '', style = [],
//
index = 0, length = text_Array.length, text_length;
for (; index < length; index++) {
if (typeof text_Array[index] !== 'object')
text_now += text_Array[index++];
if (index < length)
if (style[text_length = text_now.length])
style[text_length].add(text_Array[index]);
else
style[text_length] = new SGR_style(text_Array[index]);
}
return [ text_now, style ];
}
/**
* get / set SGR style.
*
* @param {integer}index
* index of text
* @param {Object|String|Boolean}[style]
* the style to set.<br />
* undefined: 從頭遍歷,取得某一 index 的 style。比起 `true` 取得的更準確。<br />
* 0: reset style (e.g., normal color)<br />
* null: remove style<br />
* true: 單純取得某一 index 的 style不從頭遍歷。如此取得的可能是錯誤的 style。<br />
* others: 設定 style。
* @param {integer}[to_index]
*
* @returns {SGR_style}
*/
function SGR_style_at(index, style, to_index) {
index |= 0;
if (style === null) {
// e.g., .style_at(index, null)
delete this.style[index];
return;
}
if (style === true) {
// e.g., .style_at(index, true)
return this.style[index];
}
var style_now;
// 從頭遍歷。
library_namespace.debug('Start traversing to ' + index, min_debug,
'SGR_style_at');
this.style.some(function(this_style, this_index) {
if (this_index > index)
return true;
library_namespace.debug('Traversing to index ' + this_index,
min_debug + 1, 'SGR_style_at');
if (style_now) {
// TODO: reduce. 決定最短顯示法。
style_now.add(this_style);
} else
style_now = this_style.clone();
});
if (!style_now)
style_now = new SGR_style();
if (style) {
// 設定
library_namespace.debug('Set style of "' + this.text + '"[' + index
+ '] = [' + style + ']', min_debug, 'SGR_style_at');
if (!this.style[index])
this.style[index] = style_now;
style_now.add(style = new SGR_style(style));
library_namespace
.debug('Set `to_index`', min_debug, 'SGR_style_at');
if (index < to_index) {
// TODO: 更有效率點。
while (index < to_index)
if (this.style[index]) {
var inter_style = this.style[index];
delete inter_style.reset;
style.forEach(function(style_name) {
// 覆寫設定。
delete inter_style[style_name];
})
}
}
} else if (is_reset_style(style)) {
this.style[index] = style_now.add(0);
} else {
// e.g., .style_at(index)
}
return style_now;
}
/**
* get full stylied String.
*
* @param {Boolean}[get_Array]
* get Array, else String.
*
* @returns {String}
*/
function SGR_style_toString(get_Array) {
var sequence = this.text.split('');
this.style.forEach(function(style, index) {
sequence[index] = style.toString()
// 在最後一個 item 時可能只剩 style。
+ (sequence[index] || '');
}, this);
return get_Array ? sequence : sequence.join('');
}
/**
* 測試 value 是否為 SGR_code instance。
*
* @param value
* 測試值。
*
* @returns {Boolean} 測試值為 SGR_code instance
*/
function is_SGR_code(value) {
return value instanceof SGR_code;
}
// -------------------------------------------------------------
/**
* ANSI escape code (or escape sequences) Class
*
* @param {String|Array}text
* text / text token
* @param {Object}[options]
* options : { CSI : '' }
*
* @see <a href="https://en.wikipedia.org/wiki/ANSI_escape_code#graphics"
* accessdate="2015/3/1 8:1" title="ANSI escape code (or escape
* sequences)"><span title="Select Graphic Rendition">SGR</span>
* parameters</a><br />
* <a href="http://vt100.net/docs/vt510-rm/SGR" accessdate="2015/3/1
* 8:1">SGR—Select Graphic Rendition</a>
*/
function SGR_code(text, options) {
if (false)
if (typeof options === 'string')
options = {
CSI : options
};
if (false) {
console.trace([ text, library_namespace.is_Object(text), options,
library_namespace.is_Object(options) ]);
}
if (library_namespace.is_Object(text) && !options) {
// 把 text 當作 options。
options = text;
text = null;
}
if (library_namespace.is_Object(options))
this.options = options;
options = Array.isArray(text) ? SGR_parse_list(text, options
|| Object.create(null))
// String(): 標準化 / normalization
: SGR_parse(String(text), options || Object.create(null));
this.text = options[0];
// {inner}private properties
// this.style[ index ] = new SGR_style();
this.style = options[1];
}
Object.assign(SGR_code.prototype, {
// SGR_style_to_HTML(style_mapping)
// to_HTML : SGR_style_to_HTML,
// style : SGR_style,
slice : SGR_slice,
// split : SGR_split,
concat : SGR_concat,
// TODO:
// splice : SGR_splice,
// chunk : SGR_chunk,
style_at : SGR_style_at,
// toJSON : SGR_style_toString,
toString : SGR_style_toString
});
/**
* ECMA-48 8.3.117 SGR - SELECT GRAPHIC RENDITION<br />
* 為允許使用者參照,因此放在 public。<br />
* 注意: 一般論壇 (BBS) 使用 VT100僅支援如 reset, bold, blink, reverse, foreground,
* background。
*
* @type {Object}
*/
SGR_code.style_data = {
// name : [ on, off ]
// reset / normal
reset : [ 0, ],
// bold or increased intensity (bright / highlight / 高亮)
bold : [ 1, 22 ],
// faint, decreased intensity or second colour
faint : [ 2, 22 ],
// singly underlined (mono only)
underline : [ 4, 24 ],
// blink / 閃爍
// 5: slowly blinking less then 150 per minute,
// 6: rapidly blinking
blink : [ 5, 25 ],
// negative / positive image (反白, reverse type)
reverse : [ 7, 27 ],
// concealed characters (invisible image) 隱瞞,隱匿,隱蔽,隱藏來源
conceal : [ 8, 28 ],
// display (text color, foreground color, 文字部份前景色):
// 3037
foreground : [ , library_namespace.platform.Windows
// Windows console 不支援 39。(e.g. node.js 下)
&& library_namespace.platform.nodejs ? 37 : 39 ],
// background color (背景色 / 底色): 4047
background : [ , 49 ],
// Overlined / Not overlined
overline : [ 53, 55 ]
};
/**
* style name alias<br />
* 為允許使用者添增,因此放在 public。
*/
SGR_code.style_name_alias = {
normal : 'reset',
bright : 'bold',
highlight : 'bold',
hidden : 'conceal',
text : 'foreground',
color : 'foreground',
fg : 'foreground',
bg : 'background'
};
/**
* 添增 color name alias。
*
* @example <code>
// 使設定格式時,可使用中文顏色名稱。
CeL.interact.console.SGR.add_color_alias('黑紅綠黃藍紫青白'.split(''));
* </code>
*
* @param {Array}color_list
* color name list
*/
function add_color_alias(color_list) {
if (Array.isArray(color_list)) {
color_list.forEach(function(color, index) {
SGR_code.color_index[color] = index;
});
}
}
/**
* style value alias: style_value_alias[alias] = normalized<br />
* 為允許使用者添增,因此放在 public。
*/
SGR_code.style_value_alias = Object.create(null);
// SGR_code.color_index.black = 0
SGR_code.color_index = Object.create(null);
SGR_code.color = 'black,red,green,yellow,blue,magenta,cyan,white'
.split(',');
// 鎖定物件。
Object.seal(SGR_code.color);
add_color_alias(SGR_code.color);
// 一些初始設定。
// alias of number
(function() {
for ( var property in SGR_code.style_data) {
var value = SGR_code.style_data[property];
if (typeof value[0] === 'number')
SGR_code.style_value_alias[value[0]] = '+' + property;
if (typeof value[1] === 'number')
SGR_code.style_value_alias[value[1]] = '-' + property;
}
})();
// 鎖定物件。
Object.seal(SGR_code.style_data);
Object.assign(SGR_code, {
min_debug_level : min_debug,
add_color_alias : add_color_alias,
/**
* {String} Control Sequence Introducer, or Control Sequence Initiator.<br />
* "\x1b" : [Esc]<br />
* some 論壇 (BBS): 按 Esc 兩次, '\x1b\x1b['。
*
* @see https://en.wikipedia.org/wiki/ANSI_escape_code#Sequence_elements
*/
CSI : '\x1b[',
// do not modify this value!
default_CSI : '\x1b[',
separator : ';',
end_code : 'm',
is_SGR : is_SGR_code
});
// export
_.SGR = SGR_code;
_.SGR_style = SGR_style;
if (false) {
var SGR_style = CeL.interact.console.SGR_style;
console.log('Showing ' + (new SGR_style('fg=blue')).toString() + 'blue'
+ (new SGR_style({
reset : true
})).toString() + ' text');
// for SGR: @see test.js
}
// ----------------------------------------------------------------------------------------------------------------
function console_status() {
return {
// size. 注意: 這兩個值可能會被覆寫。
width : process.stdout.columns || 80,
height : process.stdout.rows || 25
};
}
_.console_status = console_status;
// ----------------------------------------------------------------------------------------------------------------
// CLI progress bar
// https://github.com/visionmedia/node-progress
// https://github.com/bubkoo/ascii-progress
// https://github.com/AndiDittrich/Node.CLI-Progress
function Progress_bar(max_value, options) {
if (max_value > 0)
this.max_value = max_value;
this.start_time = Date.now();
if (options) {
Object.assign(this, options);
if (this.rtl && !options.start_quote && !options.end_quote) {
var quote = this.end_quote;
this.end_quote = this.start_quote;
this.start_quote = quote;
}
}
}
_.Progress_bar = Progress_bar;
Progress_bar.prototype = {
progress_value : 0,
max_value : 1,
// 方向 direction
rtl : false,
start_quote : ' [',
end_quote : '] ',
use_quote : true,
// single character
filled : '=',
blank : ' ',
show : show_progress,
tick : tick_progress,
// schema. TODO
format : '\r%{pre_text}%{start_quote}%{bar}%{end_quote}%{post_text}'
}
function show_progress(progress_value, post_text, pre_text) {
var max_dots = this.bar_width;
if (!max_dots) {
// - 1: 預防顯示到最後一個字元,會自動跳行。
max_dots = console_status().width - 1;
}
max_dots |= 0;
if (this.pre_text)
max_dots -= this.pre_text.length;
if (this.post_text)
max_dots -= this.post_text.length;
if (this.use_quote) {
max_dots -= this.left_quote.length + this.right_quote.length;
}
// current progress value
this.progress_value = +progress_value || 0;
this.percentage = Math
.round(100 * this.progress_value / this.max_value)
+ '%';
// TODO: .eta
this.completed = this.progress_value >= this.max_value;
var progress_dots = Math.round(max_dots * this.progress_value
/ this.max_value);
var bar_text = [ this.pre_text || '',
this.use_quote && this.start_quote || '',
this.filled.repeat(progress_dots),
this.blank.repeat(max_dots - progress_dots),
this.use_quote && this.end_quote || '',
// suffix
this.post_text || '' ];
if (this.rtl) {
bar_text.reverse();
}
library_namespace.log_temporary(bar_text.join('\n'));
}
function tick_progress(delta, post_text, pre_text) {
this.show(this.progress_value + delta, post_text, pre_text);
}
return (_// JSDT:_module_
);
}