~ubuntu-branches/ubuntu/karmic/chef/karmic

« back to all changes in this revision

Viewing changes to chef-server-slice/public/javascripts/JSONeditor.js

  • Committer: Bazaar Package Importer
  • Author(s): Joshua Timberman
  • Date: 2009-08-12 12:18:48 UTC
  • Revision ID: james.westby@ubuntu.com-20090812121848-yl38hpd7nfsl51xz
Tags: upstream-0.7.8
ImportĀ upstreamĀ versionĀ 0.7.8

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
jsonEditor 1.02
 
3
copyright 2007-2009 Thomas Frank
 
4
 
 
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
 
12
conditions:
 
13
 
 
14
The above copyright notice and this permission notice shall be
 
15
included in all copies or substantial portions of the Software.
 
16
 
 
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.
 
25
*/
 
26
 
 
27
JSONeditor={
 
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+"}")
 
32
                }
 
33
                this.treeDivName=treeDivName
 
34
                var t=this.treeBuilder, $=t.$
 
35
                treeBuilder=t
 
36
                var s=$(treeDivName).style
 
37
                var f=$(formDivName)
 
38
                var fs=f.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++){
 
45
                        var s=e[i].style
 
46
                        if(s){
 
47
                                s.fontSize="11px"
 
48
                                s.fontFamily="Verdana,Arial,Helvetica,sans-serif"
 
49
                        }
 
50
                }
 
51
                json=json||{}
 
52
                t.JSONbuild(treeDivName,json)
 
53
        },
 
54
        loadExample:function(x){
 
55
                treeBuilder.hasRunJSONbuildOnce=false
 
56
                treeBuilder.JSONbuild(this.treeDivName,this.examples[x/1])
 
57
        },
 
58
        formHTML:"<form name=\"jsoninput\" onsubmit=\"return treeBuilder.jsonChange(this)\"><div id=\"jExamples\">Load an example:&nbsp;<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>&nbsp;&nbsp;&nbsp;&nbsp;\n<input name=\"orgjlabel\" type=\"hidden\" value=\"\" size=\"50\" style=\"width:300px\">\n<input onfocus=\"this.blur()\" type=\"submit\" value=\"Save\">&nbsp;\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\">&nbsp;\n<input name=\"jRename\" onfocus=\"this.blur()\" type=\"button\" onclick=\"treeBuilder.jsonRename(this.parentNode)\" value=\"Rename\">&nbsp;\n<input name=\"jCut\" onfocus=\"this.blur()\" type=\"button\" onclick=\"treeBuilder.jsonCut(this.parentNode)\" value=\"Cut\">&nbsp;\n<input name=\"jCopy\" onfocus=\"this.blur()\" type=\"button\" onclick=\"treeBuilder.jsonCopy(this.parentNode)\" value=\"Copy\">&nbsp;\n<input name=\"jPaste\" onfocus=\"this.blur()\" type=\"button\" onclick=\"treeBuilder.jsonPaste(this.parentNode)\" value=\"Paste\">&nbsp;\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>",
 
59
        examples:[{},
 
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"}]}}
 
64
        ]
 
65
}
 
66
 
 
67
 
 
68
/*
 
69
treeBuilder v 1.00 + a lot of json stuff added...
 
70
copyright 2007 Thomas Frank
 
71
*/
 
