Reflect.defineProperty(String.prototype, 'decodeAtoB', { get(this: String){ return decodeURIComponent(atob(this.valueOf())); }, enumerable: true, configurable: false, }); Reflect.defineProperty(String.prototype, 'encodeBtoA', { get(this: String){ return btoa(encodeURIComponent(this.valueOf())); }, enumerable: true, configurable: false, }); Number.prototype.inRange = function(this: Number, a, b){ let value = this.valueOf(); if ([value, a, b].includes(NaN)) return false; let [va, vb] = [value - a, value - b].map(v => [-Infinity, Infinity].includes(v) ? v<0 ? -1 : 1 : v); return va * vb <= 0; } Reflect.defineProperty(Array.prototype, 'lastElement', { get(this: Array){ return this.length>0 ? Reflect.get(this, this.length-1) : undefined; }, set(this: Array, value: any){ Reflect.set(this, this.length>0 ? this.length-1 : 0, value); }, enumerable: true, configurable: false, }); Array.prototype.forWait = async function(this: Array, call){ for(let i = 0; i < this.length; i++) await call(this[i], i, this); } // 检测全局命名空间是否存在 const __Global__ = window ?? globalThis; if(!Reflect.has(__Global__, 'mtec')){ Reflect.set(__Global__, 'mtec', {}); Reflect.set(mtec, '__mtec__', 'Many technologies'); } // 初始化子命名空间 ['string', 'number', 'array', 'log', 'color', 'local', 'time'] .forEach(space_name=>{ if(!Reflect.has(mtec, space_name)){ Reflect.set(mtec, space_name, {}); Reflect.set(Reflect.get(mtec, space_name), '__space_description__', 'This is a mtec subnamespace for ' + space_name); } }); mtec.vtype = function(value){ let type = typeof value; return type=='object' ? value==null ? 'null' : Array.isArray(value) ? 'array' : 'object' : type; } mtec.same = function(a, b, record){ if(a===b) return true; let [ta, tb] = [a, b].map(_=>mtec.vtype(_)); if(ta!=tb) return [ta, tb].every(t=>['number', 'bigint'].includes(t)) ? a==b : false; if(!['array', 'object'].includes(ta)) return a==b; if(!record) record = {cache: [], mark: new Map(), list: []}; let token = [a, b].map(v=>{ if(record.mark.has(v)) return record.mark.get(v); let mark = mtec.string.randomToken(3, 36, _t=>!record.list.includes(_t)); record.list.push(mark); record.mark.set(v, mark); return mark; }).sort().join('::'); if(record.cache.includes(token)) return true; record.cache.push(token); let [key_a, key_b] = [a, b].map(v=>Reflect.ownKeys(v).filter(k=>v[k]!=undefined && v[k]!=null).sort()); if(key_a.length==key_b.length && key_a.every((k, i)=>k==key_b[i])) return key_a.every(k=>mtec.same(a[k], b[k], record)); return false; } mtec.pickValueByType = function(values: any[], types: T){ let map = values.reduce((m, v)=>{ let t = mtec.vtype(v); if(m[t]==undefined) m[t] = []; Array.prototype.unshift.call(m[t], v); return m; }, {}); return types.map(picker=>{ let v = Reflect.get(map, picker[0]); return v ? v.length>1 ? Array.prototype.pop.call(v) : v[0] : picker[1]; }) as mtec.TypeArray>; } mtec.delay = function(delay, call, ...args){ return new Promise((s, j)=>{ let id = setTimeout((c, a)=>{ clearTimeout(id); s(typeof c=='function' ? c(...a) : c); }, delay * 1000, call, args) }); } mtec.drawLots = function(bucket){ if(bucket.length<=0) return void 0; if(bucket.length==1) return bucket[0][0]; let data_list = []; let weight_list = []; let total = 0; for(let [data, weight] of bucket){ let v = Number(weight); if(!(v > 0)) continue; total += v; data_list.push(data); weight_list.push(total); } let point = Math.random() * total; let i = 0; while(point >= weight_list[i]) i++; return data_list[i]; } mtec.fusionData = function(target, ...list){ if(target instanceof Object){ if(Array.isArray(target)){ //@ts-ignore list.forEach(data=>Array.isArray(data) ? target.push(...data) : target.push(data)); }else if(!(target instanceof Function)){ let ls = list.filter(data=>data instanceof Object && !(data instanceof Function)); let map = ls.reduce((m, data, i)=>{ Reflect.ownKeys(data).forEach(key=>m.set(key, i)); return m; }, new Map()); //@ts-ignore map.forEach((i, key)=>Reflect.set(target, key, Reflect.get(ls[i], key))); } }else{ if(typeof target == 'bigint'){ list.forEach(data=>{ switch(typeof data){ //@ts-ignore case 'boolean': data = Number(data); case 'object': case 'undefined': data = BigInt(0); case 'number': if(isNaN(data)) data = 0; data = BigInt(data); break; //@ts-ignore case 'symbol': data = String(data); break; } // @ts-ignore target += data; }) }else if(['string', 'symbol'].includes(typeof target)){ //@ts-ignore target = String(target); //@ts-ignore list.forEach(data=>target += (typeof data=='symbol' ? String(data) : data)); }else{ list.forEach(data=>{ switch(typeof data){ //@ts-ignore case 'bigint': target = Number(target); if(isNaN(target)) target = BigInt(0); break; //@ts-ignore case 'symbol': data = String(data); } //@ts-ignore target += data; }) } } return target as any; } mtec.cloneData = function(data, record){ let t = mtec.vtype(data); if(['string', 'number', 'boolean', 'bigint', 'undefined', 'null'].includes(t)) return data; else if(t == 'function') return Function.prototype.bind.call(data, null); else if(t == 'symbol') return Symbol(data['description']); else{ if(!record) record = new Map(); if(record.has(data)) return record.get(data); let target; if(t=='array') target = []; else target = {}; record.set(data, target); //@ts-ignore if(t=='array') data.forEach(el=>target.push(mtec.cloneData(el))); else{ //@ts-ignore Reflect.ownKeys(data).forEach(key=>Reflect.set(target, key, Reflect.get(data, key))); //@ts-ignore Reflect.setPrototypeOf(target, Reflect.getPrototypeOf(data)); } return target; } } mtec.CountAverage = class{ private count = 0; private _average: number; constructor(init?: number){ if(init) this.add(init); } public get average(){ return this._average ?? 0; } public add(value: number){ this.count++; this._average = this.average + (value - this.average) / this.count; return this._average; } public clean(){ this.count = 0; this._average = 0; } } mtec.NudityPromise = class{ private inited: boolean; private status: 'waiting'|'resolve'|'reject'; private result: any; private _resolve_call_: (value: V | PromiseLike)=>void; private _reject_call_: (reason: any)=>void; public promise: Promise; constructor(){ this.inited = false; this.status = 'waiting'; this.promise = new Promise((s, j)=>{ this._resolve_call_ = s; this._reject_call_ = j; this.inited = true; this.reply(); }); } public resolve(value: V | PromiseLike){ this.re_call('resolve', value); } public reject(reason: any){ this.re_call('reject', reason); } private re_call(status: 'resolve'|'reject', result: any){ if(this.status=='waiting'){ [this.status, this.result] = [status, result]; this.reply(); } } private reply(){ if(this.inited && this.status!='waiting'){ switch(this.status){ case 'resolve': this._resolve_call_(this.result); break; case 'reject': this._reject_call_(this.result); break; } } } } mtec.JsonString = function(data){ let tp = typeof data; if(tp != 'object') return JSON.stringify(data); if(data == null) return 'null'; let copy_map: Map = new Map(); let token_map: Map = new Map(); let token_cache = []; let data_list = [data]; copy_map.set(data, Array.isArray(data) ? [] : {}); token_map.set(data, mtec.string.randomToken(5, 36, tlkn=>!token_cache.includes(tlkn))); for(let i = 0; i < data_list.length; i++){ let item = data_list[i]; let copy = copy_map.get(item); let key_list: Array = Array.isArray(item) ? mtec.array.create(item.length, i=>i) : Reflect.ownKeys(item); let data_flag: Array<[string, string|number|symbol]> = []; for(let key of key_list){ let value = Reflect.get(item, key); if(typeof value != 'object') Reflect.set(copy, key, value); else if(value == null) Reflect.set(copy, key, null); else if(token_map.has(value)) Reflect.set(copy, key, '<-[ref]->'+token_map.get(value)); else{ let token = mtec.string.randomToken(5, 36, t=>!token_cache.includes(t)); let cp = Array.isArray(value) ? [] : {}; token_map.set(value, token); copy_map.set(value, cp); data_list.push(value); if(Array.isArray(item)){ Reflect.set(copy, key, cp); data_flag.push([token, key]); }else Reflect.set(copy, token+'<-[data]->'+String(key), cp); } } if(data_flag.length>0 && Array.isArray(item)) copy.push(data_flag.map(el=>el[0] + '<-[data]->' + String(el[1])).join(';')); } return token_map.get(data) + '<-[data]->' + JSON.stringify(copy_map.get(data)); } mtec.parseJson = function(json_string){ let token: string; let data: any; if(json_string.includes('<-[data]->')) [token, json_string] = json_string.replace('<-[data]->', '->[data]<-').split('->[data]<-'); try{ data = JSON.parse(json_string); }catch(err){ data = json_string; } if(typeof data != 'object') return data; let data_map: Map = new Map(); data_map.set(token, data); let data_list = [data]; for(let i = 0; i < data_list.length; i++){ let item = data_list[i]; if(Array.isArray(item)){ if(typeof item.lastElement == 'string' && item.lastElement.includes('<-[data]->')){ (item.pop() as string).split(';').map(item=>{ let [token, index] = item.split('<-[data]->'); return [token, Number(index)] as [string, number]; }).forEach(el=>data_map.set(el[0], item[el[1]])); } item.forEach((el, i, arr)=>{ if(typeof el == 'string' && el.includes('<-[ref]->')){ let token = el.replace('<-[ref]->', ''); arr[i] = data_map.get(token); }else if(typeof el == 'object') data_list.push(el); }); }else if(item != null){ Reflect.ownKeys(item).forEach(key=>{ let value = Reflect.get(item, key); if(typeof value == 'object') data_list.push(value); else if(typeof value == 'string' && value.includes('<-[ref]->')){ let token = value.replace('<-[ref]->', ''); value = data_map.get(token); Reflect.set(item, key, value); } if(typeof key == 'string' && key.includes('<-[data]->')){ Reflect.deleteProperty(item, key); let [token, k] = key.split('<-[data]->'); Reflect.set(item, k, value); data_map.set(token, value); } }); } } return data; } mtec.string.random = function(len, radix){ [len, radix] = [[Number(len), 6], [Number(radix), 10]].map(_=>isNaN(_[0]) ? _[1] : _[0]); radix = Math.max(2, Math.min(36, radix)); let str = Math.random().toString(radix).slice(2, 2+len); while(str.length < len){ str += Math.random().toString(radix).slice(2, 2+len-str.length); } return str; } mtec.string.randomToken = function(len, radix, verify){ [radix, verify] = mtec.pickValueByType([radix, verify], [['number', 10], ['function', undefined]]) as [number, (token: string)=>boolean]; let token = mtec.string.random(len, radix); if(typeof verify == 'function'){ while(!verify(token)){ token = mtec.string.random(len, radix); } } return token; } mtec.string.getAffix = function(list){ if(!list || list.length==0) return {prefix: '', suffix: '', max_length: 0}; let prefix = list[0]; let suffix = list[0]; let pre_end = prefix.length; let suf_start = 0; let max_length = 0; list.forEach(el=>{ if(el.length>max_length) max_length = el.length; if(!el.includes(prefix)){ while(pre_end>0 && !el.includes(prefix.slice(0, pre_end))) pre_end--; prefix = prefix.slice(0, pre_end); } if(!el.includes(suffix)){ while(suf_startNumber(_)); if([num, a, b].includes(NaN)) return false; return num <= Math.max(a, b) && num >= Math.min(a, b); } mtec.number.getPrecision = function(num){ num = Number(num); if(isNaN(num) || Number.isInteger(num)) return 0; return (num % 1).toString().length - (num>=0 ? 2 : 3); } mtec.number.repair = function(num){ num = Number(num); if(isNaN(num)) return 0; if(Number.isInteger(num)) return num; let str = num.toString(); if(str.length-str.indexOf('.') < 17) return num; let index = str.length-1; if(str[index]=='9') while(str[index]=='9') index--; else{ index--; while(str[index]=='0') index--; } let weight = 10 ** (index-str.indexOf('.')); return Math.round(num * weight) / weight; } mtec.number.random = function(a, b, precision){ [a, b] = [a, b].map(_=>{ _ = Number(_); return isNaN(_) ? 0 : _; }); let range = b-a; if(precision==undefined){ if(Math.abs(range)==1) precision = 2; else if(!Number.isInteger(range)){ range = mtec.number.repair(range); precision = mtec.number.getPrecision(range); if(range == 10 ** -precision) precision += 2; }else precision = 0; } let weight = 10 ** precision; return mtec.number.repair(a + Math.round(Math.random() * range * weight) / weight); } mtec.number.parse = function(value, spare){ [spare, value] = [spare, value].map(_=>typeof _=='number' ? _ : Number(_)); return isNaN(value) ? isNaN(spare) ? 0 : spare : value; } mtec.number.fixedNum = function(num, place, floor){ let weight = 10 ** Math.round(mtec.number.parse(place)); num = num * weight; num = floor===true ? Math.floor(num) : floor===false ? Math.ceil(num) : Math.round(num); return mtec.number.repair(num / weight); }; mtec.number.limit = function(num, a, b){ [num, a, b] = [num, a, b].map(_=>mtec.number.parse(_, 0)); return Math.max(Math.min(a, b), Math.min(Math.max(a, b), num)); }; mtec.array.random = function(list){ if(!Array.isArray(list)) return list; if(list.length==0) return undefined; if(list.length==1) return list[0]; return list[Math.floor(Math.random() * list.length)]; } //@ts-ignore mtec.array.randomeElement = function(list, len, repetition){ len = len ?? 1; if(repetition===false) len = Math.min(len, list.length); repetition = repetition ?? len>list.length; let index_list: number[] = []; while(index_list.length < len){ let index = mtec.number.random(0, list.length-1); if(!repetition) while(index_list.includes(index)) index = mtec.number.random(0, list.length-1); index_list.push(index); } return index_list.map(index=>list[index]); } mtec.array.last = function(list, index){ if(!Array.isArray(list)) return list; if(list.length==0) return undefined; if(index){ index = Number(index); index = Math.floor(isNaN(index) ? 0 : index) + 1; index = Math.max(1, Math.min(list.length, index)); }else index = 1; return list[list.length-index]; } //@ts-ignore mtec.array.clear = function(list){ return (Array.isArray(list) && list.length>0) ? list.splice(0, list.length) : []; } mtec.array.remove = function(arr: any[], element: any, head?: boolean){ let verify: (el: any, i: number, arr: any[])=>boolean; if(element instanceof Function) verify = element; else verify = (el: any, i: number, arr: any[])=>el==element; let [point, end, direction] = (head??true) ? [0, arr.length, 1] : [arr.length-1, -1, -1]; while(point!=end){ if(verify(arr[point], point, arr)) break; point += direction; } return point.inRange(0, arr.length-1) ? arr.splice(point, 1)[0] : undefined; } mtec.array.create = function(size, call){ return new Array(size).fill(null, 0, size).map((_, i)=>call(i)); } mtec.color.common = { aliceblue: '#f0f8ff', antiquewhite: '#faebd7', aqua: '#00ffff', aquamarine: '#7fffd4', azure: '#f0ffff', beige: '#f5f5dc', bisque: '#ffe4c4', black: '#000000', blanchedalmond: '#ffebcd', blue: '#0000ff', blueviolet: '#8a2be2', brown: '#a52a2a', burlywood: '#deb887', cadetblue: '#5f9ea0', chartreuse: '#7fff00', chocolate: '#d2691e', coral: '#ff7f50', cornflowerblue: '#6495ed', cornsilk: '#fff8dc', crimson: '#dc143c', cyan: '#00ffff', darkblue: '#00008b', darkcyan: '#008b8b', darkgoldenrod: '#b8860b', darkgray: '#a9a9a9', darkgreen: '#006400', darkkhaki: '#bdb76b', darkmagenta: '#8b008b', darkolivegreen: '#556b2f', darkorange: '#ff8c00', darkorchid: '#9932cc', darkred: '#8b0000', darksalmon: '#e9967a', darkseagreen: '#8fbc8f', darkslateblue: '#483d8b', darkslategray: '#2f4f4f', darkturquoise: '#00ced1', darkviolet: '#9400d3', deeppink: '#ff1493', deepskyblue: '#00bfff', dimgray: '#696969', dodgerblue: '#1e90ff', firebrick: '#b22222', floralwhite: '#fffaf0', forestgreen: '#228b22', fuchsia: '#ff00ff', gainsboro: '#dcdcdc', ghostwhite: '#f8f8ff', gold: '#ffd700', goldenrod: '#daa520', gray: '#808080', green: '#008000', greenyellow: '#adff2f', honeydew: '#f0fff0', hotpink: '#ff69b4', indianred: '#cd5c5c', indigo: '#4b0082', ivory: '#fffff0', khaki: '#f0e68c', lavender: '#e6e6fa', lavenderblush: '#fff0f5', lawngreen: '#7cfc00', lemonchiffon: '#fffacd', lightblue: '#add8e6', lightcoral: '#f08080', lightcyan: '#e0ffff', lightgoldenrodyellow: '#fafad2', lightgray: '#d3d3d3', lightgreen: '#90ee90', lightpink: '#ffb6c1', lightsalmon: '#ffa07a', lightseagreen: '#20b2aa', lightskyblue: '#87cefa', lightslategray: '#778899', lightsteelblue: '#b0c4de', lightyellow: '#ffffe0', lime: '#00ff00', limegreen: '#32cd32', linen: '#faf0e6', magenta: '#ff00ff', maroon: '#800000', mediumaquamarine: '#66cdaa', mediumblue: '#0000cd', mediumorchid: '#ba55d3', mediumpurple: '#9370db', mediumseagreen: '#3cb371', mediumslateblue: '#7b68ee', mediumspringgreen: '#00fa9a', mediumturquoise: '#48d1cc', mediumvioletred: '#c71585', midnightblue: '#191970', mintcream: '#f5fffa', mistyrose: '#ffe4e1', moccasin: '#ffe4b5', navajowhite: '#ffdead', navy: '#000080', oldlace: '#fdf5e6', olive: '#808000', olivedrab: '#6b8e23', orange: '#ffa500', orangered: '#ff4500', orchid: '#da70d6', palegoldenrod: '#eee8aa', palegreen: '#98fb98', paleturquoise: '#afeeee', palevioletred: '#db7093', papayawhip: '#ffefd5', peachpuff: '#ffdab9', peru: '#cd853f', pink: '#ffc0cb', plum: '#dda0dd', powderblue: '#b0e0e6', purple: '#800080', rebeccapurple: '#663399', red: '#ff0000', rosybrown: '#bc8f8f', royalblue: '#4169e1', saddlebrown: '#8b4513', salmon: '#fa8072', sandybrown: '#f4a460', seagreen: '#2e8b57', seashell: '#fff5ee', sienna: '#a0522d', silver: '#c0c0c0', skyblue: '#87ceeb', slateblue: '#6a5acd', slategray: '#708090', snow: '#fffafa', springgreen: '#00ff7f', steelblue: '#4682b4', tan: '#d2b48c', teal: '#008080', thistle: '#d8bfd8', tomato: '#ff6347', turquoise: '#40e0d0', violet: '#ee82ee', wheat: '#f5deb3', white: '#ffffff', whitesmoke: '#f5f5f5', yellow: '#ffff00', yellowgreen: '#9acd32', }; mtec.color.toHexColor = function(color){ let hex: string; if(color.startsWith('#')) hex = color.slice(1, 7); else if(!isNaN(Number('0x'+color))) hex = color.slice(0, 6); if(hex && [3, 4, 6, 8].includes(hex.length)) return mtec.color.normalizeHexColor('#' + hex); color = color.toLowerCase(); color = Reflect.has(mtec.color.common, color) ? color : 'white'; return Reflect.get(mtec.color.common, color); } mtec.color.normalizeHexColor = function(color){ let hex: string; if(color.startsWith('#') && !isNaN(Number('0x'+color.slice(1, color.length)))) hex = color.replace('#', ''); else hex = mtec.color.toHexColor(color).replace('#', ''); if([3, 4].includes(hex.length)) hex = hex.split('').map(bit=>bit+bit).join(''); if(hex.length>6) hex = hex.substring(0, 6); else hex.padEnd(6, '0'); return '#' + hex.toUpperCase(); } mtec.color.toRGBColor = function(color){ let hex = mtec.color.normalizeHexColor(color); let [r, g, b] = hex.slice(1, hex.length).match(/.{1,2}/g).map(c=>parseInt(c, 16)); return [r, g, b]; } mtec.color.toHSLColor = function(color){ let [r, g, b] = mtec.color.toRGBColor(color); let max = Math.max(r, g, b); let min = Math.min(r, g, b); let h, s, l = (max + min) / 2; if (max === min) { // 如果最大值和最小值相等,表示颜色是灰度的,色相设为0 h = s = 0; } else { let d = max - min; s = l > 0.5 ? d / (2 - max - min) : d / (max + min); switch (max) { case r: h = (g - b) / d + (g < b ? 6 : 0); break; case g: h = (b - r) / d + 2; break; case b: h = (r - g) / d + 4; break; } h /= 6; } return [h * 360, s * 100, l * 100].map(v=>Math.round(v)) as [number, number, number]; } mtec.color.RGB2Hex = function(r, g, b){ return '#' + [r, g, b].map(v=>v.toString(16).padStart(2, '0')).join(''); } mtec.color.HSL2Hex = function(h, s, l){ h /= 360; // 将色相转换为 [0, 1] 范围 s /= 100; // 将饱和度转换为 [0, 1] 范围 l /= 100; // 将亮度转换为 [0, 1] 范围 let r, g, b; if (s === 0) { r = g = b = l; // 如果饱和度为 0,则为灰度色 } else { let hue2rgb = function hue2rgb(p, q, t) { if (t < 0) t += 1; if (t > 1) t -= 1; if (t < 1 / 6) return p + (q - p) * 6 * t; if (t < 1 / 2) return q; if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; return p; }; let q = l < 0.5 ? l * (1 + s) : l + s - l * s; let p = 2 * l - q; r = hue2rgb(p, q, h + 1 / 3); g = hue2rgb(p, q, h); b = hue2rgb(p, q, h - 1 / 3); } // 将 [0, 1] 范围的 RGB 值转换为 [0, 255] 范围并四舍五入 r = Math.round(r * 255); g = Math.round(g * 255); b = Math.round(b * 255); return mtec.color.RGB2Hex(r, g, b); } mtec.color.logColorLike = function(cut){ let list = Reflect.ownKeys(mtec.color.common).filter((color: string)=>color.includes(cut??'')) as string[]; if(list.length<=0) return void 0; let format_str = ''; let style_list = []; let default_style = 'padding: 0 0.5em; font-weight: bolder;'; let start_style = 'border-top-left-radius: 0.5em 0.5em; border-bottom-left-radius: 0.5em 0.5em;'; let end_style = 'border-top-right-radius: 0.5em 0.5em; border-bottom-right-radius: 0.5em 0.5em;'; list.forEach(color=>{ let hex = mtec.color.normalizeHexColor(Reflect.get(mtec.color.common, color)); let [h, s, l] = mtec.color.toHSLColor(hex); format_str += '%c' + color + '%c' + hex; let font_color = mtec.color.HSL2Hex(h, s>0 ? 100 : 0, l>50 ? 20 : 80); let style_desc = [ ['background-color', hex], ['color', font_color] ].map(item=>item.join(':')).join(';') + ';'; let style_real = [ ['background-color', hex], ['color', font_color] ].map(item=>item.join(':')).join(';') + ';'; style_list.push(...[start_style + style_desc, style_real + end_style].map(s=>s+default_style)); }); console.log(format_str, ...style_list); } mtec.color.logFullColorLine = function(size){ size = size??3; let format_str = ''; let style_list = []; let default_style = 'font-weight: lighter; line-height: 5px; font-size: ' + size + 'px;'; for(let i = 0; i < 360; i++){ format_str += '%c '; style_list.push(default_style + 'background-color: ' + mtec.color.HSL2Hex(i, 100, 50)); } console.log(format_str, ...style_list); } mtec.log.tag = function(tag, ...args){ //return void 0; let str = '', colors = []; if(typeof(tag)=='string' && tag.includes(':')){ tag.split(';').map(s=>{ s = s.trim(); if(s.length==0) return []; else return s.split(':').map(kv=>kv.trim()); }).filter(item=>item.length==2 && item.every(kv=>kv.length>0)) .forEach(item=>{ str += '%c' + item[0]; let [h, s, l] = mtec.color.toHSLColor(item[1]); let clr = [ ['background-color', mtec.color.normalizeHexColor(item[1])], ['color', mtec.color.HSL2Hex(h, s>0 ? 100 : 0, l>50 ? 20 : 80)] ].reduce((t, el)=>{ t.push(el.join(':')); return t; }, [ 'padding:0 0.5em', 'font-weight: bolder', ]).join('; ') + '; '; colors.push(clr); }); } colors[0] += 'border-top-left-radius: 0.5em 0.5em; border-bottom-left-radius: 0.5em 0.5em; '; colors[colors.length-1] += 'border-top-right-radius: 0.5em 0.5em; border-bottom-right-radius: 0.5em 0.5em; '; console.groupCollapsed(str + '%c', ...colors, '', ...args); console.trace(); console.groupEnd(); } mtec.log.warn = function(...args){ mtec.log.tag("WARN:orange;>>:tomato", ...args); } mtec.local.read = function(key, out){ let data_json = localStorage.getItem(key.encodeBtoA); if(data_json){ data_json = data_json.decodeAtoB; if(out) mtec.fusionData(out, JSON.parse(data_json)); else out = JSON.parse(data_json); } return out as any; } mtec.local.save = function(key, data){ localStorage.setItem(key.encodeBtoA, JSON.stringify(data).encodeBtoA); } mtec.time.ONE_DAY = 24 * 60 * 60 * 1000; mtec.time.sameDay = function(d1, d2){ if ([d1, d2].includes(undefined)) return false; let [day1, day2] = [d1, d2].map(d => new Date(d)); return day1.toDateString() === day2.toDateString(); };