3
copyright 2007-2009 Thomas Frank
5
Permission is hereby granted, free of charge, to any person
6
obtaining a copy of this software and associated documentation
7
files (the "Software"), to deal in the Software without
8
restriction, including without limitation the rights to use,
9
copy, modify, merge, publish, distribute, sublicense, and/or sell
10
copies of the Software, and to permit persons to whom the
11
Software is furnished to do so, subject to the following
14
The above copyright notice and this permission notice shall be
15
included in all copies or substantial portions of the Software.
17
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24
OTHER DEALINGS IN THE SOFTWARE.
28
start:function(treeDivName,formDivName,json,showExamples){
29
if(this.examples.length<6){
30
var e=this.treeBuilder.JSONstring.make(this)
31
eval("this.examples[5]={JSONeditor:"+e+"}")
33
this.treeDivName=treeDivName
34
var t=this.treeBuilder, $=t.$
36
var s=$(treeDivName).style
39
f.innerHTML=this.formHTML
40
if(!showExamples){$('jExamples').style.display="none"}
41
fs.fontSize=s.fontSize="11px"
42
fs.fontFamily=s.fontFamily="Verdana,Arial,Helvetica,sans-serif"
43
var e=f.getElementsByTagName("*")
44
for(var i=0;i<e.length;i++){
48
s.fontFamily="Verdana,Arial,Helvetica,sans-serif"
52
t.JSONbuild(treeDivName,json)
54
loadExample:function(x){
55
treeBuilder.hasRunJSONbuildOnce=false
56
treeBuilder.JSONbuild(this.treeDivName,this.examples[x/1])
58
formHTML:"<form name=\"jsoninput\" onsubmit=\"return treeBuilder.jsonChange(this)\"><div id=\"jExamples\">Load an example: <select name=\"jloadExamples\" onchange=\"JSONeditor.loadExample(this.value)\"><option value=\"0\">None/empty</option><option value=\"1\">Employee data</option><option value=\"2\">Sample Konfabulator Widget</option><option value=\"3\">Member data</option><option value=\"4\">A menu system</option><option value=\"5\">The source code of this JSON editor</option></select><br><br></div>\nLabel:<br><input name=\"jlabel\" type=\"text\" value=\"\" size=\"60\" style=\"width:400px\"><br><br>\nValue: <br><textarea name=\"jvalue\" rows=\"10\" cols=\"50\" style=\"width:400px\"></textarea><br><br>\nData type: <select onchange=\"treeBuilder.changeJsonDataType(this.value,this.parentNode)\" name=\"jtype\">\n<option value=\"object\">object</option>\n<option value=\"array\">array</option>\n<option value=\"function\">function</option>\n<option value=\"string\">string</option>\n<option value=\"number\">number</option>\n<option value=\"boolean\">boolean</option>\n<option value=\"null\">null</option>\n<option value=\"undefined\">undefined</option>\n</select> \n<input name=\"orgjlabel\" type=\"hidden\" value=\"\" size=\"50\" style=\"width:300px\">\n<input onfocus=\"this.blur()\" type=\"submit\" value=\"Save\"> \n<br><br>\n<input name=\"jAddChild\" onfocus=\"this.blur()\" type=\"button\" onclick=\"treeBuilder.jsonAddChild(this.parentNode)\" value=\"Add child\">\n<input name=\"jAddSibling\" onfocus=\"this.blur()\" type=\"button\" onclick=\"treeBuilder.jsonAddSibling(this.parentNode)\" value=\"Add sibling\">\n<br><br>\n<input name=\"jRemove\" onfocus=\"this.blur()\" type=\"button\" onclick=\"treeBuilder.jsonRemove(this.parentNode)\" value=\"Delete\"> \n<input name=\"jRename\" onfocus=\"this.blur()\" type=\"button\" onclick=\"treeBuilder.jsonRename(this.parentNode)\" value=\"Rename\"> \n<input name=\"jCut\" onfocus=\"this.blur()\" type=\"button\" onclick=\"treeBuilder.jsonCut(this.parentNode)\" value=\"Cut\"> \n<input name=\"jCopy\" onfocus=\"this.blur()\" type=\"button\" onclick=\"treeBuilder.jsonCopy(this.parentNode)\" value=\"Copy\"> \n<input name=\"jPaste\" onfocus=\"this.blur()\" type=\"button\" onclick=\"treeBuilder.jsonPaste(this.parentNode)\" value=\"Paste\"> \n<br><br>\n<input type=\"checkbox\" name=\"jbefore\">Add children first/siblings before\n<br>\n<input type=\"checkbox\" name=\"jPasteAsChild\">Paste as child on objects & arrays\n<br><br><div id=\"jformMessage\"></div>\n</form>",
60
{employee:{gid:102, companyID:121, defaultActionID:444,names:{firstName:"Stive", middleInitial:"Jr",lastName:"Martin"},address:{city:"Albany",state:"NY",zipCode:"14410-585",addreess:"41 State Street"},job:{departmentID:102,jobTitleID:100,hireDate:"1/02/2000",terminationDate:"1/12/2007"},contact:{phoneHome:"12-123-2133", beeper:"5656",email1:"info@soft-amis.com",fax:"21-321-23223",phoneMobile:"32-434-3433",phoneOffice:"82-900-8993"},login:{employeeID:"eID102",password:"password",superUser:true,lastLoginDate:"1/12/2007",text:"text", regexp:/^mmm/, date: new Date() },comment:{PCDATA:"comment"},roles:[{role:102},{role:103}]}},
61
{"widget": {"debug": true,"window": {"title": "Sample Konfabulator Widget","name": "main_window","width": 500,"height": 500},"Pairs": [ {"src": "Images/Sun.png","name": "sun1"},{"hOffset": 250,"vOffset": 200},null,{"alignment": "center"}],"text": {"a very long item label here": "Click Here","size": 36,"style": "null","name": "text1","hOffset": 250,"vOffset": 100,"alignment": "center","onmouseover": function(){alert("Hello World");},"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"}}},
62
{"members": [{"href": "1","entity": {"category": [{"term": "weblog", "label": "Weblog stuff"}],"updated": "2007-05-02T23:32:03Z","title": "This is the second post","author": {"uri": "http://dealmeida.net/","email": "roberto@dealmeida.net","name": "Rob de Almeida"},"summary": "Testing search","content": {"content": "This is my second post, to test the search.","type": "text"},"id": "1"}},{"href": "0","entity": {"category": [{"term": "weblog", "label": "Weblog stuff"},{"term": "json", "label": "JSON"}],"updated": "2007-05-02T23:25:59Z","title": "This is the second version of the first post","author": {"uri": "http://dealmeida.net/","email": "roberto@dealmeida.net","name": "Rob de Almeida"},"summary": "This is my first post here, after some modifications","content": {"content": "This is my first post, testing the jsonstore WSGI microapp PUT.","type": "html"},"id": "0"}}],"next": null},
63
{"menu": {"header": "SVG Viewer","items": [{"id": "Open"},{"id": "OpenNew", "label": "Open New", "thing": "thing"},{"id": "ZoomIn", "label": "Zoom In"},{"id": "ZoomOut", "label": "Zoom Out"},{"id": "OriginalView", "label": "Original View"},null,{"id": "Quality"},{"id": "Pause"},{"id": "Mute"},null,{"id": "Find", "label": "Find..."},{"id": "FindAgain", "label": "Find Again"},{"id": "Copy"},{"id": "CopyAgain", "label": "Copy Again"},{"id": "CopySVG", "label": "Copy SVG"},{"id": "ViewSVG", "label": "View SVG"}]}}
69
treeBuilder v 1.00 + a lot of json stuff added...
70
copyright 2007 Thomas Frank
72
JSONeditor.treeBuilder={
78
folderNodeOpenLast:'',
86
folderNodeOpenFirst:'',
87
folderNodeLastFirst:'',
88
folderNodeOpenLastFirst:'',
89
path:'../../images/treeBuilderImages/',
92
$:function(x){return document.getElementById(x)},
94
var x=x.innerHTML.split("\n");
96
for(var i=0;i<x.length;i++){
98
var y=x[i].split("\t");
99
var l=0;while(!y[l]){l++};
100
var la=y[l]?y[l]:'';l++;
102
d.push({level:l,label:la,todo:t});
108
return x.constructor==Array
110
jSyncTree:function(x){
111
var d=this.$(this.baseDiv).getElementsByTagName('div')
112
for(var i=0;i<d.length;i++){
114
treeBuilder.maniClick="giveItBack"
118
treeBuilder.maniClick="selectIt"
121
while(t.id!=this.baseDiv){if(t.style){this.openAndClose(t.id,"open")};t=t.parentNode}
124
treeBuilder.maniClick=false
126
jsonResponder:function(x){
127
this.jTypeChanged=false
128
treeBuilder.jSyncTree(x)
130
eval("var a=treeBuilder."+x)
131
eval("var ap=treeBuilder."+treeBuilder.jsonParent(x))
132
var b=JSON.stringify(a, null, ' ');
133
var t=(a && treeBuilder.isArray(a))?"array":typeof a
134
var tp=(ap && treeBuilder.isArray(ap))?"array":typeof ap
135
if(a===null){t="null"}
136
var f=document.forms.jsoninput
137
if(t=="string"){eval("b="+b)}
142
f.jlabel.disabled=f.jlabel.value=="json"
143
f.jtype.disabled=f.jlabel.disabled
144
f.jRemove.disabled=f.jlabel.disabled
145
f.jAddSibling.disabled=f.jlabel.disabled
146
f.jRename.disabled=f.jlabel.disabled || tp=="array"
147
f.jAddChild.disabled=t!="array" && t!="object"
148
f.jPaste.disabled=!treeBuilder.jClipboard
149
f.jCut.disabled=f.jlabel.disabled
151
jsonParent:function(x){
152
// inmproved thanks to \x000
153
if(x=="json"){return "treeBuilder"}
154
if (x.charAt(x.length-1)==']') {return x.substring(0,x.lastIndexOf('['))}
155
return x.substring(0,x.lastIndexOf('.'))
157
jsonChild:function(el1){
158
var p=this.jsonParent(el1)
159
el1=el1.split(p).join("")
160
if(el1.charAt(0)=="."){el1=el1.substring(1)}
161
if(el1.charAt(0)=="["){el1=el1.substring(2,el1.length-2)}
164
jsonRemove:function(f){
165
this.jsonChange(f,true)
167
jsonAlreadyExists:function(o,l){
168
if(o[l]!==undefined){
170
while(o[l+"_"+co]!==undefined){co++}
172
var p='"'+l+'" already exists in this object.\nDo you want to rename? (otherwise the old "'+l+'" will be overwritten.)'
178
jsonAddChild:function(f,label){
179
var first=f.jbefore.checked
180
var l=f.orgjlabel.value
181
eval('var o=this.'+l)
182
var t=(o && this.isArray(o))?"array":typeof o
184
var nl=label||prompt("Label (without path):","")
186
if(nl/1==nl){nl="$"+nl}
187
nl=this.jsonAlreadyExists(o,nl)
188
var n=nl.replace(/\w/g,'')===""?l+"."+nl:l+'["'+nl+'"]'
189
eval('this.'+n+'={}')
191
eval("var t=this."+l+";this."+l+"={};var s=this."+l)
192
eval('this.'+n+'={}')
193
for(var i in t){s[i]=t[i]}
198
n=l+"["+(o.length-1)+"]"
200
for(var i=o.length-1;i>0;i--){o[i]=o[i-1]}
205
this.JSONbuild(this.baseDiv,this.json)
206
for(var i in this.stateMem){this.openAndClose(i,true)}
207
this.jsonResponder(n)
209
jsonAddSibling:function(f,label){
210
var before=f.jbefore.checked
211
var l=f.orgjlabel.value
213
eval('var temp=this.'+l)
215
var s=this.JSONstring.make(this.json)
217
if(s.length<2){s=s[0].split(r)}
218
var lp=this.jsonParent(l)
219
eval('var o=this.'+lp)
220
var t=(o && this.isArray(o))?"array":typeof o
222
var nl=label||prompt("Label (without path):","")
224
if(nl/1==nl){nl="$"+nl}
225
nl=this.jsonAlreadyExists(o,nl)
226
var n=nl.replace(/\w/g,'')===""?"."+nl:'["'+nl+'"]'
227
s=s.join('null,"'+nl+'":{},')
233
k[k.length-1]=(k[k.length-1].split("]").join("")/1+1)+"]"
236
s=s.split("},}").join("}}") // replace with something better soon
238
eval('this.'+l+'=temp')
239
if(before){lp=this.jsonSwitchPlace(this.jsonParent(l),l,lp)}
240
this.JSONbuild(this.baseDiv,this.json)
241
for(var i in this.stateMem){this.openAndClose(i,true)}
242
this.jsonResponder(lp)
244
jSaveFirst:function(f,a){
245
var l=f.orgjlabel.value
246
eval("var orgj=this."+l)
247
orgj=this.JSONstring.make(orgj)
249
v=f.jtype.value=="string"?this.JSONstring.make(v):v
250
v=v.split("\r").join("")
251
if(orgj!=v || f.orgjlabel.value!=f.jlabel.value || this.jTypeChanged){
252
var k=confirm("Save before "+a+"?")
253
if(k){this.jsonChange(f)}
256
jsonRename:function(f){
257
this.jSaveFirst(f,"renaming")
258
var orgl=l=f.orgjlabel.value
260
var nl=prompt("Label (without path):",l)
262
this.jsonResponder(orgl)
263
var nl=nl.replace(/\w/g,'')===""?"."+nl:'["'+nl+'"]'
264
f.jlabel.value=this.jsonParent(orgl)+nl
265
this.jsonChange(f,false,true)
267
jsonSwitchPlace:function(p,el1,el2){
268
var orgel1=el1, orgel2=el2
269
eval("var o=this."+p)
271
eval("var t=this."+el1)
272
eval("this."+el1+"=this."+el2)
273
eval("this."+el2+"=t")
276
el1=this.jsonChild(el1)
277
el2=this.jsonChild(el2)
280
if(i==el1){o2[el2]=o[el2];o2[el1]=o[el1];continue}
284
eval("this."+p+"=o2")
288
this.jSaveFirst(f,"cutting")
289
this.jsonCopy(f,true)
290
this.jsonChange(f,true)
291
this.setJsonMessage('Cut to clipboard!')
293
jsonCopy:function(f,r){
294
if(!r){this.jSaveFirst(f,"copying")}
295
var l=f.orgjlabel.value
296
eval("var v=this."+l)
297
v=this.JSONstring.make(v)
298
var l=this.jsonChild(l)
299
this.jClipboard={label:l,jvalue:v}
300
this.jsonResponder(f.jlabel.value)
301
if(!r){this.setJsonMessage('Copied to clipboard!')}
303
jsonPaste:function(f,r){
305
var sibling=t!="object" && t!="array"
306
if(!f.jPasteAsChild.checked){sibling=true}
307
if(f.orgjlabel.value=="json"){sibling=false}
308
if(sibling){this.jsonAddSibling(f,this.jClipboard.label)}
309
else {this.jsonAddChild(f,this.jClipboard.label)}
310
var l=f.orgjlabel.value
311
eval("this."+l+"="+this.jClipboard.jvalue)
312
this.jsonResponder(l)
314
if(!r){this.setJsonMessage('Pasted!')}
316
setJsonMessage:function(x){
317
this.$('jformMessage').innerHTML=x
318
setTimeout("treeBuilder.$('jformMessage').innerHTML=''",1500)
320
changeJsonDataType:function(x,f){
321
this.jTypeChanged=true
324
v=x=='object'?'{"label":"'+v+'"}':v
325
v=x=='array'?'["'+v+'"]':v
330
v=x=='function'?'function(){'+v+'}':v
335
v=x=='undefined'?'undefined':v
338
jsonChange:function(f,remove,rename){
341
var orgl=f.orgjlabel.value||"json.not1r2e3a4l"
342
eval("var cur=this."+l)
343
if(l!=orgl && cur!==undefined){
344
var c=confirm(l+"\n\nalready contains other data. Overwrite?")
347
var v=f.jvalue.value.split("\r").join("")
348
if(f.jtype.value=="string"){
349
v=this.JSONstring.make(v)
353
this.JSONbuild(this.baseDiv,v)
354
for(var i in this.stateMem){this.openAndClose(i,true)}
355
this.setJsonMessage('Saved!')
358
eval("var json="+this.JSONstring.make(this.json))
359
var randi=Math.random()
361
var paname=this.jsonParent(orgl)
362
var samepa=this.jsonParent(orgl)==this.jsonParent(l)
363
eval("var pa="+paname)
364
if(this.isArray(pa)){
365
eval(paname+'=[];var newpa='+paname)
366
for(var i=0;i<pa.length;i++){
367
if(pa[i]!=randi){newpa[i]=pa[i]}
370
var pos=l.substring(l.lastIndexOf("[")+1,l.lastIndexOf("]"))/1
371
newpa=newpa.splice(pos,1)
373
if(!remove){eval(l+"="+v)}
376
eval(paname+'={};var newpa='+paname)
378
if(pa[i]!=randi){newpa[i]=pa[i]}
379
else if(samepa && !remove){eval(l+"="+v)}
381
if(!samepa && !remove){eval(l+"="+v)}
384
var selId=this.selectedElement?this.selectedElement.id:null
385
this.JSONbuild(this.baseDiv,this.json)
386
for(var i in this.stateMem){this.openAndClose(i,true)}
387
this.selectedElement=this.$(selId)
388
if(this.selectedElement && !remove && orgl!="json.not1r2e3a4l"){
389
this.selectedElement.style.fontWeight="bold"
392
this.setJsonMessage(remove?'Deleted!':rename?'Renamed!':'Saved!')
393
if(!remove){this.jsonResponder(l)}
396
alert(err+"\n\n"+"Save error!")
400
JSONbuild:function(divName,x,y,z){
407
var t=(x && this.isArray(x))?"array":typeof x
408
y=y===undefined?"json":y
410
this.partMem[z]='["'+y+'"]'
411
if(typeof y!="number" && y.replace(/\w/g,'')===""){this.partMem[z]="."+y}
412
if(typeof y=="number"){this.partMem[z]="["+y+"]"}
413
if(z===0){this.partMem[z]="json"}
414
this.partMem=this.partMem.slice(0,z+1)
416
this.JSONmem.push({type:t,label:y,todo:this.partMem.join(""),level:z+1})
422
for(var i=0;i<l.length;i++){
423
this.JSONbuild(false,x[l[i]],l[i],z+1)
427
for(var i=0;i<x.length;i++){
428
this.JSONbuild(false,x[i],i,z+1)
432
this.build(divName,this.jsonResponder,this.JSONmem)
433
if(!this.hasRunJSONbuildOnce){this.jsonResponder('json')}
434
this.hasRunJSONbuildOnce=true
437
build:function(divName,todoFunc,data){
439
// divName is the id of the div we'll build the tree inside
441
// todoFunc - a function to call on label click with todo as parameter
443
// data should be an array of objects
444
// each object should contain label,todo + level or id and pid (parentId)
446
var d=data, n=divName, $=this.$, lastlevel=0, levelmem=[], im=this.images;
447
this.treeBaseDiv=divName
449
var c=$(divName).childNodes;
450
for(var i=0;i<c.length;i++){
451
if((c[i].tagName+"").toLowerCase()=='pre'){d=this.preParse(c[i])}
455
$(n).style.display="none";
456
while ($(n).firstChild){$(n).removeChild($(n).firstChild)};
457
for(var i=0;i<d.length;i++){
458
if(d[i].level && !lastlevel){lastlevel=d[i].level};
459
if(d[i].level && d[i].level>lastlevel){levelmem.push(n);n=d[i-1].id};
460
if(d[i].level && d[i].level>lastlevel+1){return 'Trying to jump levels!'};
461
if(d[i].level && d[i].level<lastlevel){
462
for(var j=d[i].level;j<lastlevel;j++){n=levelmem.pop()}
464
if(!d[i].id){d[i].id=n+"_"+i};
465
if(!d[i].pid){d[i].pid=n};
466
lastlevel=d[i].level;
467
var a=document.createElement('div');
468
var t=document.createElement('span');
469
t.style.verticalAlign='middle';
470
a.style.whiteSpace='nowrap';
471
var t2=document.createTextNode(d[i].label);
473
a.style.paddingLeft=d[i].pid==divName?'0px':im.nodeWidth+'px';
474
a.style.cursor='pointer';
475
a.style.display=(d[i].pid==divName)?'':'none';
481
a.onclick=function(e){
482
if(treeBuilder.maniClick=="giveItBack"){return todo}
483
if(treeBuilder.selectedElement){
484
treeBuilder.selectedElement.style.fontWeight=""
486
this.style.fontWeight="bold"
487
treeBuilder.selectedElement=this
488
if(treeBuilder.maniClick=="selectIt"){return}
490
if (!e){e=window.event};
491
e.cancelBubble = true;
492
if(e.stopPropagation){e.stopPropagation()};
494
a.onmouseover=function(e){
495
//this.style.color="#999"
496
if (!e){e=window.event};
497
e.cancelBubble = true;
498
if(e.stopPropagation){e.stopPropagation()};
500
a.onmouseout=function(e){
501
//this.style.color=""
502
if (!e){e=window.event};
503
e.cancelBubble = true;
504
if(e.stopPropagation){e.stopPropagation()};
508
$(d[i].pid).appendChild(a);
509
if(d[i].pid==divName && !a.previousSibling){a.first=true};
511
// calculate necessary element looks before initial display
512
for(var i=0;i<d.length;i++){var x=$(d[i].id);if(x && x.style.display!="none"){this.setElementLook(x)}};
513
$(divName).style.display="";
515
setElementLook:function(m){
516
var $=this.$, im=this.images
520
if(!Object.prototype[j]){
521
if(j=="vertLine"){break};
522
var img=document.createElement('img');
523
var k=(m.first && j.indexOf('Node')>=0)?j+'First':j;
524
img.src=im.path+(im[k]?im[k]:k+'.gif');
525
img.style.display="none";
526
img.style.verticalAlign="middle";
528
if(j.indexOf('folderNode')==0){
529
img.onclick=function(e){
530
treeBuilder.openAndClose(this);
531
if (!e){e=window.event};
532
e.cancelBubble = true;
533
if(e.stopPropagation){e.stopPropagation()};
536
if(m.firstChild){m.insertBefore(img,m.childNodes[co]); co++}
537
else {m.appendChild(img)};
540
m.insertBefore(m.t,m.childNodes[co]);
543
var lastChild=m.childNodes[m.childNodes.length-1];
544
var isParent=(lastChild.tagName+"").toLowerCase()=="div";
545
var isLast=!m.nextSibling;
546
var isOpen=isParent && lastChild.style.display!='none';
547
$(m.id+"_folder").style.display=!isOpen && isParent?'':'none';
548
$(m.id+"_folderOpen").style.display=isOpen && isParent?'':'none';
549
$(m.id+"_doc").style.display=isParent?'none':'';
550
$(m.id+"_docNode").style.display=isParent || isLast?'none':'';
551
$(m.id+"_docNodeLast").style.display=isParent || !isLast?'none':'';
552
$(m.id+"_folderNode").style.display=isOpen || !isParent || isLast?'none':'';
553
$(m.id+"_folderNodeLast").style.display=isOpen || !isParent || !isLast?'none':'';
554
$(m.id+"_folderNodeOpen").style.display=!isOpen || !isParent || isLast?'none':'';
555
$(m.id+"_folderNodeOpenLast").style.display=!isOpen || !isParent || !isLast?'none':'';
556
var p=m.parentNode.nextSibling;
558
var sp=p;insideBase=false;
559
while(sp){if(sp==$(this.treeBaseDiv)){insideBase=true};sp=sp.parentNode}
560
if(!insideBase){return}
561
var bg=im.path+(im.vertLine?im.vertLine:'vertLine.gif');
562
m.style.backgroundImage='url('+bg+')';
563
m.style.backgroundRepeat='repeat-y'
566
openAndClose:function(x,remem){
567
var o, div=remem?this.$(x):x.parentNode;
569
if(remem){o=this.stateMem[div.id]}
570
else {o=x.id.indexOf('Open')<0}
571
if(remem=="open"){o=true}
572
this.stateMem[div.id]=o
573
var c=div.childNodes;
574
for(var i=0;i<c.length;i++){
575
if(c[i].tagName.toLowerCase()!="div"){continue};
576
c[i].style.display=o?'':'none';
577
if(o && !c[i].inited){this.setElementLook(c[i])}
579
this.setElementLook(div)
587
copyright 2006 Thomas Frank
589
Based on Steve Yen's implementation:
590
http://trimpath.com/project/wiki/JsonLibrary
593
JSONeditor.treeBuilder.JSONstring={
596
includeFunctions: true,
597
detectCirculars:false,
598
restoreCirculars:false,
599
make:function(arg,restore) {
600
this.restore=restore;
601
this.mem=[];this.pathMem=[];
602
return this.toJsonStringArray(arg).join('');
604
toObject:function(x){
605
eval("this.myObj="+x);
606
if(!this.restoreCirculars || !alert){return this.myObj};
608
this.make(this.myObj,true);
609
var r=this.restoreCode.join(";")+";";
610
eval('r=r.replace(/\\W([0-9]{1,})(\\W)/g,"[$1]$2").replace(/\\.\\;/g,";")');
614
toJsonStringArray:function(arg, out) {
615
if(!out){this.path=[]};
618
switch (typeof arg) {
621
if(this.detectCirculars){
622
var m=this.mem; var n=this.pathMem;
623
for(var i=0;i<m.length;i++){
625
out.push('"JSONcircRef:'+n[i]+'"');return out
628
m.push(arg); n.push(this.path.join("."));
631
if (arg.constructor == Array) {
633
for (var i = 0; i < arg.length; ++i) {
637
this.toJsonStringArray(arg[i], out);
642
} else if (typeof arg.toString != 'undefined') {
646
if(!this.includeProtos && arg[i]===arg.constructor.prototype[i]){continue};
648
var curr = out.length;
650
out.push(this.compactOutput?',':',\n');
651
this.toJsonStringArray(i, out);
653
this.toJsonStringArray(arg[i], out);
654
if (out[out.length - 1] == u)
655
out.splice(curr, out.length - curr);
670
try {eval('var a='+arg)}
671
catch(e){arg='function(){alert("Could not convert the real function to JSON, due to a browser bug only found in Safari. Let us hope it will get fixed in future versions of Safari!")}'}
672
out.push(this.includeFunctions?arg:u);
675
if(this.restore && arg.indexOf("JSONcircRef:")==0){
676
this.restoreCode.push('this.myObj.'+this.path.join(".")+"="+arg.split("JSONcircRef:").join("this.myObj."));
679
var a=['\n','\\n','\r','\\r','"','\\"'];
680
arg+=""; for(var i=0;i<6;i+=2){arg=arg.split(a[i]).join(a[i+1])};
685
out.push(String(arg));
691
http://www.JSON.org/json2.js
696
NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
698
See http://www.JSON.org/js.html
700
This file creates a global JSON object containing two methods: stringify
703
JSON.stringify(value, replacer, space)
704
value any JavaScript value, usually an object or array.
706
replacer an optional parameter that determines how object
707
values are stringified for objects. It can be a
708
function or an array of strings.
710
space an optional parameter that specifies the indentation
711
of nested structures. If it is omitted, the text will
712
be packed without extra whitespace. If it is a number,
713
it will specify the number of spaces to indent at each
714
level. If it is a string (such as '\t' or ' '),
715
it contains the characters used to indent at each level.
717
This method produces a JSON text from a JavaScript value.
719
When an object value is found, if the object contains a toJSON
720
method, its toJSON method will be called and the result will be
721
stringified. A toJSON method does not serialize: it returns the
722
value represented by the name/value pair that should be serialized,
723
or undefined if nothing should be serialized. The toJSON method
724
will be passed the key associated with the value, and this will be
725
bound to the object holding the key.
727
For example, this would serialize Dates as ISO strings.
729
Date.prototype.toJSON = function (key) {
731
// Format integers to have at least two digits.
732
return n < 10 ? '0' + n : n;
735
return this.getUTCFullYear() + '-' +
736
f(this.getUTCMonth() + 1) + '-' +
737
f(this.getUTCDate()) + 'T' +
738
f(this.getUTCHours()) + ':' +
739
f(this.getUTCMinutes()) + ':' +
740
f(this.getUTCSeconds()) + 'Z';
743
You can provide an optional replacer method. It will be passed the
744
key and value of each member, with this bound to the containing
745
object. The value that is returned from your method will be
746
serialized. If your method returns undefined, then the member will
747
be excluded from the serialization.
749
If the replacer parameter is an array of strings, then it will be
750
used to select the members to be serialized. It filters the results
751
such that only members with keys listed in the replacer array are
754
Values that do not have JSON representations, such as undefined or
755
functions, will not be serialized. Such values in objects will be
756
dropped; in arrays they will be replaced with null. You can use
757
a replacer function to replace those with JSON values.
758
JSON.stringify(undefined) returns undefined.
760
The optional space parameter produces a stringification of the
761
value that is filled with line breaks and indentation to make it
764
If the space parameter is a non-empty string, then that string will
765
be used for indentation. If the space parameter is a number, then
766
the indentation will be that many spaces.
770
text = JSON.stringify(['e', {pluribus: 'unum'}]);
771
// text is '["e",{"pluribus":"unum"}]'
774
text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
775
// text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
777
text = JSON.stringify([new Date()], function (key, value) {
778
return this[key] instanceof Date ?
779
'Date(' + this[key] + ')' : value;
781
// text is '["Date(---current time---)"]'
784
JSON.parse(text, reviver)
785
This method parses a JSON text to produce an object or array.
786
It can throw a SyntaxError exception.
788
The optional reviver parameter is a function that can filter and
789
transform the results. It receives each of the keys and values,
790
and its return value is used instead of the original value.
791
If it returns what it received, then the structure is not modified.
792
If it returns undefined then the member is deleted.
796
// Parse the text. Values that look like ISO date strings will
797
// be converted to Date objects.
799
myData = JSON.parse(text, function (key, value) {
801
if (typeof value === 'string') {
803
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
805
return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
812
myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
814
if (typeof value === 'string' &&
815
value.slice(0, 5) === 'Date(' &&
816
value.slice(-1) === ')') {
817
d = new Date(value.slice(5, -1));
826
This is a reference implementation. You are free to copy, modify, or
829
This code should be minified before deployment.
830
See http://javascript.crockford.com/jsmin.html
832
USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
836
/*jslint evil: true */
838
/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
839
call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
840
getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
841
lastIndex, length, parse, prototype, push, replace, slice, stringify,
842
test, toJSON, toString, valueOf
845
// Create a JSON object only if one does not already exist. We create the
846
// methods in a closure to avoid creating global variables.
848
var JSON = JSON || {};
853
// Format integers to have at least two digits.
854
return n < 10 ? '0' + n : n;
857
if (typeof Date.prototype.toJSON !== 'function') {
859
Date.prototype.toJSON = function (key) {
861
return isFinite(this.valueOf()) ?
862
this.getUTCFullYear() + '-' +
863
f(this.getUTCMonth() + 1) + '-' +
864
f(this.getUTCDate()) + 'T' +
865
f(this.getUTCHours()) + ':' +
866
f(this.getUTCMinutes()) + ':' +
867
f(this.getUTCSeconds()) + 'Z' : null;
870
String.prototype.toJSON =
871
Number.prototype.toJSON =
872
Boolean.prototype.toJSON = function (key) {
873
return this.valueOf();
877
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
878
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
881
meta = { // table of character substitutions
893
function quote(string) {
895
// If the string contains no control characters, no quote characters, and no
896
// backslash characters, then we can safely slap some quotes around it.
897
// Otherwise we must also replace the offending characters with safe escape
900
escapable.lastIndex = 0;
901
return escapable.test(string) ?
902
'"' + string.replace(escapable, function (a) {
904
return typeof c === 'string' ? c :
905
'\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
911
function str(key, holder) {
913
// Produce a string from holder[key].
915
var i, // The loop counter.
916
k, // The member key.
917
v, // The member value.
923
// If the value has a toJSON method, call it to obtain a replacement value.
925
if (value && typeof value === 'object' &&
926
typeof value.toJSON === 'function') {
927
value = value.toJSON(key);
930
// If we were called with a replacer function, then call the replacer to
931
// obtain a replacement value.
933
if (typeof rep === 'function') {
934
value = rep.call(holder, key, value);
937
// What happens next depends on the value's type.
939
switch (typeof value) {
945
// JSON numbers must be finite. Encode non-finite numbers as null.
947
return isFinite(value) ? String(value) : 'null';
952
// If the value is a boolean or null, convert it to a string. Note:
953
// typeof null does not produce 'null'. The case is included here in
954
// the remote chance that this gets fixed someday.
956
return String(value);
958
// If the type is 'object', we might be dealing with an object or an array or
963
// Due to a specification blunder in ECMAScript, typeof null is 'object',
964
// so watch out for that case.
970
// Make an array to hold the partial results of stringifying this object value.
975
// Is the value an array?
977
if (Object.prototype.toString.apply(value) === '[object Array]') {
979
// The value is an array. Stringify every element. Use null as a placeholder
980
// for non-JSON values.
982
length = value.length;
983
for (i = 0; i < length; i += 1) {
984
partial[i] = str(i, value) || 'null';
987
// Join all of the elements together, separated with commas, and wrap them in
990
v = partial.length === 0 ? '[]' :
992
partial.join(',\n' + gap) + '\n' +
994
'[' + partial.join(',') + ']';
999
// If the replacer is an array, use it to select the members to be stringified.
1001
if (rep && typeof rep === 'object') {
1002
length = rep.length;
1003
for (i = 0; i < length; i += 1) {
1005
if (typeof k === 'string') {
1008
partial.push(quote(k) + (gap ? ': ' : ':') + v);
1014
// Otherwise, iterate through all of the keys in the object.
1016
var z = new Array();
1022
for (var l=0;l<z.length;l++) {
1023
if (Object.hasOwnProperty.call(value, z[l])) {
1024
v = str(z[l], value);
1026
partial.push(quote(z[l]) + (gap ? ': ' : ':') + v);
1032
// Join all of the member texts together, separated with commas,
1033
// and wrap them in braces.
1035
v = partial.length === 0 ? '{}' :
1036
gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
1037
mind + '}' : '{' + partial.join(',') + '}';
1043
// If the JSON object does not yet have a stringify method, give it one.
1045
if (typeof JSON.stringify !== 'function') {
1046
JSON.stringify = function (value, replacer, space) {
1048
// The stringify method takes a value and an optional replacer, and an optional
1049
// space parameter, and returns a JSON text. The replacer can be a function
1050
// that can replace values, or an array of strings that will select the keys.
1051
// A default replacer method can be provided. Use of the space parameter can
1052
// produce text that is more easily readable.
1058
// If the space parameter is a number, make an indent string containing that
1061
if (typeof space === 'number') {
1062
for (i = 0; i < space; i += 1) {
1066
// If the space parameter is a string, it will be used as the indent string.
1068
} else if (typeof space === 'string') {
1072
// If there is a replacer, it must be a function or an array.
1073
// Otherwise, throw an error.
1076
if (replacer && typeof replacer !== 'function' &&
1077
(typeof replacer !== 'object' ||
1078
typeof replacer.length !== 'number')) {
1079
throw new Error('JSON.stringify');
1082
// Make a fake root object containing our value under the key of ''.
1083
// Return the result of stringifying the value.
1085
return str('', {'': value});
1090
// If the JSON object does not yet have a parse method, give it one.
1092
if (typeof JSON.parse !== 'function') {
1093
JSON.parse = function (text, reviver) {
1095
// The parse method takes a text and an optional reviver function, and returns
1096
// a JavaScript value if the text is a valid JSON text.
1100
function walk(holder, key) {
1102
// The walk method is used to recursively walk the resulting structure so
1103
// that modifications can be made.
1105
var k, v, value = holder[key];
1106
if (value && typeof value === 'object') {
1108
if (Object.hasOwnProperty.call(value, k)) {
1110
if (v !== undefined) {
1118
return reviver.call(holder, key, value);
1122
// Parsing happens in four stages. In the first stage, we replace certain
1123
// Unicode characters with escape sequences. JavaScript handles many characters
1124
// incorrectly, either silently deleting them, or treating them as line endings.
1127
if (cx.test(text)) {
1128
text = text.replace(cx, function (a) {
1130
('0000' + a.charCodeAt(0).toString(16)).slice(-4);
1134
// In the second stage, we run the text against regular expressions that look
1135
// for non-JSON patterns. We are especially concerned with '()' and 'new'
1136
// because they can cause invocation, and '=' because it can cause mutation.
1137
// But just to be safe, we want to reject all unexpected forms.
1139
// We split the second stage into 4 regexp operations in order to work around
1140
// crippling inefficiencies in IE's and Safari's regexp engines. First we
1141
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
1142
// replace all simple value tokens with ']' characters. Third, we delete all
1143
// open brackets that follow a colon or comma or that begin the text. Finally,
1144
// we look to see that the remaining characters are only whitespace or ']' or
1145
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
1147
if (/^[\],:{}\s]*$/.
1148
test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
1149
replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
1150
replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
1152
// In the third stage we use the eval function to compile the text into a
1153
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
1154
// in JavaScript: it can begin a block or an object literal. We wrap the text
1155
// in parens to eliminate the ambiguity.
1157
j = eval('(' + text + ')');
1159
// In the optional fourth stage, we recursively walk the new structure, passing
1160
// each name/value pair to a reviver function for possible transformation.
1162
return typeof reviver === 'function' ?
1163
walk({'': j}, '') : j;
1166
// If the text is not JSON parseable, then a SyntaxError is thrown.
1168
throw new SyntaxError('JSON.parse');