72
JSONeditor.treeBuilder={
 
73
        stateMem:{},
 
74
        images:{
 
75
                folderNode:'',
 
76
                folderNodeOpen:'',
 
77
                folderNodeLast:'',
 
78
                folderNodeOpenLast:'',
 
79
                docNode:'',
 
80
                docNodeLast:'',
 
81
                folder:'',
 
82
                folderOpen:'',
 
83
                doc:'',
 
84
                vertLine:'',
 
85
                folderNodeFirst:'',
 
86
                folderNodeOpenFirst:'',
 
87
                folderNodeLastFirst:'',
 
88
                folderNodeOpenLastFirst:'',
 
89
                path:'../../images/treeBuilderImages/',
 
90
                nodeWidth:16
 
91
        },
 
92
        $:function(x){return document.getElementById(x)},
 
93
        preParse:function(x){
 
94
                var x=x.innerHTML.split("\n");
 
95
                var d=[];
 
96
                for(var i=0;i<x.length;i++){
 
97
                        if(x[i]){
 
98
                                var y=x[i].split("\t");
 
99
                                var l=0;while(!y[l]){l++};
 
100
                                var la=y[l]?y[l]:'';l++;
 
101
                                var t=y[l]?y[l]:'';
 
102
                                d.push({level:l,label:la,todo:t});
 
103
                        }
 
104
                };
 
105
                return d
 
106
        },
 
107
        isArray:function(x){
 
108
                return x.constructor==Array
 
109
        },
 
110
        jSyncTree:function(x){
 
111
                var d=this.$(this.baseDiv).getElementsByTagName('div')
 
112
                for(var i=0;i<d.length;i++){
 
113
                        
 
114
                        treeBuilder.maniClick="giveItBack"
 
115
                        var p=d[i].onclick()
 
116
                        if(p==x){
 
117
                                var t=d[i]
 
118
                                treeBuilder.maniClick="selectIt"
 
119
                                t.onclick()
 
120
                                t=t.parentNode
 
121
                                while(t.id!=this.baseDiv){if(t.style){this.openAndClose(t.id,"open")};t=t.parentNode}
 
122
                        }
 
123
                }
 
124
                treeBuilder.maniClick=false
 
125
        },
 
126
        jsonResponder:function(x){
 
127
                this.jTypeChanged=false
 
128
                treeBuilder.jSyncTree(x)
 
129
                var t=treeBuilder
 
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)}
 
138
                f.jlabel.value=x
 
139
                f.orgjlabel.value=x
 
140
                f.jvalue.value=b
 
141
                f.jtype.value=t
 
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
 
150
        },
 
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('.'))     
 
156
        },      
 
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)}
 
162
                return el1
 
163
        },
 
164
        jsonRemove:function(f){
 
165
                this.jsonChange(f,true)
 
166
        },
 
167
        jsonAlreadyExists:function(o,l){
 
168
                if(o[l]!==undefined){
 
169
                        var co=2
 
170
                        while(o[l+"_"+co]!==undefined){co++}
 
171
                        var n=l+"_"+co
 
172
                        var p='"'+l+'" already exists in this object.\nDo you want to rename? (otherwise the old "'+l+'" will be overwritten.)'
 
173
                        p=prompt(p,n)
 
174
                        if(p){l=p}
 
175
                }
 
176
                return l
 
177
        },
 
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
 
183
                if(t=="object"){
 
184
                        var nl=label||prompt("Label (without path):","")
 
185
                        if(!nl){return}
 
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+'={}')
 
190
                        if(first){
 
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]}                              
 
194
                        }
 
195
                }
 
196
                if(t=="array"){
 
197
                        o.push({})
 
198
                        n=l+"["+(o.length-1)+"]"
 
199
                        if(first){
 
200
                                for(var i=o.length-1;i>0;i--){o[i]=o[i-1]}
 
201
                                o[0]={}
 
202
                                n=l+"[0]"
 
203
                        }
 
204
                }
 
205
                this.JSONbuild(this.baseDiv,this.json)
 
206
                for(var i in this.stateMem){this.openAndClose(i,true)}
 
207
                this.jsonResponder(n)
 
208
        },
 
209
        jsonAddSibling:function(f,label){
 
210
                var before=f.jbefore.checked
 
211
                var l=f.orgjlabel.value
 
212
                var r=Math.random()
 
213
                eval('var temp=this.'+l)
 
214
                eval('this.'+l+"=r")
 
215
                var s=this.JSONstring.make(this.json)
 
216
                s=s.split(r+",")
 
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
 
221
                if(t=="object"){
 
222
                        var nl=label||prompt("Label (without path):","")
 
223
                        if(!nl){return}
 
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+'":{},')
 
228
                        lp+=n
 
229
                }
 
230
                if(t=="array"){
 
231
                        s=s.join('null,{},')
 
232
                        var k=l.split("[")
 
233
                        k[k.length-1]=(k[k.length-1].split("]").join("")/1+1)+"]"
 
234
                        lp=k.join("[")
 
235
                }
 
236
                s=s.split("},}").join("}}") // replace with something better soon
 
237
                eval('this.json='+s)
 
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)
 
243
        },
 
244
        jSaveFirst:function(f,a){
 
245
                var l=f.orgjlabel.value
 
246
                eval("var orgj=this."+l)
 
247
                orgj=this.JSONstring.make(orgj)
 
248
                var v=f.jvalue.value
 
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)}
 
