"use strict"; const VERSION = "V2.21", ITERABLE = new Object(); const MAX_CHARS = 50, MAX_PROP = 1000; const objA = [], objP = [], NL = "\n"; const hist = []; //object history -- global variable var current = 0; //current object index in list1 & objA var _ = ""; //current object var __ = ""; //previous object var MENU; //installed by the caller class Menu { constructor() {} allKeysIn(obj) { //let s = []; for (let k in obj) s.push(k); return Object.getOwnPropertyNames(obj) } allValuesOf(obj) { return this.allKeysIn(obj).map(k => obj[k]) .filter(x => typeof x != 'function') } deepEqual(a, b) { //compare two objects return JSON.stringify(a) == JSON.stringify(b) } toString() { return "[object Menu] " +this.constructor.name } } function report(input, result) { let msg = trunc(input); if (result != undefined) { display(result); msg += trunc(" ⇒ "+result); } console.log(msg); out.innerText = msg; out.style.background = ""; } function reportError(e) { console.log(e); out.innerText = e //trunc(" "+e, 4*MAX_CHARS); out.style.background = "pink"; } function doMethod(met) { //target == list3 if (!met) return let n = _[met].length; //number of arguments in met let str = "Enter "; if (n == 0) str += "optional arguments "; if (n == 1) str += "the argument "; if (n >= 2) str += n+" arguments separated by commas "; str += '\nto invoke '+met+'()' if (invokeDialog.showModal) { invokeLabel.innerText = str invokeInput.value = '' invokeInput.focus() invokeDialog.returnValue = '' invokeDialog.showModal() invokeDialog.oncancel = () => { invokeDialog.returnValue ='cancel' } invokeDialog.onclose = () => { if (invokeDialog.returnValue !== 'cancel') doInvoke(met, invokeInput.value) } } else { //dialog tag not supported let arg = prompt(str) //+"in order to call "+met+'()' if (arg != null) doInvoke(met, arg) } } function doInvoke(met, arg) { try { let cmd = "_."+met+"("+arg+")"; report(cmd, eval(cmd)); } catch(e) { reportError(e); } } function doProperty(c) { //target == list2 if (c == 0 && _ instanceof Array) return; if (objP[c] === ITERABLE) display([..._]); //convert _ to Array else display(objP[c]); } function doClick(evt, target) { let e = evt.target; //document.elementFromPoint(evt.clientX, evt.clientY); //console.log(e.innerText); if (target == list3) { doMethod(e.innerText); return; } let p = e.parentNode; if (p !== target) e = p; //
  • console.assert(e.parentNode === target); let c = target.children.length; while (c--) if (target.children[c] === e) break; if (c < 0) return; if (target == list1) displayItem(c); else if (target == list2) doProperty(c); else reportError("Unknown "+target); } function previous() { if (hist.length < 2) return; hist.pop(); display(hist.pop()); } function removeIt(ctrl) { if (objA.length < 2) return; if (ctrl) { objA.length = 1; displayItem(0); } else { objA.splice(current, 1); //list1.removeChild(list1.children[current]); previous(); } } function doKey(evt) { //console.log(evt.key, current); switch (evt.key) { case "Delete": removeIt(evt.ctrlKey); return; case "ArrowUp": evt.preventDefault() //evt.stopPropagation(); displayItem(current-1); return; case "ArrowDown": evt.preventDefault() //evt.stopPropagation(); displayItem(current+1); return; case "ArrowLeft": case "Backspace": previous(); return; } } function doEnter(evt) { if (evt.key == "Enter") try { evt.stopPropagation(); let exp = inp.value; report(exp, eval(exp)); } catch(e) { reportError(e); } } function trunc(s, M=MAX_CHARS) { //truncate to M chars if (s == null) return "null"; if (s.length > M+4) s = s.substring(0, M)+"..."; return s; } Request.prototype.toString = function() { let s = this.url; let k = s.indexOf('/',8); return "[R] "+s.substring(k+1) } // 'this' is undefined in ()=>"File: "+this.name; File.prototype.toString = function() { return "[F] "+this.name } FileList.prototype.toString = function () { return '[object FileList] '+this.length } Blob.prototype.toString = function () { return '[object Blob] '+this.size } ArrayBuffer.prototype.toString = function () { return '[object ArrayBuffer] '+this.byteLength } function checkFile(f) { //not used if (Reflect.ownKeys(f).includes("toString")) return; f.toString = () => "File: "+f.name; //console.log("toString returns "+f); } function objToString(obj) { const LT = /"); if (s.includes(RB)) s = s.replace(RB, ""); else s = s+""; } if (!s.startsWith("* *")) list += "
  • "+ s +"
  • "; else { //class name let cls = (j>0? "cname" : "cfirst") list += "
  • " + s.substring(3); if (j > 0) continue; //show tip for the first item let sc = superClasses(_).join("
    "); list += ""+ sc +"
  • "; } } L.innerHTML = list; } function superClasses(obj) { let a = []; while (obj = Object.getPrototypeOf(obj)) a.push(obj.constructor.name); return a; } function display(f) { //if (f.toString == undefined) f = {}; Gürkan Yakar if (!f) return; let t = typeof f; if (t != "string" && t != "object") return; if (!f.toString) throw "cannot display object"; //if (f instanceof Promise) nothing to display in f if (f.then && typeof(f.then) == "function") { f.then(display, reportError); console.log(f); out.innerText = "A Promise was made"; out.style.background = "cyan"; return; } let i = objA.indexOf(f); if (i < 0) objA.push(f); displayItem(i); return f; } function displayItem(c) { const DEPRECATED = ["anchor", "big", "blink", "bold", "fixed", "fontcolor", "fontsize", "italics", "link", "small", "strike", "sub", "substr", "sup"]; function selectCurrent(dark) { let b = list1.children[current]; if (!b) return; b.style.color = (dark? "white" : ""); b.style.background = (dark? "blue" : ""); //b.scrollIntoView(); } function selectItem() { selectCurrent(false); current = c; if (c < 0) current = objA.length-1; if (c >= objA.length) current = 0; selectCurrent(true); } function addMethod(m, key) { if (meth.includes(key)) return; m.push(key); numM++; } function addProperty(key) { let obj = _[key]; let s = key +": "+ objToString(obj); objP.push(obj); prop.push(s); numP++; } function process(proto) { //http://stackoverflow.com/a/8024294/271577 if (proto != Object.getPrototypeOf(_)) try { let aaP = null, aaS = "* *"+proto.constructor.name; meth.push(aaS); if (proto == _ && proto[Symbol.iterator]) { aaP = ITERABLE; aaS += " — Iterable"; } objP.push(aaP); prop.push(aaS); let len = proto["length"]; //may throw TypeError if (len && typeof len != "function" && proto == _) addProperty("length"); } catch(error) { //silently ignore } if (Object.getPrototypeOf(proto) == null) return; //Object keys are not listed let K = Object.getOwnPropertyNames(proto); if (K.length > MAX_PROP) K.length = MAX_PROP; let mmm = []; for (let key of K) { if (key.startsWith("webkit")) continue; try { let obj = _[key]; //may throw TypeError const LC = /[a-z0-9]/; if (typeof obj == "function") { if (key == "constructor") continue; if (!LC.test(key[0])) continue; addMethod(mmm, key); } else if (obj != null) { if (key == "length") continue; //skip uppercase constants if (proto == _ || LC.test(key)) addProperty(key); } } catch(error) { //silently ignore } } //we cannot sort properties, Array keys are string if (mmm.length == 0) return; if (sorted.checked) mmm.sort(); meth = meth.concat(mmm); } //_ is global -- current object arrayToList(objA, list1); selectItem(); __ = _; _ = objA[current]; objP.length = 0; //clear let prop = [], meth = [], numP = 0, numM = 0; list1.focus(); let proto = _; do { process(proto); proto = Object.getPrototypeOf(proto); } while (proto); arrayToList(prop, list2); addMethod(meth, "toString"); if (typeof _ == "string") { //remove DEPRECATED items meth = meth.filter(m => !DEPRECATED.includes(m)); numM = meth.length - 2; } arrayToList(meth, list3); let s = numP+" properties and "+numM+" methods"; out.innerText = trunc(_.toString(), 25)+" — "+s; out.style.background = ""; let n = hist.length; if (hist[n-1] !== _) hist.push(_); if (n > 50) hist.splice(0, 20); } function hideTip(t, show) { //let v = show? 'visible' : 'hidden' for (let x of t.querySelectorAll(".tip1")) x.hidden = !show } function inspect(parent, init) { let t = document.createElement("table"); t.className = 'inspector'; parent.appendChild(t); t.innerHTML = `   Objects     Properties Methods sort ` let d = document.createElement("dialog"); d.id = 'invokeDialog'; parent.appendChild(d); d.innerHTML = `


    _ $ f

    ` if (init) init(); if (!MENU) menu.style.visibility="hidden"; inp.selectionEnd = inp.value.length; inp.selectionStart = 0; inp.focus(); if (window.sss) for (let x of sss.querySelectorAll("button")) { x.onmouseover = (e) => { hideTip(x, true) } x.onmouseout = (e) => { hideTip(x, false) } } } function insertChars(cc, n) { let i = invokeInput, v = i.value let s = i.selectionStart, e = i.selectionEnd i.value = v.slice(0, s) + cc + v.slice(e, v.length) i.selectionStart = i.selectionEnd = s+n; i.focus() }