@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.
2 lines (1 loc) • 16.4 kB
JavaScript
!function(){const e=window,t=document,n=String,i=Number,l=Boolean,r=Array,o=Object,a=(DocumentFragment,Blob),s=URL,c=Math,h=Date,d=setTimeout,p=JSON,u=confirm,f=parseFloat,v=isNaN,y=FileReader,b=File,g=fetch,m=Promise,w=/\.([\w_-]+)?/gi,C=/\#([\w_-]+)?/i,k=/^\w+(?=[\#\.]*)/i,j=["string","number","boolean","array","object"],x="https://cdn.jsdelivr.net/npm/@pardnchiu/nanojson@0.3.4/dist/NanoJSON.css",A="beforeRender",H="beforeUpdate",L="beforeDestroy",z="rendered",M="updated",S="destroyed";for(let e of[O("link",{rel:"preconnect",href:"https://cdn.jsdelivr.net"}),O("link",{rel:"preload",href:x,as:"style"}),O("link",{rel:"stylesheet",href:x})])t.head.appendChild(e);document.addEventListener("DOMContentLoaded",(e=>{$("%cNanoJSON\nGitHub: https://github.com/pardnchiu/NanoJSON\nCreator: Pardn Chiu\nLicense: Proprietary","line-height: 1.75rem; font-size: 0.875rem;")}));const T={number:'<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="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" fill="#5F6368"/></svg>',string:'<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="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" fill="#5F6368"/></svg>',object:'<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path 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" fill="#5F6368"/></svg>',array:'<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M14.625 19v-1.75h2.625V6.75h-2.625V5H19v14h-4.375zM5 19V5h4.375v1.75H6.75v10.5h2.625V19H5z" fill="#5F6368"/></svg>',boolean:'<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="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" fill="#5F6368"/></svg>',right:'<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M10 18V6l6 6-6 6z" fill="#5F6368"/></svg>',folder:'<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="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" fill="#5F6368"/></svg>',add:'<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg"><path d="M11 13H5v-2h6V5h2v6h6v2h-6v6h-2v-6z" fill="#5F6368"/></svg>',download:'<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="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" fill="#5F6368"/></svg>',clear:'<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="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" fill="#5F6368"/></svg>'};function F(e={}){let t;if("object"==typeof e&&e.hasOwnProperty("dom")){t=e.dom;for(const n of o.keys(e))"dom"!==n&&(t[n]=t=>e[n](t));return t}}function V(e,t,n){if({object:1,array:1}[e.type.toLowerCase()]&&!e.collapsed)return O("section",{class:"pd-json-editor-nested-child"},[...e.children.map(((e,n)=>t(e,n))),F({dom:O("button.child-add",T.add),onclick:n})])}function E(e){const t={object:1,array:1}[e.type.toLowerCase()];return F({dom:O("button",{dataset:{collapsible:t?1:0,collapsed:e.collapsed?1:0}},t?T.right:null),onclick:t?t=>e.setCollapsed():e=>{}})}function O(e="",n,i){const l=((e.match(k)||[])[0]||"").trim(),a=((e.match(C)||[])[1]||"").trim(),s=(w.test(e)?e.match(w):[]).map((e=>e.replace(/^\./,"")));if(l.length<1)return;let c,h,d="temp"===e,p=d?t.createDocumentFragment():t.createElement(l);a.length>0&&(p.id=a);for(const e of s)p.classList.add(e);if(null==n&&null!=i&&([n,i]=[i,null]),null!=n&&null!=i)[c,h]=[n,i];else if(null==i)"string"==typeof n||"number"==typeof n||r.isArray(n)?h=n:c=n;else if(null==n)return p;return(()=>{if("object"==typeof c&&null!=c)for(const e in c){if(!c.hasOwnProperty(e))continue;const t=c[e];if({value:1,innerText:1,innerHTML:1,textContent:1,contentEditable:1,selected:1,checked:1}[e])p[e]=t;else if({display:1,color:1,backgroundColor:1,background:1,width:1,height:1,float:1}[e])p.style[e]=t;else if("dataset"===e&&"object"==typeof t)for(const e of o.keys(t))p.dataset[e]=t[e];else null!=t&&p.setAttribute(e,t)}})(),(()=>{if(null==h)return;const e="object"==typeof h;if(r.isArray(h)){for(let e of h){const n=e instanceof Element;"string"==typeof e||"number"==typeof e?d?p.appendChild(t.createTextNode(e)):p.innerHTML+=e:n&&p.appendChild(e)}return}if(e)return;const n=h,i="input"===l,o="textarea"===l;"img"===l||"source"===l?p.src=n:o||i?p.value=n:d?p.appendChild(t.createTextNode(h)):p.innerHTML=n})(),p}function N(e){return new m((async(t,n)=>{if(null!=e)if(e instanceof b){const n=new y;n.onload=n=>{try{t(p.parse(n.target.result))}catch(n){R(`Failed to parse JSON from file ${e.name}: ${n}`),t({})}},n.readAsText(e)}else"object"==typeof e?t(e):"string"==typeof e?await g(e).then((e=>e.text())).then((e=>{try{t(p.parse(e))}catch(e){throw e}})).catch((n=>{R(`Failed to fetch data from ${e}: ${n}`),t({})})):(R(`Invalid data type: ${e} (${typeof e})`),t({}));else t({})}))}function B(e){return r.isArray(e)?"array":"object"==typeof e?"object":"boolean"==typeof e?"boolean":"number"==typeof e?"number":"string"}function D(e,t,n,i){function l(e){return e.replace(/\n/g,"")}function r(t){const n=t.target;n.value=n.nextElementSibling.innerHTML=l(n.value),e.key=l(n.value).trim()}return n?O("span.array-index",t):O("label",[F({dom:O("textarea#key-"+e.id,{placeholder:"KEY"},e.key.replace(/\n/g,"")),oninput:e=>{r(e),i.update((()=>{}))},onchange:e=>{r(e)}}),O("pre",l(e.key))])}function R(...e){const n=O("script",`console.error.apply(void 0, ${p.stringify(e)});`);t.body.appendChild(n),n.remove()}function $(...e){const n=O("script",`console.log.apply(void 0, ${p.stringify(e)});`);t.body.appendChild(n),n.remove()}new Map;function U(e,t){if("number"===e.type.toLowerCase()){const r=/[^\d\-\.]/g,o=f(e.value);function i(e){return v(e)?"":n(e).replace(/\s/g,"").replace(r,"")}return O("label",[F({dom:O("textarea#value-"+e.id,{placeholder:"NUM"},i(o)),oninput:n=>{console.log("onchange",n);const l=n.target;l.value=l.nextElementSibling.innerHTML=i(l.value),e.value=i(l.value),t.update((()=>{}))},onchange:t=>{console.log("onchange",t);const n=t.target;n.value=n.nextElementSibling.innerHTML=i(n.value),e.value=i(n.value)}}),O("pre",i(o))])}if("boolean"===e.type.toLowerCase())return e.value.trim().length<1&&(e.value="true"),F({dom:O("select#value-"+e.id,[O("option",{value:"true",selected:"true"===e.value},"true"),O("option",{value:"false",selected:"false"===e.value},"false")]),onchange:n=>{e.value=n.target.value,t.update((()=>{}))}});{function l(e){return e.replace(/\n/g,"<br>")}return O("label",{display:{object:1,array:1}[e.type.toLowerCase()]?"none":"block"},[F({dom:O("textarea#value-"+e.id,{placeholder:"VAL"},e.value),oninput:n=>{const i=n.target;i.nextElementSibling.innerHTML=l(i.value),e.value=i.value,t.update((()=>{}))},onchange:t=>{const n=t.target;n.nextElementSibling.innerHTML=l(n.value),e.value=n.value}}),O("pre",l(e.value))])}}e.JSONEditor=class{children=[];body;editor;button=[];#e;#t=!1;#n="Object";get type(){return this.#n}constructor(e={}){"object"==typeof e?this.#i(e):R("Failed to load config.")}async#i(e={}){this.editor=O("section");const n=e.when??{},i=e.title??"",r=e.description??"",o=l(null==e.fill?1:e.fill)?1:0;let a=null!=e.button&&"object"==typeof e.button?e.button:{import:1,export:1,reset:1};a.reset=a.reset??1,a.import=a.import??1,a.export=a.export??1,this.#e=new I({beforeRender:n[A],rendered:n[z],beforeUpdate:n[H],updated:n[M],beforeDestroy:n[L],destroyed:n[S]});let s=await N(e.file??e.json??e.path);this.children=this.#l(s);let c=O("temp",[Math.max(i.length,r.length)>0?O("header",[i.length>0?O("strong",i):null,r.length>0?O("p",r):null]):null,this.editor,O("footer",[F({dom:O("button",{title:"Add row"},T.add),onclick:e=>this.insert()}),l(a.import)?F({dom:O("button",{title:"Open file"},T.folder),onclick:e=>e.target.nextElementSibling.click()}):null,l(a.import)?F({dom:O("input",{type:"file",accept:".json",display:"none"}),onchange:e=>this.import(e.target.files[0])}):null,l(a.export)?F({dom:O("button",{title:"Download file"},T.download),onclick:e=>{u("Download?")&&this.export()}}):null,l(a.reset)?F({dom:O("button",{title:"清空"},T.clear),onclick:e=>{u("Reset?")&&this.import({})}}):null])]);const h="pd-json-editor";null==e.id?(this.body=O("section."+h),this.body.appendChild(c)):(this.body=t.getElementById(e.id),this.body.classList.add(h),this.body.replaceChildren(...c.children)),this.body.dataset.fill=o,this.children.length<1&&this.insert(),this.#e.render((async()=>{this.render(),this.#t=!0}))}#r(e){return e.render()}#l(e,t=null){const i=[];if(r.isArray(e))for(let l of e){const e=B(l),r=new J({type:e,parent:t??this,editor:this,lifecycle:this.#e});"object"===e&&null!=l||"array"===e?r.children=this.#l(l,r):r.value=n(l),i.push(r)}else for(const[l,r]of o.entries(e)){const e=B(r),o=new J({key:l,type:e,parent:t??this,editor:this,lifecycle:this.#e});"object"===e&&null!=r||"array"===e?o.children=this.#l(r,o):o.value=null==r?"":n(r),i.push(o)}return i}render(e=!1){let t=O("temp",this.children.map((e=>this.#r(e))));this.editor.replaceChildren(...t.children),this.#t&&e&&this.#e.update((()=>{}))}insert(){this.children.push(new J({parent:this,editor:this,lifecycle:this.#e})),this.render()}get json(){const e={};for(let t of this.children)t.key&&(e[t.key||0]=t.json);return p.stringify(e,null,4)}async import(e){let t=await N(e);this.children=this.#l(t),this.render(!0)}reset(){this.import({})}export(){const e={};for(let t of this.children)(t.key||1===this.children.length)&&(e[t.key||0]=t.json);const n=new a([p.stringify(e,null,4)],{type:"application/json"}),i=s.createObjectURL(n),l=O("a",{href:i,download:`JSONEditor-${h.now()}.json`});t.body.appendChild(l),l.click(),t.body.removeChild(l),s.revokeObjectURL(i)}};class J{key="";type="string";value="";parent;children=[];collapsed=!1;#o;#a;#e;constructor(e={}){"object"==typeof e?(this.id=function(e=64){let t="";for(let n=0;n<e;n++)t+="abcdefghijklmnopqrstuvwxyz0123456789".charAt(c.floor(36*c.random()));return t}(),this.key=e.key??this.key,this.type=e.type??this.type,this.value=e.value??this.value,this.parent=e.parent??this.parent,this.children=e.children??this.children,this.collapsed=e.collapsed??this.collapsed,this.#a=e.editor,this.#e=e.lifecycle):R("Failed to load config form editor node.")}render(){return this.create()}addChild(){this.#s()}updateChild(){this.create(),this.#c()}setCollapsed(){this.collapsed=!this.collapsed,this.create()}get json(){return this.#h()}#c(){this.#e.update((e=>{}))}create(){let e=O("section.pd-json-editor-child",[O("section.pair-wrapper",[O("section#"+this.id+".input-group",[E(this),D(this,this.parent.children.indexOf(this),"array"===this.parent.type,this.#e),O("span",":"),(n=this,O("label",["number"===n.type.toLowerCase()?T.number:"boolean"===n.type.toLowerCase()?T.boolean:"array"===n.type.toLowerCase()?T.array:"object"===n.type.toLowerCase()?T.object:T.string,F({dom:O("select",[...j.map((e=>O("option",{value:e,selected:e===n.type},e)))]),onchange:e=>{if(n.type=e.target.value,{object:1,array:1}[e.target.value.toLowerCase()])n.value="",0===n.children.length&&n.addChild();else if("number"===e.target.value.toLowerCase()){const e=f(n.value);n.value=v(e)?"":e}else n.value="",n.children=[];n.updateChild(),t.getElementById("value-"+n.id).focus()}})])),U(this,this.#e),F({dom:O("button",T.add),onclick:e=>{u("Remove?")&&this.#d()}})]),V(this,((e,t)=>{let n=e.create();return n.dataset.last=t===this.children.length-1?1:0,n}),(()=>{this.#s()}))])]);var n;return this.#o&&this.#o.parentElement.replaceChild(e,this.#o),this.#o=e,this.#o}#s(){const e=new J({parent:this,editor:this.#a,lifecycle:this.#e});this.children.push(e);const t=this.#o.querySelector("section.pd-json-editor-nested-child");if(null!=t){const n=t.children[t.children.length-1];for(let e of n.parentElement.children)e.dataset.last=0;const i=e.create();i.dataset.last=1,t.insertBefore(i,n)}this.#c()}#d(){if(!this.parent)return;const e=this.parent.children.indexOf(this);if(-1===e)return;const t=this.#o.previousElementSibling;"1"===this.#o.dataset.last&&null!=t&&(t.dataset.last=1),this.parent.children.splice(e,1),this.#o.remove(),this.#c()}#h(){if(!this.parent)return;if("array"===this.type)return this.children.map((e=>e.#h()));if("object"===this.type){const e={};for(let t of this.children)(t.key||"array"===this.parent.type)&&(e[t.key||o.keys(e).length]=t.#h());return e}let e=this.value;return"boolean"===this.type?e="true"===e.toLowerCase():"number"===this.type&&(e=i(e)),e}}class I{#p;#u;#f;#v;#y;#b;#g;#m;#w;constructor(e={}){this.#p=e[A]||void 0,this.#u=e[z]||void 0,this.#f=e[H]||void 0,this.#v=e[M]||void 0,this.#y=e[L]||void 0,this.#b=e[S]||void 0}async#C(e){return new m(((t,n)=>{t(!1!==e())}))}#k(e){e(!1)}async render(e){this.#g=h.now(),null!=this.#p&&!1===await this.#C(this.#p)||(await e(),this.#m=h.now()-this.#g,$(`Rendered in ${this.#m}ms.`),null!=this.#u&&this.#k(this.#u))}async update(e){clearTimeout(this.#w),this.#w=d((async()=>{this.#g=h.now(),null!=this.#f&&!1===await this.#C(this.#f)||(await e(),this.#m=h.now()-this.#g,$(`Updated in ${this.#m}ms.`),null!=this.#v&&this.#k(this.#v))}),300)}async destroy(e){this.#g=h.now(),null!=this.#y&&!1===await this.#C(this.#y)||(await e(),this.#m=h.now()-this.#g,$(`Destroyed in ${this.#m}ms.`),null!=this.#b&&this.#k(this.#b))}}}("undefined"==typeof window?window={}:window);export const JSONEditor = window.JSONEditor;