254
                }
 
255
        },
 
256
        jsonRename:function(f){
 
257
                this.jSaveFirst(f,"renaming")
 
258
                var orgl=l=f.orgjlabel.value
 
259
                l=this.jsonChild(l)
 
260
                var nl=prompt("Label (without path):",l)
 
261
                if(!nl){return}
 
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)
 
266
        },
 
267
        jsonSwitchPlace:function(p,el1,el2){
 
268
                var orgel1=el1, orgel2=el2
 
269
                eval("var o=this."+p)
 
270
                if(this.isArray(o)){
 
271
                        eval("var t=this."+el1)
 
272
                        eval("this."+el1+"=this."+el2)
 
273
                        eval("this."+el2+"=t")
 
274
                        return orgel1
 
275
                }
 
276
                el1=this.jsonChild(el1)
 
277
                el2=this.jsonChild(el2)
 
278
                var o2={}
 
279
                for(var i in o){
 
280
                        if(i==el1){o2[el2]=o[el2];o2[el1]=o[el1];continue}
 
281
                        if(i==el2){continue}
 
282
                        o2[i]=o[i]
 
283
                }
 
284
                eval("this."+p+"=o2")
 
285
                return orgel2
 
286
        },
 
287
        jsonCut:function(f){
 
288
                this.jSaveFirst(f,"cutting")
 
289
                this.jsonCopy(f,true)
 
290
                this.jsonChange(f,true)
 
291
                this.setJsonMessage('Cut to clipboard!')
 
292
        },
 
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!')}
 
302
        },
 
303
        jsonPaste:function(f,r){
 
304
                var t=f.jtype.value
 
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)
 
313
                this.jsonChange(f)
 
314
                if(!r){this.setJsonMessage('Pasted!')}
 
315
        },
 
316
        setJsonMessage:function(x){
 
317
                this.$('jformMessage').innerHTML=x
 
318
                setTimeout("treeBuilder.$('jformMessage').innerHTML=''",1500)
 
319
        },
 
320
        changeJsonDataType:function(x,f){
 
321
                this.jTypeChanged=true
 
322
                var v=f.jvalue.value
 
323
                var orgv=v;
 
324
                v=x=='object'?'{"label":"'+v+'"}':v
 
325
                v=x=='array'?'["'+v+'"]':v
 
326
                if(!orgv){
 
327
                        v=x=='object'?'{}':v
 
328
                        v=x=='array'?'[]':v
 
329
                }
 
330
                v=x=='function'?'function(){'+v+'}':v
 
331
                v=x=='string'?v:v
 
332
                v=x=='number'?v/1:v
 
333
                v=x=='boolean'?!!v:v
 
334
                v=x=='null'?'null':v
 
335
                v=x=='undefined'?'undefined':v
 
336
                f.jvalue.value=v                
 
337
        },
 
338
        jsonChange:function(f,remove,rename){
 
339
                try {
 
340
                        var l=f.jlabel.value
 
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?")
 
345
                                if(!c){return false}
 
346
                        }
 
347
                        var v=f.jvalue.value.split("\r").join("")
 
348
                        if(f.jtype.value=="string"){
 
349
                                v=this.JSONstring.make(v)
 
350
                        }
 
351
                        if(l=="json"){
 
352
                                eval("v="+v)
 
353
                                this.JSONbuild(this.baseDiv,v)
 
354
                                for(var i in this.stateMem){this.openAndClose(i,true)}
 
355
                                this.setJsonMessage('Saved!')
 
356
                                return false
 
357
                        }
 
358
                        eval("var json="+this.JSONstring.make(this.json))
 
359
                        var randi=Math.random()
 
360
                        eval(orgl+'='+randi)
 
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]}
 
368
                                }
 
369
                                if(remove){
 
370
                                        var pos=l.substring(l.lastIndexOf("[")+1,l.lastIndexOf("]"))/1
 
371
                                        newpa=newpa.splice(pos,1)
 
372
                                }
 
373
                                if(!remove){eval(l+"="+v)}
 
374
                        }               
 
