UNPKG

@pardnchiu/nanojson

Version:

A lightweight JSON editor built with pure JavaScript and native APIs. It features visual editing, dynamic type switching, and file import/export capabilities. Suitable for website embedding and JSON data editing.

1 lines 16.3 kB
!function () { const e = /\.([\w_-]+)?/gi, t = /\#([\w_-]+)?/i, i = /^\w+(?=[\#\.]*)/i, n = "pd-json-editor", s = "pd-json-editor-nested-child", o = "button:not(.collapsed), textarea, input, select", r = "appendChild", l = "array", a = "body", c = "boolean", h = "display", d = "temp", f = "push", u = "button", p = "children", y = "collapsed", m = "setAttribute", v = "dataset", b = "true", _ = "disabled", g = "type", k = "editor", w = "export", C = "import", A = "innerHTML", j = "isArray", H = "label", z = "section", S = "length", M = "toLowerCase", N = "match", O = "nextElementSibling", V = "querySelectorAll", x = "number", D = "object", L = "option", T = "parent", R = "placeholder", E = "render", U = "replace", $ = "reset", B = "selected", F = "string", J = "target", I = "textarea", P = "title", q = "update", K = "value", Y = "readonly", G = "lifecycle", Q = "click", W = "download", X = { [D]: 1, [l]: 1 }; let Z = "beforeRender", ee = "beforeUpdate", te = "beforeDestroy", ie = "rendered", ne = "updated", se = "destroyed"; let oe = { [x]: "M5.5 15v-4.5H4V9h3v6H5.5zM9 15v-2.5c0-.283.096-.52.287-.713A.967.967 0 0110 11.5h2v-1H9V9h3.5c.283 0 .52.096.713.287.191.192.287.43.287.713v1.5c0 .283-.096.52-.287.713a.968.968 0 01-.713.287h-2v1h3V15H9zm6 0v-1.5h3v-1h-2v-1h2v-1h-3V9h3.5c.283 0 .52.096.712.287.192.192.288.43.288.713v4c0 .283-.096.52-.288.713A.968.968 0 0118.5 15H15z", [F]: "M17 15a.968.968 0 01-.712-.287A.968.968 0 0116 14v-4c0-.283.096-.52.288-.713A.968.968 0 0117 9h3c.283 0 .52.096.712.287.192.192.288.43.288.713v1h-1.5v-.5h-2v3h2V13H21v1c0 .283-.096.52-.288.713A.968.968 0 0120 15h-3zm-7.5 0V9h4c.283 0 .52.096.713.287.191.192.287.43.287.713v1c0 .283-.096.52-.287.713A.968.968 0 0113.5 12c.283 0 .52.096.713.287.191.192.287.43.287.713v1c0 .283-.096.52-.287.713A.968.968 0 0113.5 15h-4zm1.5-3.75h2v-.75h-2v.75zm0 2.25h2v-.75h-2v.75zM3 15v-5c0-.283.096-.52.288-.713A.968.968 0 014 9h3c.283 0 .52.096.713.287.191.192.287.43.287.713v5H6.5v-1.5h-2V15H3zm1.5-3h2v-1.5h-2V12z", [D]: "M13.5 18v-1.5h2.25c.213 0 .39-.072.534-.216a.726.726 0 00.216-.534v-1.5c0-.475.137-.906.413-1.294A2.233 2.233 0 0118 12.131v-.262a2.233 2.233 0 01-1.087-.825A2.184 2.184 0 0116.5 9.75v-1.5a.726.726 0 00-.216-.534.726.726 0 00-.534-.216H13.5V6h2.25a2.17 2.17 0 011.594.656c.437.438.656.969.656 1.594v1.5c0 .213.072.39.216.534a.726.726 0 00.534.216h.75v3h-.75a.726.726 0 00-.534.216.726.726 0 00-.216.534v1.5a2.17 2.17 0 01-.656 1.594A2.17 2.17 0 0115.75 18H13.5zm-5.25 0a2.17 2.17 0 01-1.594-.656A2.17 2.17 0 016 15.75v-1.5a.726.726 0 00-.216-.534.726.726 0 00-.534-.216H4.5v-3h.75c.213 0 .39-.072.534-.216A.726.726 0 006 9.75v-1.5c0-.625.219-1.156.656-1.594A2.17 2.17 0 018.25 6h2.25v1.5H8.25a.726.726 0 00-.534.216.726.726 0 00-.216.534v1.5c0 .475-.138.906-.412 1.294A2.233 2.233 0 016 11.869v.262c.45.163.813.438 1.088.825.274.388.412.819.412 1.294v1.5c0 .213.072.39.216.534a.726.726 0 00.534.216h2.25V18H8.25z", [l]: "M14.625 19v-1.75h2.625V6.75h-2.625V5H19v14h-4.375zM5 19V5h4.375v1.75H6.75v10.5h2.625V19H5z", [c]: "M7.91 16.818c-1.365 0-2.524-.477-3.478-1.432C3.477 14.432 3 13.273 3 11.91c0-1.364.477-2.523 1.432-3.477C5.386 7.477 6.545 7 7.909 7h8.182c1.363 0 2.523.477 3.477 1.432.955.954 1.432 2.114 1.432 3.477 0 1.364-.477 2.523-1.432 3.477-.954.955-2.114 1.432-3.477 1.432H7.909zm0-1.636h8.18c.9 0 1.671-.32 2.312-.962.641-.64.962-1.41.962-2.31 0-.9-.32-1.671-.962-2.312a3.151 3.151 0 00-2.311-.962H7.909c-.9 0-1.67.32-2.311.962a3.151 3.151 0 00-.962 2.311c0 .9.32 1.67.962 2.311.64.641 1.411.962 2.311.962zm8.18-.818c.683 0 1.262-.239 1.74-.716a2.367 2.367 0 00.716-1.739c0-.682-.24-1.261-.716-1.739a2.367 2.367 0 00-1.74-.715c-.68 0-1.26.238-1.738.716a2.367 2.367 0 00-.716 1.738c0 .682.239 1.261.716 1.739a2.367 2.367 0 001.739.716z", right: "M10 18V6l6 6-6 6z", folder: "M4 20c-.55 0-1.02-.196-1.413-.587A1.926 1.926 0 012 18V6c0-.55.196-1.02.587-1.412A1.926 1.926 0 014 4h6l2 2h8c.55 0 1.02.196 1.413.588.391.391.587.862.587 1.412H11.175l-2-2H4v12l2.4-8h17.1l-2.575 8.575a1.95 1.95 0 01-.738 1.038A1.985 1.985 0 0119 20H4zm2.1-2H19l1.8-6H7.9l-1.8 6z", add: "M11 13H5v-2h6V5h2v6h6v2h-6v6h-2v-6z", [W]: "M12 16l-5-5 1.4-1.45 2.6 2.6V4h2v8.15l2.6-2.6L17 11l-5 5zm-6 4c-.55 0-1.02-.196-1.412-.587A1.926 1.926 0 014 18v-3h2v3h12v-3h2v3c0 .55-.196 1.02-.587 1.413A1.926 1.926 0 0118 20H6z", clear: "M17.25 18H22v2h-6.75l2-2zm-12.5 2l-2.125-2.125c-.383-.383-.58-.858-.587-1.425-.009-.567.179-1.05.562-1.45l11-11.4c.383-.4.854-.6 1.412-.6.559 0 1.03.192 1.413.575L21.4 8.55c.383.383.575.858.575 1.425 0 .567-.192 1.042-.575 1.425L13 20H4.75zm7.4-2L20 9.95 15.05 5 4 16.4 5.6 18h6.55z" }; for (let e of Object.keys(oe)) oe[e] = `<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="${oe[e]}" fill="#5F6368"/></svg>`; function re(e, t, i) { if (X[e[g][M]()] && !e[y]) return (z + "." + s)._([...e[p].map((e, i) => t(e, i)), (u + ".child-add")._(oe.add)._({ click: i })]) } function le(e) { const t = X[e[g][M]()]; return (u + "." + y)._({ [v]: { collapsable: t ? 1 : 0, [y]: e[y] ? 1 : 0 } }, t ? oe.right : null)._({ [Q]: t ? t => e.setCollapsed() : e => { } }) } function ae(e) { return new Promise(async (t, i) => { if (null != e) if (e instanceof File) { const i = new FileReader; i.onload = i => { try { const e = JSON.parse(i[J].result); t(e) } catch (i) { console.error(`Failed to parse JSON from file ${e.name}: ${i}`), t() } }, i.readAsText(e) } else if (typeof e === D) t(e); else if (typeof e === F) try { const i = await fetch(e); if (i.ok) { const e = await i.text(), n = JSON.parse(e); t(n) } } catch (i) { console.error(`Failed to fetch data from ${e}: ${i}`), t() } else console.error(`Invalid data [_type]: ${e} (${typeof e})`), t(); else t() }) } function ce(e) { return Array[j](e) ? l : typeof e === D ? D : typeof e === c ? c : typeof e === x ? x : F } function he(e, t, i, n, s) { function o(e) { return e[U](/\n/g, "") } function r(t) { const i = t[J]; i[K] = i[O][A] = o(i[K]), e.key = o(i[K]).trim() } return i ? "span.array-index"._(t) : H._([(I + "#key-" + e.id)._({ [R]: "KEY", [_]: n ? "" : null }, e.key[U](/\n/g, ""))._({ input: e => { r(e), s[q](() => { }) }, change: e => { r(e) } }), "pre"._(o(e.key))]) } document.addEventListener("DOMContentLoaded", e => { console.log("NanoJSON: https://github.com/pardnchiu/NanoJSON\nLicense: MIT\nCreator: Pardn Chiu") }), String.prototype._ = function (n, s) { const o = this.toString(), l = ((o[N](i) || [])[0] || "").trim(), a = ((o[N](t) || [])[1] || "").trim(), c = (e.test(o) ? o[N](e) : []).map(e => e[U](/^\./, "")); if (l[S] < 1) return; let f, u, p = o === d, y = p ? document.createDocumentFragment() : document.createElement(l); a[S] > 0 && (y.id = a); for (const e of c) y.classList.add(e); if (null == n && null != s && ([n, s] = [s, null]), null != n && null != s) [f, u] = [n, s]; else if (null == s) typeof n === F || typeof n === x || Array[j](n) ? u = n : f = n; else if (null == n) return y; return (() => { if (typeof f === D && null != f) for (const e in f) { if (!f.hasOwnProperty(e)) continue; const t = f[e]; if ({ [K]: 1, innerText: 1, [A]: 1, textContent: 1, contentEditable: 1, [B]: 1, checked: 1 }[e]) y[e] = t; else if ({ [h]: 1, color: 1, backgroundColor: 1, background: 1, width: 1, height: 1, float: 1 }[e]) y.style[e] = t; else if (e === v && typeof t === D) for (const e of Object.keys(t)) y[v][e] = t[e]; else null != t && y[m](e, t) } })(), (() => { if (null == u) return; if (Array[j](u)) { for (let e of u) typeof e === F || typeof e === x ? p ? y[r](document.createTextNode(e)) : y[A] += e : e instanceof Element && y[r](e); return } if (typeof u === D) return; const e = u; "img" === l || "source" === l ? y.src = e : l === I || "input" === l ? y[K] = e : p ? y[r](document.createTextNode(u)) : y[A] = e })(), y }, HTMLElement.prototype._ = function (e = {}) { if (typeof e !== D) return; let t = this; for (const i of Object.keys(e)) t["on" + i] = t => e[i](t); return t }; new Map; function de(e, t) { if (e[g][M]() === x) { const s = parseFloat(e[K]); function i(e) { return isNaN(e) && "-" !== e && "." !== e ? "" : String(e)[U](/\s/g, "") } return H._([(I + "#value-" + e.id)._({ [R]: "NUM" }, i(s))._({ input: n => { const s = n[J]; s[K] = s[O][A] = i(s[K]), e[K] = i(s[K]), t[q](() => { }) }, change: t => { const n = t[J]; n[K] = n[O][A] = i(n[K]), e[K] = i(n[K]) } }), "pre"._(i(s))]) } if (e[g][M]() === c) return e[K].trim()[S] < 1 && (e[K] = b), ("select#value-" + e.id)._([L._({ [K]: b, [B]: e[K] === b }, b), L._({ [K]: "false", [B]: "false" === e[K] }, "false")])._({ change: i => { e[K] = i[J][K], t[q](() => { }) } }); { const o = X[e[g][M]()]; function n(e) { return e[U](/\n/g, "<br>") } return H._({ [h]: o ? "none" : "block" }, [(I + "#value-" + e.id)._({ [R]: "VAL" }, e[K])._({ input: i => { const s = i[J]; s[O][A] = n(s[K]), e[K] = s[K], t[q](() => { }) }, change: t => { const i = t[J]; i[O][A] = n(i[K]), e[K] = i[K] } }), "pre"._(n(e[K]))]) } } window.JSONEditor = class { children = []; body; editor; button = []; #e; #t = !1; #i = D; get type() { return this.#i } constructor(e = {}) { if (typeof e != D) return void console.error("Failed to load config."); let t = e.css; (null == t || typeof t !== F || t.length < 1) && (t = "https://cdn.jsdelivr.net/npm/@pardnchiu/nanojson@1.1.7/dist/NanoJSON.css"), function (e) { for (let t of ["link"._({ rel: "preload", href: e, as: "style" }), "link"._({ rel: "stylesheet", href: e })]) document.head[r](t) }(t), this.#n(e) } async#n(e = {}) { this[k] = "section"._(); const t = e.when ?? {}, i = e[P] ?? "", s = e.description ?? "", l = Boolean(null == e.fill ? 1 : e.fill) ? 1 : 0, c = e[Y] ?? !1; let f = null != e[u] && typeof e[u] === D ? e[u] : { [C]: 1, [w]: 1, [$]: 1 }; f[$] = f[$] ?? 1, f[C] = f[C] ?? 1, f[w] = f[w] ?? 1, this.#e = new ue({ beforeRender: t[Z], rendered: t[ie], beforeUpdate: t[ee], updated: t[ne], beforeDestroy: t[te], destroyed: t[se] }); let y = await ae(e.file ?? e.json ?? e.path) ?? {}; this[p] = this.#s(y); let A = d._([Math.max(i[S], s[S]) > 0 ? "header"._([i[S] > 0 ? "strong"._(i) : null, s[S] > 0 ? "p"._(s) : null]) : null, this[k], "footer"._([u._({ [P]: "Add" }, oe.add)._({ [Q]: e => this.insert() }), Boolean(f[C]) ? u._({ [P]: "Open" }, oe.folder)._({ [Q]: e => e[J][O][Q]() }) : null, Boolean(f[C]) ? "input"._({ [g]: "file", accept: ".json", [h]: "none" })._({ change: e => this[C](e[J].files[0]) }) : null, Boolean(f[w]) ? u._({ [P]: "Download" }, oe[W])._({ [Q]: e => { confirm(e[J][P] + "?") && this[w]() } }) : null, Boolean(f[$]) ? u._({ [P]: "Reset" }, oe.clear)._({ [Q]: e => { confirm(e[J][P] + "?") && this[C]({}) } }) : null])]); null == e.id ? (this[a] = (z + "." + n)._(), this[a][r](A)) : (this[a] = document.getElementById(e.id), this[a].classList.add(n), this[a].replaceChildren(...A[p])), this[a][v].fill = l, this[p][S] < 1 && this.insert(), this.#e[E](async () => { if (this[E](), c) { this[a][v][Y] = 1; for (let e of [...this[a][V](o)]) e[m](_, b) } this.#t = !0 }) } enable() { this[a][v][Y] = 0; for (let e of [...this[a][V](o)]) e.removeAttribute(_) } disable() { this[a][v][Y] = 1; for (let e of [...this[a][V](o)]) e[m](_, b) } #o(e) { return e.render() } #s(e, t = null) { const i = []; if (Array[j](e)) for (let n of e) { const e = ce(n), s = new fe({ [g]: e, [T]: t ?? this, [k]: this, [G]: this.#e }); e === D && null != n || e === l ? s[p] = this.#s(n, s) : s[K] = String(n), i[f](s) } else for (const [n, s] of Object.entries(e)) { const e = ce(s), o = new fe({ key: n, [g]: e, [T]: t ?? this, [k]: this, [G]: this.#e }); e === D && null != s || e === l ? o[p] = this.#s(s, o) : null === s ? (o[g] = _null, o[K] = null) : o[K] = String(s), i[f](o) } return i } render(e = !1) { let t = d._(this[p].map(e => this.#o(e))); this[k].replaceChildren(...t[p]), this.#t && e && this.#e[q](() => { }) } insert() { this[p][f](new fe({ [T]: this, [k]: this, [G]: this.#e })), this[E]() } get json() { const e = {}; for (let t of this[p]) t.key && (e[t.key || 0] = t.json); return JSON.stringify(e, null, 4) } async import(e) { let t = await ae(e) ?? {}; this[p] = this.#s(t), this[E](!0) } reset() { this[C]({}) } export() { const e = {}; for (let t of this[p]) (t.key || 1 === this[p][S]) && (e[t.key || 0] = t.json); const t = new Blob([JSON.stringify(e, null, 4)], { [g]: "application/json" }), i = URL.createObjectURL(t), n = "a"._({ href: i, [W]: `NanoJSON-${Date.now()}.json` }); document[a][r](n), n[Q](), document[a].removeChild(n), URL.revokeObjectURL(i) } }; class fe { key = ""; type = "string"; value = ""; parent; children = []; collapsed = !1; #r; #l; #e; constructor(e = {}) { typeof e == D ? (this.id = function (e = 64) { let t = ""; for (let i = 0; i < e; i++)t += "abcdefghijklmnopqrstuvwxyz0123456789".charAt(Math.floor(36 * Math.random())); return t }(), this.key = e.key ?? this.key, this[g] = e[g] ?? this[g], this[K] = e[K] ?? this[K], this[T] = e[T] ?? this[T], this[p] = e[p] ?? this[p], this[y] = e[y] ?? this[y], this.#l = e[k], this.#e = e[G]) : console.error("Failed to load config form editor node.") } render() { return this.#o() } addChild() { this.#a() } updateChild() { this.#o(), this.#c() } setCollapsed() { this[y] = !this[y], this.#o() } get json() { return this.#h() } #c() { this.#e[q](e => { }) } #o() { let e = "section.pd-json-editor-child"._(["section.pair-wrapper"._([("section#" + this.id + ".input-group")._([le(this), he(this, this[T][p].indexOf(this), this[T][g] === l, "1" === this.#l[a][v][Y], this.#e), "span"._(":"), (t = this, i = "1" === this.#l[a][v][Y], H._([t[g][M]() === x ? oe[x] : t[g][M]() === c ? oe[c] : t[g][M]() === l ? oe[l] : t[g][M]() === D ? oe[D] : oe[F], "select"._({ [_]: i ? "" : null }, [...[F, x, c, l, D].map(e => L._({ [K]: e, [B]: e === t[g] }, e))])._({ change: e => { if (t[g] = e[J][K], X[e[J][K][M]()]) t[K] = "", 0 === t[p][S] && t.addChild(); else if (e[J][K][M]() === x) { const e = parseFloat(t[K]); t[K] = isNaN(e) ? "" : e } else t[K] = "", t[p] = []; t.updateChild(), document.getElementById("value-" + t.id).focus() } })])), de(this, this.#e), u._(oe.add)._({ [Q]: e => { confirm("Remove?") && this.#d() } })]), re(this, (e, t) => { let i = e.#o(); return i[v].last = t === this[p].length - 1 ? 1 : 0, i }, () => { this.#a() })])]); var t, i; return this.#r && this.#r.parentElement.replaceChild(e, this.#r), this.#r = e, this.#r } #a() { const e = new fe({ [T]: this, [k]: this.#l, [G]: this.#e }); this[p][f](e); const t = this.#r.querySelector(z + "." + s); if (null != t) { const i = t[p][t[p].length - 1]; for (let e of i.parentElement[p]) e[v].last = 0; const n = e.#o(); n[v].last = 1, t.insertBefore(n, i) } this.#c() } #d() { if (!this[T]) return; const e = this[T][p].indexOf(this); if (-1 === e) return; const t = this.#r.previousElementSibling; "1" === this.#r[v].last && null != t && (t[v].last = 1), this[T][p].splice(e, 1), this.#r.remove(), this.#c() } #h() { if (!this[T]) return; if (this[g] === l) return this[p].map(e => e.#h()); if (this[g] === D) { const e = {}; for (let t of this[p]) (t.key || this[T][g] === l) && (e[t.key || Object.keys(e)[S]] = t.#h()); return e } let e = this[K]; return this[g] === c ? e = e[M]() === b : this[g] === x && (e = Number(e)), e } } class ue { #f; #u; #p; #y; #m; #v; #b; #_; #g; constructor(e = {}) { this.#f = e[Z] || void 0, this.#u = e[ie] || void 0, this.#p = e[ee] || void 0, this.#y = e[ne] || void 0, this.#m = e[te] || void 0, this.#v = e[se] || void 0 } async#k(e) { return new Promise((t, i) => { t(!1 !== e()) }) } #w(e) { e(!1) } async render(e) { this.#b = Date.now(), null != this.#f && !1 === await this.#k(this.#f) || (await e(), this.#_ = Date.now() - this.#b, console.log(`Rendered in ${this.#_}ms.`), null != this.#u && this.#w(this.#u)) } async update(e) { clearTimeout(this.#g), this.#g = setTimeout(async () => { this.#b = Date.now(), null != this.#p && !1 === await this.#k(this.#p) || (await e(), this.#_ = Date.now() - this.#b, console.log(`Updated in ${this.#_}ms.`), null != this.#y && this.#w(this.#y)) }, 300) } async destroy(e) { this.#b = Date.now(), null != this.#m && !1 === await this.#k(this.#m) || (await e(), this.#_ = Date.now() - this.#b, console.log(`Destroyed in ${this.#_}ms.`), null != this.#v && this.#w(this.#v)) } } }("undefined" == typeof window ? window = {} : window);