Logger
(Global => { var LoggerStyleContent = ` :host { background: #2a2a2a; color: #fff; font-family: Courier, monospace; margin: .25em; padding: .5em; } :host * { font-family: Courier, monospace; } label.input { display: flex; vertical-align: middle; } label.input::before { content: '> '; display: inline-block; font-weight: bold; padding: .5em .75em .5em .25em; } label.input input { background: rgba(255, 255, 255, .1); border: none; border-radius: 0; color: #dfd; flex-grow: 1; padding: .5em; } label.input input:focus { background: rgba(255, 255, 255, .35); outline: none; } div { padding: .25em 0; } ul { margin: 0 0 0 1.5em; padding: .25em .5em; } div > ul { background: rgba(255, 255, 255, .1); box-shadow: 0 0 3px #fff; margin: 0; } table { color: #fff; box-shadow: 0 0 3px #fff; white-space: nowrap; } td, th { padding: .25em .5em; } th { background: rgba(255, 255, 255, .35); } a { color: #5c9; cursor: pointer; font-style: italic; font-weight: bold; text-decoration: underline; } a:hover { text-decoration: none; } button { background: rgba(255, 255, 255, 0); border: 1px solid #fff; border-radius: 3px; color: #fff; cursor: pointer; margin: 0 .25em; padding: .25em .5em; transition: all .2s; } button:hover { background: rgba(255, 255, 255, .35); } .progress { color: #fa3; } .success { color: #3f3; } .warning { color: #f33; } `; var e = _ => document.createElement(_); var Analyze = obj => { var tp = typeof obj, val = String(obj), name = obj?.constructor?.name; val = (tp == 'boolean' || tp == 'number' || tp == 'symbol' || obj == null || obj == void(0)) ? val : tp == 'bigint' ? `${val}n` : tp =='string' ? `"${val}"` : name.match(/^.*?Array$/) ? `${name}(${obj.length}) [${obj.length ? '...' : ''}]` : (name == 'ArrayBuffer' || name == 'SharedArrayBuffer' || name == 'DataView') ? `${name}(${obj.byteLength}) {}` : name == 'Object' ? `Object {${Object.keys(obj).length ? '...' : ''}}` : `${name} {}`; var keys = (obj == null || obj == void(0)) ? [] : [...Object.keys(obj), ...Object.getOwnPropertyNames(obj)]; for(var k in obj) keys.push(k); keys = keys .filter((v, i, a) => a.indexOf(v) == i) .filter(v => tp == 'string' ? !v.match(/^\d+$/) : true); return {value: val, type: tp, name: name, keys: keys}; }; var LoggerElement = {}; LoggerElement.Button = class { constructor(name) { this.elm = e('button'); this.elm.innerHTML = name; } callback(func) { this.elm.onclick = func; return this; } click() { this.elm.click(); return this; } once() { return new Promise(r => this.elm.addEventListener('click', e => r(this))); } }; LoggerElement.List = class { constructor(obj, tgt=null, key=null) { this.obj = obj; this.info = Analyze(this.obj); this.elm = document.createElement('ul'); var b = document.createElement('b'); this.elm.appendChild(b); b.innerHTML = key ? key : this.info.value; var s = document.createElement('span'); key && (this.elm.appendChild(s), s.innerHTML = `<b>:</b> ${this.info.value}`); tgt && tgt.appendChild(this.elm); this.btn = new LoggerElement.Button('^').callback(e => this.children ? this.close() : this.open()); this.elm.insertBefore(this.btn.elm, b); } open() { if(this.children) return this; this.children = this.info.keys.map(k => { try { return new LoggerElement.List(this.obj[k], this.elm, k); }catch(e) {} }).filter(v => v); var p = this.obj?.__proto__; p && this.children.push(new LoggerElement.List(p, this.elm, '__proto__')); return this; } close() { this.children.map(c => c.elm.remove()); this.children = null; return this; } }; LoggerElement.Input = class { constructor() { this.elm = e('label'); this.input = e('input'); this.input.type = 'text'; this.elm.appendChild(this.input); this.elm.classList.add('input'); } get content() { return this.input.value; } set content(v) { this.input.value = v; } once(lock) { return new Promise(r => this.elm.addEventListener('keyup', e => e.key == 'Enter' && (lock && (this.input.disabled = true), r(this.content)) )); } }; LoggerElement.Table = class {// [{a: 1, b: 1}, {a: 2, b: 2}] constructor(obj, keys=Object.keys(obj[0])) { this.elm = e('table'); this.head = e('thead'); this.body = e('tbody'); [this.head, this.body].map(e => this.elm.appendChild(e)); this.update(obj, keys); } update(obj, keys=Object.keys(obj[0])) { this.obj = obj; this.keys = keys; this.tr(this.head, e('td'), ...this.keys.map(k => { var th = e('th'); th.innerHTML = k; return th; })); this.obj.map((o, i) => { var th = e('th'); th.innerHTML = i; this.tr(this.body, th, ...this.keys.map(k => { var td = e('td'); ['Input', 'Button'].map(v => LoggerElement[v]).find(v => o[k] instanceof v) ? td.appendChild(o[k].elm) : td.innerHTML = o[k]; return td; })); }); return this; } tr(target, ...tdth) { var tr = e('tr'); tdth.map(t => tr.appendChild(t)); target.appendChild(tr); return tr; } }; var LoggerLine = class { constructor(self, val) { this.self = self; this.line = e('div'); this.self.Shadow.appendChild(this.line); this.content = val; } get content() { return this.line.innerHTML; } set content(str) { this.line.innerHTML = str; } append(...elms) { elms.forEach(e => this.line.appendChild(e.elm)); return this; } }; var LoggerProgress = class extends LoggerLine { constructor(self, rel) { super(self); this.rel = rel; this.update(rel); } update(rel, ...cls) { this.rel = rel; var n = Math.floor(rel * 20); var _cls = ['progress', ...(rel == 1 ? ['success'] : []), ...cls].join(' '); var prog = [...Array(n).fill('='), '|', ...Array(20 - n).fill(' ')].slice(0, 20).join(''); var per = ((rel * 100).toPrecision(3) + ' ').slice(0, 4); this.content = `[ <span class="${_cls}">${per}%</span> |${prog}]`; return this; } }; var Logger = class { constructor(body) { this.body = body; this.Shadow || this.body.attachShadow({mode: 'open'}); this.Shadow.innerHTML = `<style>${LoggerStyleContent}</style>`; } get Shadow() { return this.body.shadowRoot; } static get Element() { return LoggerElement; } line(str='') { return new LoggerLine(this, str); } progress(rel=0) { return new LoggerProgress(this, rel); } }; Global.Logger = Logger; })(this);
sample
<head> <meta name="viewport" content="width=device-width, initial-scale=1.0"/> <script src="./1"></script> <!-- http://topia.wikidot.com/logger/code/1 --> </head> <body> <script> (async () => { var sleep = s => new Promise(r => setTimeout(r, s * 1000)); var div = document.createElement('div'); document.body.innerHTML = ''; document.body.appendChild(div); var log = new Logger(div); var line_1 = log.line('test <a>test</a>'); var line_2 = log.line('Now progress...'); var line_3 = log.progress(); var flag = false; var button_1 = new Logger.Element.Button('abort?') .callback(e => (flag = true)); var line_4 = log.line() .append(button_1); for(var step = 39, i = 0; i <= step; i++) { if(flag) { line_3.update(line_3.rel, 'warning'); break; } line_3.update(i / step); await sleep(0.1); } line_2.content += flag ? ' abort.' : ' done.'; var line_5 = log.line('|============================|'); var button_2 = new Logger.Element.Button('next?'); var line_6 = log.line() .append(button_2); await button_2.once(); var button_3 = new Logger.Element.Button('sample'); var table_1 = new Logger.Element.Table( [{a: 'a-0', b: 'b-0', c: 'c-0', d: 'd-0'}, {a: 'a-1', b: 'b-1', c: 'c-1', d: button_3}], ['a', 'b', 'c', 'd', 'e'] ); line_6.content = ''; line_6.append(table_1); var line_7 = log.line(); button_3.callback(e => line_7.append( new Logger.Element.List(e) )); var input_1 = new Logger.Element.Input(); var line_8 = log.line().append(input_1); var line_9 = log.line(`Input: ${await input_1.once(true)}`); })(); </script> </body>