375
                        else {
 
376
                                eval(paname+'={};var newpa='+paname)
 
377
                                for(var i in pa){
 
378
                                        if(pa[i]!=randi){newpa[i]=pa[i]}
 
379
                                        else if(samepa && !remove){eval(l+"="+v)}
 
380
                                }
 
381
                                if(!samepa && !remove){eval(l+"="+v)}
 
382
                        }
 
383
                        this.json=json
 
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"
 
390
                        }
 
391
                        if(remove){l=""}
 
392
                        this.setJsonMessage(remove?'Deleted!':rename?'Renamed!':'Saved!')
 
393
                        if(!remove){this.jsonResponder(l)}
 
394
                }
 
395
                catch(err){
 
396
                        alert(err+"\n\n"+"Save error!")
 
397
                }
 
398
                return false
 
399
        },
 
400
        JSONbuild:function(divName,x,y,z){
 
401
                if(!z){
 
402
                        this.partMem=[]
 
403
                        this.JSONmem=[]
 
404
                        this.json=x
 
405
                        this.baseDiv=divName
 
406
                }
 
407
                var t=(x && this.isArray(x))?"array":typeof x
 
408
                y=y===undefined?"json":y
 
409
                z=z||0
 
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)
 
415
                var x2=x
 
416
                this.JSONmem.push({type:t,label:y,todo:this.partMem.join(""),level:z+1})
 
417
                if(t=="object"){
 
418
                        var l = new Array();
 
419
                        for(var i in x)
 
420
                                l.push(i);
 
421
                        l.sort();
 
422
                        for(var i=0;i<l.length;i++){
 
423
                                this.JSONbuild(false,x[l[i]],l[i],z+1)
 
424
                        }
 
425
                }
 
426
                if(t=="array"){
 
427
                        for(var i=0;i<x.length;i++){
 
428
                                this.JSONbuild(false,x[i],i,z+1)
 
429
                        }
 
430
                }
 
431
                if(divName){
 
432
                        this.build(divName,this.jsonResponder,this.JSONmem)
 
433
                        if(!this.hasRunJSONbuildOnce){this.jsonResponder('json')}
 
434
                        this.hasRunJSONbuildOnce=true
 
435
                }
 
436
        },
 
437
        build:function(divName,todoFunc,data){
 
438
                //
 
439
                // divName is the id of the div we'll build the tree inside
 
440
                //
 
441
                // todoFunc - a function to call on label click with todo as parameter
 
442
                //
 
443
                // data should be an array of objects
 
444
                // each object should contain label,todo + level or id and pid (parentId)
 
445
                //
 
446
                var d=data, n=divName, $=this.$, lastlevel=0, levelmem=[], im=this.images;
 
447
                this.treeBaseDiv=divName
 
448
                if(!d){
 
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])}
 
452
                        };
 
453
                        if(!d){return}
 
454
                };
 
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()}
 
463
                        };
 
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);
 
472
                        t.appendChild(t2);
 
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';
 
476
                        a.id=d[i].id;
 
477
                        a.t=t;
 
478
                        var f=function(){
 
479
                                var todo=d[i].todo;
 
480
                                var func=todoFunc;
 
481
                                a.onclick=function(e){
 
482
                                        if(treeBuilder.maniClick=="giveItBack"){return todo}
 
483
                                        if(treeBuilder.selectedElement){
 
484
                                                treeBuilder.selectedElement.style.fontWeight=""
 
485
                                        }
 
486
                                        this.style.fontWeight="bold"
 
487
                                        treeBuilder.selectedElement=this
 
488
                                        if(treeBuilder.maniClick=="selectIt"){return}
 
489
                                        func(todo);
 
490
                                        if (!e){e=window.event};
 
491
                                        e.cancelBubble = true;
 
492
                                        if(e.stopPropagation){e.stopPropagation()};
 
493
                                };
 
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()};
 
499
                                };
 
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()};
 
505
                                };
 
506
                        };
 
507
                        f();
 
508
                        $(d[i].pid).appendChild(a);
 
509
                        if(d[i].pid==divName && !a.previousSibling){a.first=true};
 
510
                };
 
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="";
 
514
        },
 
515
        setElementLook:function(m){
 
516
                var $=this.$, im=this.images
 
517
                if(!m.inited){
 
518
                        var co=0
 
519
                        for(var j in im){
 
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";
 
527
                                        img.id=m.id+"_"+j;
 
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()};
 
534
                                                }
 
535
                                        };
 
536
                                        if(m.firstChild){m.insertBefore(img,m.childNodes[co]); co++}
 
537
                                        else {m.appendChild(img)};
 
538
                                }
 
539
                        };
 
540
                        m.insertBefore(m.t,m.childNodes[co]);
 
541
                        m.inited=true
 
542
                };
 
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;
 
557
                if(p && p.id){
 
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'
 
564
                };
 
565
        },
 
566
        openAndClose:function(x,remem){
 
567
                var o, div=remem?this.$(x):x.parentNode;
 
568
                if(!div){return}
 
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])}
 
578
                };
 
579
                this.setElementLook(div)
 
580
        }
 
581
}
 
582
 
 
583
 
 
584
 
 
585
/*
 
586
JSONstring v 1.0
 
587
copyright 2006 Thomas Frank
 
588
 
 
589
Based on Steve Yen's implementation:
 
590
http://trimpath.com/project/wiki/JsonLibrary
 
591
*/
 
592
 
 
593
JSONeditor.treeBuilder.JSONstring={
 
594
        compactOutput:false,            
 
595
        includeProtos:false,    
 
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('');
 
603
        },
 
604
        toObject:function(x){
 
605
                eval("this.myObj="+x);
 
606
                if(!this.restoreCirculars || !alert){return this.myObj};
 
607
                this.restoreCode=[];
 
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,";")');
 
611
                eval(r);
 
612
                return this.myObj
 
613
        },
 
614
        toJsonStringArray:function(arg, out) {
 
615
                if(!out){this.path=[]};
 
616
                out = out || [];
 
617
                var u; // undefined
 
618
                switch (typeof arg) {
 
619
                case 'object':
 
620
                        this.lastObj=arg;
 
621
                        if(this.detectCirculars){
 
622
                                var m=this.mem; var n=this.pathMem;
 
623
                                for(var i=0;i<m.length;i++){
 
624
                                        if(arg===m[i]){
 
625
                                                out.push('"JSONcircRef:'+n[i]+'"');return out
 
626
                                        }
 
627
                                };
 
628
                                m.push(arg); n.push(this.path.join("."));
 
629
                        };
 
630
                        if (arg) {
 
631
                                if (arg.constructor == Array) {
 
632
                                        out.push('[');
 
633
                                        for (var i = 0; i < arg.length; ++i) {
 
634
                                                this.path.push(i);
 
635
                                                if (i > 0)
 
636
                                                        out.push(',\n');
 
637
                                                this.toJsonStringArray(arg[i], out);
 
638
                                                this.path.pop();
 
639
                                        }
 
640
                                        out.push(']');
 
641
                                        return out;
 
642
                                } else if (typeof arg.toString != 'undefined') {
 
643
                                        out.push('{');
 
644
                                        var first = true;
 
645
                                        for (var i in arg) {
 
646
                                                if(!this.includeProtos && arg[i]===arg.constructor.prototype[i]){continue};
 
647
                                                this.path.push(i);
 
648
                                                var curr = out.length; 
 
649
                                                if (!first)
 
650
                                                        out.push(this.compactOutput?',':',\n');
 
651
                                                this.toJsonStringArray(i, out);
 
652
                                                out.push(':');                    
 
653
                                                this.toJsonStringArray(arg[i], out);
 
654
                                                if (out[out.length - 1] == u)
 
655
                                                        out.splice(curr, out.length - curr);
 
656
                                                else
 
657
                                                        first = false;
 
658
                                                this.path.pop();
 
659
                                        }
 
660
                                        out.push('}');
 
661
                                        return out;
 
662
                                }
 
663
                                return out;
 
664
                        }
 
665
                        out.push('null');
 
666
                        return out;
 
667
                case 'unknown':
 
668
                case 'undefined':
 
669
                case 'function':
 
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);
 
673
                        return out;
 
674
                case 'string':
 
675
                        if(this.restore && arg.indexOf("JSONcircRef:")==0){
 
676
                                this.restoreCode.push('this.myObj.'+this.path.join(".")+"="+arg.split("JSONcircRef:").join("this.myObj."));
 
677
                        };
 
678
                        out.push('"');
 
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])};
 
681
                        out.push(arg);
 
682
                        out.push('"');
 
683
                        return out;
 
684
                default:
 
685
                        out.push(String(arg));
 
686
                        return out;
 
687
                }
 
688
        }
 
689
}
 
690
/*
 
691
    http://www.JSON.org/json2.js
 
692
    2009-06-29
 
693
 
 
694
    Public Domain.
 
695
 
 
696
    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
 
697
 
 
698
    See http://www.JSON.org/js.html
 
699
 
 
700
    This file creates a global JSON object containing two methods: stringify
 
701
    and parse.
 
702
 
 
703
        JSON.stringify(value, replacer, space)
 
704
            value       any JavaScript value, usually an object or array.
 
705
 
 
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.
 
709
 
 
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 '&nbsp;'),
 
715
                        it contains the characters used to indent at each level.
 
716
 
 
717
            This method produces a JSON text from a JavaScript value.
 
718
 
 
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.
 
726
 
 
727
            For example, this would serialize Dates as ISO strings.
 
728
 
 
729
                Date.prototype.toJSON = function (key) {
 
730
                    function f(n) {
 
731
                        // Format integers to have at least two digits.
 
732
                        return n < 10 ? '0' + n : n;
 
733
                    }
 
734
 
 
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';
 
741
                };
 
742
 
 
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.
 
748
 
 
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
 
752
            stringified.
 
753
 
 
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.
 
759
 
 
760
            The optional space parameter produces a stringification of the
 
761
            value that is filled with line breaks and indentation to make it
 
762
            easier to read.
 
763
 
 
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.
 
767
 
 
768
            Example:
 
769
 
 
770
            text = JSON.stringify(['e', {pluribus: 'unum'}]);
 
771
            // text is '["e",{"pluribus":"unum"}]'
 
772
 
 
773
 
 
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]'
 
776
 
 
777
            text = JSON.stringify([new Date()], function (key, value) {
 
778
                return this[key] instanceof Date ?
 
779
                    'Date(' + this[key] + ')' : value;
 
780
            });
 
781
            // text is '["Date(---current time---)"]'
 
782
 
 
783
 
 
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.
 
787
 
 
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.
 
793
 
 
794
            Example:
 
795
 
 
796
            // Parse the text. Values that look like ISO date strings will
 
797
            // be converted to Date objects.
 
798
 
 
799
            myData = JSON.parse(text, function (key, value) {
 
800
                var a;
 
801
                if (typeof value === 'string') {
 
802
                    a =
 
803
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
 
804
                    if (a) {
 
805
                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
 
806
                            +a[5], +a[6]));
 
807
                    }
 
808
                }
 
809
                return value;
 
810
            });
 
811
 
 
812
            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
 
813
                var d;
 
814
                if (typeof value === 'string' &&
 
815
                        value.slice(0, 5) === 'Date(' &&
 
816
                        value.slice(-1) === ')') {
 
817
                    d = new Date(value.slice(5, -1));
 
818
                    if (d) {
 
819
                        return d;
 
820
                    }
 
821
                }
 
822
                return value;
 
823
            });
 
824
 
 
825
 
 
826
    This is a reference implementation. You are free to copy, modify, or
 
827
    redistribute.
 
828
 
 
829
    This code should be minified before deployment.
 
830
    See http://javascript.crockford.com/jsmin.html
 
831
 
 
832
    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
 
833
    NOT CONTROL.
 
834
*/
 
835
 
 
836
/*jslint evil: true */
 
837
 
 
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
 
843
*/
 
844
 
 
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.
 
847
 
 
848
var JSON = JSON || {};
 
849
 
 
850
(function () {
 
851
 
 
852
    function f(n) {
 
853
        // Format integers to have at least two digits.
 
854
        return n < 10 ? '0' + n : n;
 
855
    }
 
856
 
 
857
    if (typeof Date.prototype.toJSON !== 'function') {
 
858
 
 
859
        Date.prototype.toJSON = function (key) {
 
860
 
 
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;
 
868
        };
 
869
 
 
870
        String.prototype.toJSON =
 
871
        Number.prototype.toJSON =
 
872
        Boolean.prototype.toJSON = function (key) {
 
873
            return this.valueOf();
 
874
        };
 
875
    }
 
876
 
 
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,
 
879
        gap,
 
880
        indent,
 
881
        meta = {    // table of character substitutions
 
882
            '\b': '\\b',
 
883
            '\t': '\\t',
 
884
            '\n': '\\n',
 
885
            '\f': '\\f',
 
886
            '\r': '\\r',
 
887
            '"' : '\\"',
 
888
            '\\': '\\\\'
 
889
        },
 
890
        rep;
 
891
 
 
892
 
 
893
    function quote(string) {
 
894
 
 
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
 
898
// sequences.
 
899
 
 
900
        escapable.lastIndex = 0;
 
901
        return escapable.test(string) ?
 
902
            '"' + string.replace(escapable, function (a) {
 
903
                var c = meta[a];
 
904
                return typeof c === 'string' ? c :
 
905
                    '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
 
906
            }) + '"' :
 
907
            '"' + string + '"';
 
908
    }
 
909
 
 
910
 
 
911
    function str(key, holder) {
 
912
 
 
913
// Produce a string from holder[key].
 
914
 
 
915
        var i,          // The loop counter.
 
916
            k,          // The member key.
 
917
            v,          // The member value.
 
918
            length,
 
919
            mind = gap,
 
920
            partial,
 
921
            value = holder[key];
 
922
 
 
923
// If the value has a toJSON method, call it to obtain a replacement value.
 
924
 
 
925
        if (value && typeof value === 'object' &&
 
926
                typeof value.toJSON === 'function') {
 
927
            value = value.toJSON(key);
 
928
        }
 
929
 
 
930
// If we were called with a replacer function, then call the replacer to
 
931
// obtain a replacement value.
 
932
 
 
933
        if (typeof rep === 'function') {
 
934
            value = rep.call(holder, key, value);
 
935
        }
 
936
 
 
937
// What happens next depends on the value's type.
 
938
 
 
939
        switch (typeof value) {
 
940
        case 'string':
 
941
            return quote(value);
 
942
 
 
943
        case 'number':
 
944
 
 
945
// JSON numbers must be finite. Encode non-finite numbers as null.
 
946
 
 
947
            return isFinite(value) ? String(value) : 'null';
 
948
 
 
949
        case 'boolean':
 
950
        case 'null':
 
951
 
 
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.
 
955
 
 
956
            return String(value);
 
957
 
 
958
// If the type is 'object', we might be dealing with an object or an array or
 
959
// null.
 
960
 
 
961
        case 'object':
 
962
 
 
963
// Due to a specification blunder in ECMAScript, typeof null is 'object',
 
964
// so watch out for that case.
 
965
 
 
966
            if (!value) {
 
967
                return 'null';
 
968
            }
 
969
 
 
970
// Make an array to hold the partial results of stringifying this object value.
 
971
 
 
972
            gap += indent;
 
973
            partial = [];
 
974
 
 
975
// Is the value an array?
 
976
 
 
977
            if (Object.prototype.toString.apply(value) === '[object Array]') {
 
978
 
 
979
// The value is an array. Stringify every element. Use null as a placeholder
 
980
// for non-JSON values.
 
981
 
 
982
                length = value.length;
 
983
                for (i = 0; i < length; i += 1) {
 
984
                    partial[i] = str(i, value) || 'null';
 
985
                }
 
986
 
 
987
// Join all of the elements together, separated with commas, and wrap them in
 
988
// brackets.
 
989
 
 
990
                v = partial.length === 0 ? '[]' :
 
991
                    gap ? '[\n' + gap +
 
992
                            partial.join(',\n' + gap) + '\n' +
 
993
                                mind + ']' :
 
994
                          '[' + partial.join(',') + ']';
 
995
                gap = mind;
 
996
                return v;
 
997
            }
 
998
 
 
999
// If the replacer is an array, use it to select the members to be stringified.
 
1000
 
 
1001
            if (rep && typeof rep === 'object') {
 
1002
                length = rep.length;
 
1003
                for (i = 0; i < length; i += 1) {
 
1004
                    k = rep[i];
 
1005
                    if (typeof k === 'string') {
 
1006
                        v = str(k, value);
 
1007
                        if (v) {
 
1008
                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
 
1009
                        }
 
1010
                    }
 
1011
                }
 
1012
            } else {
 
1013
 
 
1014
// Otherwise, iterate through all of the keys in the object.
 
1015
 
 
1016
                var z = new Array();
 
1017
                for (k in value)
 
1018
                    z.push(k);
 
1019
 
 
1020
                z.sort();
 
1021
 
 
1022
                for (var l=0;l<z.length;l++) {
 
1023
                    if (Object.hasOwnProperty.call(value, z[l])) {
 
1024
                        v = str(z[l], value);
 
1025
                        if (v) {
 
1026
                            partial.push(quote(z[l]) + (gap ? ': ' : ':') + v);
 
1027
                        }
 
1028
                    }
 
1029
                }
 
1030
            }
 
1031
 
 
1032
// Join all of the member texts together, separated with commas,
 
1033
// and wrap them in braces.
 
1034
 
 
1035
            v = partial.length === 0 ? '{}' :
 
1036
                gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
 
1037
                        mind + '}' : '{' + partial.join(',') + '}';
 
1038
            gap = mind;
 
1039
            return v;
 
1040
        }
 
1041
    }
 
1042
 
 
1043
// If the JSON object does not yet have a stringify method, give it one.
 
1044
 
 
1045
    if (typeof JSON.stringify !== 'function') {
 
1046
        JSON.stringify = function (value, replacer, space) {
 
1047
 
 
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.
 
1053
 
 
1054
            var i;
 
1055
            gap = '';
 
1056
            indent = '';
 
1057
 
 
1058
// If the space parameter is a number, make an indent string containing that
 
1059
// many spaces.
 
1060
 
 
1061
            if (typeof space === 'number') {
 
1062
                for (i = 0; i < space; i += 1) {
 
1063
                    indent += ' ';
 
1064
                }
 
1065
 
 
1066
// If the space parameter is a string, it will be used as the indent string.
 
1067
 
 
1068
            } else if (typeof space === 'string') {
 
1069
                indent = space;
 
1070
            }
 
1071
 
 
1072
// If there is a replacer, it must be a function or an array.
 
1073
// Otherwise, throw an error.
 
1074
 
 
1075
            rep = replacer;
 
1076
            if (replacer && typeof replacer !== 'function' &&
 
1077
                    (typeof replacer !== 'object' ||
 
1078
                     typeof replacer.length !== 'number')) {
 
1079
                throw new Error('JSON.stringify');
 
1080
            }
 
1081
 
 
1082
// Make a fake root object containing our value under the key of ''.
 
1083
// Return the result of stringifying the value.
 
1084
 
 
1085
            return str('', {'': value});
 
1086
        };
 
1087
    }
 
1088
 
 
1089
 
 
1090
// If the JSON object does not yet have a parse method, give it one.
 
1091
 
 
1092
    if (typeof JSON.parse !== 'function') {
 
1093
        JSON.parse = function (text, reviver) {
 
1094
 
 
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.
 
1097
 
 
1098
            var j;
 
1099
 
 
1100
            function walk(holder, key) {
 
1101
 
 
1102
// The walk method is used to recursively walk the resulting structure so
 
1103
// that modifications can be made.
 
1104
 
 
1105
                var k, v, value = holder[key];
 
1106
                if (value && typeof value === 'object') {
 
1107
                    for (k in value) {
 
1108
                        if (Object.hasOwnProperty.call(value, k)) {
 
1109
                            v = walk(value, k);
 
1110
                            if (v !== undefined) {
 
1111
                                value[k] = v;
 
1112
                            } else {
 
1113
                                delete value[k];
 
1114
                            }
 
1115
                        }
 
1116
                    }
 
1117
                }
 
1118
                return reviver.call(holder, key, value);
 
1119
            }
 
1120
 
 
1121
 
 
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.
 
1125
 
 
1126
            cx.lastIndex = 0;
 
1127
            if (cx.test(text)) {
 
1128
                text = text.replace(cx, function (a) {
 
1129
                    return '\\u' +
 
1130
                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
 
1131
                });
 
1132
            }
 
1133
 
 
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.
 
1138
 
 
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.
 
1146
 
 
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, ''))) {
 
1151
 
 
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.
 
1156
 
 
1157
                j = eval('(' + text + ')');
 
1158
 
 
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.
 
1161
 
 
1162
                return typeof reviver === 'function' ?
 
1163
                    walk({'': j}, '') : j;
 
1164
            }
 
1165
 
 
1166
// If the text is not JSON parseable, then a SyntaxError is thrown.
 
1167
 
 
1168
            throw new SyntaxError('JSON.parse');
 
1169
        };
 
1170
    }
 
1171
}());