chef-server-slice 0.7.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (158) hide show
  1. data/LICENSE +201 -0
  2. data/README.rdoc +135 -0
  3. data/app/controllers/application.rb +228 -0
  4. data/app/controllers/cookbook_attributes.rb +59 -0
  5. data/app/controllers/cookbook_definitions.rb +60 -0
  6. data/app/controllers/cookbook_files.rb +94 -0
  7. data/app/controllers/cookbook_libraries.rb +60 -0
  8. data/app/controllers/cookbook_recipes.rb +59 -0
  9. data/app/controllers/cookbook_templates.rb +80 -0
  10. data/app/controllers/cookbooks.rb +63 -0
  11. data/app/controllers/exceptions.rb +33 -0
  12. data/app/controllers/main.rb +7 -0
  13. data/app/controllers/nodes.rb +144 -0
  14. data/app/controllers/openid_consumer.rb +133 -0
  15. data/app/controllers/openid_register.rb +113 -0
  16. data/app/controllers/openid_server.rb +252 -0
  17. data/app/controllers/roles.rb +138 -0
  18. data/app/controllers/search.rb +58 -0
  19. data/app/controllers/search_entries.rb +73 -0
  20. data/app/controllers/status.rb +34 -0
  21. data/app/helpers/application_helper.rb +144 -0
  22. data/app/helpers/cookbook_attributes_helper.rb +7 -0
  23. data/app/helpers/cookbook_definitions_helper.rb +8 -0
  24. data/app/helpers/cookbook_files_helper.rb +8 -0
  25. data/app/helpers/cookbook_libraries_helper.rb +7 -0
  26. data/app/helpers/cookbook_recipes_helper.rb +8 -0
  27. data/app/helpers/cookbook_templates_helper.rb +8 -0
  28. data/app/helpers/cookbooks_helper.rb +31 -0
  29. data/app/helpers/exceptions_helper.rb +6 -0
  30. data/app/helpers/global_helpers.rb +39 -0
  31. data/app/helpers/nodes_helper.rb +33 -0
  32. data/app/helpers/openid_consumer_helper.rb +8 -0
  33. data/app/helpers/openid_register_helper.rb +8 -0
  34. data/app/helpers/openid_server_helper.rb +6 -0
  35. data/app/helpers/openid_server_helpers.rb +29 -0
  36. data/app/helpers/roles_helper.rb +5 -0
  37. data/app/helpers/search_entries_helper.rb +8 -0
  38. data/app/helpers/search_helper.rb +38 -0
  39. data/app/helpers/status_helper.rb +26 -0
  40. data/app/views/cookbook_templates/index.html.haml +7 -0
  41. data/app/views/cookbooks/index.html.haml +10 -0
  42. data/app/views/cookbooks/show.html.haml +40 -0
  43. data/app/views/exceptions/bad_request.json.erb +1 -0
  44. data/app/views/exceptions/internal_server_error.html.erb +216 -0
  45. data/app/views/exceptions/not_acceptable.html.erb +63 -0
  46. data/app/views/exceptions/not_found.html.erb +47 -0
  47. data/app/views/exceptions/standard_error.html.erb +217 -0
  48. data/app/views/layout/chef_server_slice.html.haml +53 -0
  49. data/app/views/layout/login.html.haml +37 -0
  50. data/app/views/main/index.html.erb +1 -0
  51. data/app/views/nodes/_action.html.haml +13 -0
  52. data/app/views/nodes/_form.html.haml +56 -0
  53. data/app/views/nodes/_navigation.html.haml +9 -0
  54. data/app/views/nodes/_resource.html.haml +22 -0
  55. data/app/views/nodes/edit.html.haml +7 -0
  56. data/app/views/nodes/index.html.haml +25 -0
  57. data/app/views/nodes/new.html.haml +6 -0
  58. data/app/views/nodes/show.html.haml +60 -0
  59. data/app/views/openid_consumer/index.html.haml +23 -0
  60. data/app/views/openid_consumer/start.html.haml +4 -0
  61. data/app/views/openid_login/index.html.haml +5 -0
  62. data/app/views/openid_register/index.html.haml +19 -0
  63. data/app/views/openid_register/show.html.haml +7 -0
  64. data/app/views/openid_server/decide.html.haml +27 -0
  65. data/app/views/roles/_form.html.haml +48 -0
  66. data/app/views/roles/_navigation.html.haml +9 -0
  67. data/app/views/roles/edit.html.haml +6 -0
  68. data/app/views/roles/index.html.haml +22 -0
  69. data/app/views/roles/new.html.haml +6 -0
  70. data/app/views/roles/show.html.haml +29 -0
  71. data/app/views/search/_search_form.html.haml +6 -0
  72. data/app/views/search/index.html.haml +9 -0
  73. data/app/views/search/show.html.haml +14 -0
  74. data/app/views/search_entries/index.html.haml +8 -0
  75. data/app/views/search_entries/show.html.haml +7 -0
  76. data/app/views/status/index.html.haml +88 -0
  77. data/config/init.rb +48 -0
  78. data/config/router.rb +6 -0
  79. data/lib/chef-server-slice.rb +150 -0
  80. data/lib/chef-server-slice/merbtasks.rb +103 -0
  81. data/lib/chef-server-slice/slicetasks.rb +20 -0
  82. data/lib/chef-server-slice/spectasks.rb +53 -0
  83. data/public/facebox/README.txt +4 -0
  84. data/public/facebox/b.png +0 -0
  85. data/public/facebox/bl.png +0 -0
  86. data/public/facebox/br.png +0 -0
  87. data/public/facebox/closelabel.gif +0 -0
  88. data/public/facebox/facebox.css +95 -0
  89. data/public/facebox/facebox.js +319 -0
  90. data/public/facebox/loading.gif +0 -0
  91. data/public/facebox/tl.png +0 -0
  92. data/public/facebox/tr.png +0 -0
  93. data/public/images/avatar.png +0 -0
  94. data/public/images/black_big.png +0 -0
  95. data/public/images/indicator.gif +0 -0
  96. data/public/images/merb.jpg +0 -0
  97. data/public/images/toggle-collapse-dark.png +0 -0
  98. data/public/images/toggle-collapse-light.png +0 -0
  99. data/public/images/toggle-collapse.gif +0 -0
  100. data/public/images/toggle-expand-dark.png +0 -0
  101. data/public/images/toggle-expand-light.png +0 -0
  102. data/public/images/toggle-expand.gif +0 -0
  103. data/public/images/treeBuilderImages/Thumbs.db +0 -0
  104. data/public/images/treeBuilderImages/doc.gif +0 -0
  105. data/public/images/treeBuilderImages/docNode.gif +0 -0
  106. data/public/images/treeBuilderImages/docNodeLast.gif +0 -0
  107. data/public/images/treeBuilderImages/docNodeLastFirst.gif +0 -0
  108. data/public/images/treeBuilderImages/folder.gif +0 -0
  109. data/public/images/treeBuilderImages/folderNode.gif +0 -0
  110. data/public/images/treeBuilderImages/folderNodeFirst.gif +0 -0
  111. data/public/images/treeBuilderImages/folderNodeLast.gif +0 -0
  112. data/public/images/treeBuilderImages/folderNodeLastFirst.gif +0 -0
  113. data/public/images/treeBuilderImages/folderNodeOpen.gif +0 -0
  114. data/public/images/treeBuilderImages/folderNodeOpenFirst.gif +0 -0
  115. data/public/images/treeBuilderImages/folderNodeOpenLast.gif +0 -0
  116. data/public/images/treeBuilderImages/folderNodeOpenLastFirst.gif +0 -0
  117. data/public/images/treeBuilderImages/folderOpen.gif +0 -0
  118. data/public/images/treeBuilderImages/vertLine.gif +0 -0
  119. data/public/javascripts/JSONeditor.js +1171 -0
  120. data/public/javascripts/chef.js +126 -0
  121. data/public/javascripts/jquery-1.3.2.min.js +19 -0
  122. data/public/javascripts/jquery-ui-1.7.1.custom.min.js +65 -0
  123. data/public/javascripts/jquery.editinline.js +108 -0
  124. data/public/javascripts/jquery.jeditable.mini.js +30 -0
  125. data/public/javascripts/jquery.livequery.js +250 -0
  126. data/public/javascripts/jquery.localscroll.js +104 -0
  127. data/public/javascripts/jquery.scrollTo.js +150 -0
  128. data/public/javascripts/jquery.tools.min.js +17 -0
  129. data/public/javascripts/jquery.treeTable.min.js +165 -0
  130. data/public/stylesheets/base.css +336 -0
  131. data/public/stylesheets/chef.css +157 -0
  132. data/public/stylesheets/images/ui-bg_diagonals-small_0_aaaaaa_40x40.png +0 -0
  133. data/public/stylesheets/images/ui-bg_diagonals-thick_15_444444_40x40.png +0 -0
  134. data/public/stylesheets/images/ui-bg_glass_100_f0f0f0_1x400.png +0 -0
  135. data/public/stylesheets/images/ui-bg_glass_50_99c2ff_1x400.png +0 -0
  136. data/public/stylesheets/images/ui-bg_glass_55_fbf5d0_1x400.png +0 -0
  137. data/public/stylesheets/images/ui-bg_glass_80_e6e6e6_1x400.png +0 -0
  138. data/public/stylesheets/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  139. data/public/stylesheets/images/ui-bg_highlight-hard_100_f9f9f9_1x100.png +0 -0
  140. data/public/stylesheets/images/ui-bg_highlight-soft_100_e7eef3_1x100.png +0 -0
  141. data/public/stylesheets/images/ui-icons_222222_256x240.png +0 -0
  142. data/public/stylesheets/images/ui-icons_2694e8_256x240.png +0 -0
  143. data/public/stylesheets/images/ui-icons_2e83ff_256x240.png +0 -0
  144. data/public/stylesheets/images/ui-icons_72a7cf_256x240.png +0 -0
  145. data/public/stylesheets/images/ui-icons_888888_256x240.png +0 -0
  146. data/public/stylesheets/images/ui-icons_cd0a0a_256x240.png +0 -0
  147. data/public/stylesheets/images/ui-icons_ffffff_256x240.png +0 -0
  148. data/public/stylesheets/jquery-ui-1.7.1.custom.css +404 -0
  149. data/public/stylesheets/jquery.treeTable.css +43 -0
  150. data/public/stylesheets/themes/bec-green/style.css +290 -0
  151. data/public/stylesheets/themes/bec/style.css +301 -0
  152. data/public/stylesheets/themes/blue/style.css +280 -0
  153. data/public/stylesheets/themes/default/style.css +267 -0
  154. data/public/stylesheets/themes/djime-cerulean/style.css +298 -0
  155. data/public/stylesheets/themes/kathleene/style.css +272 -0
  156. data/public/stylesheets/themes/orange/style.css +263 -0
  157. data/public/stylesheets/themes/reidb-greenish/style.css +301 -0
  158. metadata +341 -0
@@ -0,0 +1,319 @@
1
+ /*
2
+ * Facebox (for jQuery)
3
+ * version: 1.2 (05/05/2008)
4
+ * @requires jQuery v1.2 or later
5
+ *
6
+ * Examples at http://famspam.com/facebox/
7
+ *
8
+ * Licensed under the MIT:
9
+ * http://www.opensource.org/licenses/mit-license.php
10
+ *
11
+ * Copyright 2007, 2008 Chris Wanstrath [ chris@ozmm.org ]
12
+ *
13
+ * Usage:
14
+ *
15
+ * jQuery(document).ready(function() {
16
+ * jQuery('a[rel*=facebox]').facebox()
17
+ * })
18
+ *
19
+ * <a href="#terms" rel="facebox">Terms</a>
20
+ * Loads the #terms div in the box
21
+ *
22
+ * <a href="terms.html" rel="facebox">Terms</a>
23
+ * Loads the terms.html page in the box
24
+ *
25
+ * <a href="terms.png" rel="facebox">Terms</a>
26
+ * Loads the terms.png image in the box
27
+ *
28
+ *
29
+ * You can also use it programmatically:
30
+ *
31
+ * jQuery.facebox('some html')
32
+ *
33
+ * The above will open a facebox with "some html" as the content.
34
+ *
35
+ * jQuery.facebox(function($) {
36
+ * $.get('blah.html', function(data) { $.facebox(data) })
37
+ * })
38
+ *
39
+ * The above will show a loading screen before the passed function is called,
40
+ * allowing for a better ajaxy experience.
41
+ *
42
+ * The facebox function can also display an ajax page or image:
43
+ *
44
+ * jQuery.facebox({ ajax: 'remote.html' })
45
+ * jQuery.facebox({ image: 'dude.jpg' })
46
+ *
47
+ * Want to close the facebox? Trigger the 'close.facebox' document event:
48
+ *
49
+ * jQuery(document).trigger('close.facebox')
50
+ *
51
+ * Facebox also has a bunch of other hooks:
52
+ *
53
+ * loading.facebox
54
+ * beforeReveal.facebox
55
+ * reveal.facebox (aliased as 'afterReveal.facebox')
56
+ * init.facebox
57
+ *
58
+ * Simply bind a function to any of these hooks:
59
+ *
60
+ * $(document).bind('reveal.facebox', function() { ...stuff to do after the facebox and contents are revealed... })
61
+ *
62
+ */
63
+ (function($) {
64
+ $.facebox = function(data, klass) {
65
+ $.facebox.loading()
66
+
67
+ if (data.ajax) fillFaceboxFromAjax(data.ajax)
68
+ else if (data.image) fillFaceboxFromImage(data.image)
69
+ else if (data.div) fillFaceboxFromHref(data.div)
70
+ else if ($.isFunction(data)) data.call($)
71
+ else $.facebox.reveal(data, klass)
72
+ }
73
+
74
+ /*
75
+ * Public, $.facebox methods
76
+ */
77
+
78
+ $.extend($.facebox, {
79
+ settings: {
80
+ opacity : 0,
81
+ overlay : true,
82
+ loadingImage : '/facebox/loading.gif',
83
+ closeImage : '/facebox/closelabel.gif',
84
+ imageTypes : [ 'png', 'jpg', 'jpeg', 'gif' ],
85
+ faceboxHtml : '\
86
+ <div id="facebox" style="display:none;"> \
87
+ <div class="popup"> \
88
+ <table> \
89
+ <tbody> \
90
+ <tr> \
91
+ <td class="tl"/><td class="b"/><td class="tr"/> \
92
+ </tr> \
93
+ <tr> \
94
+ <td class="b"/> \
95
+ <td class="body"> \
96
+ <div class="content"> \
97
+ </div> \
98
+ <div class="footer"> \
99
+ <a href="#" class="close"> \
100
+ <img src="/facebox/closelabel.gif" title="close" class="close_image" /> \
101
+ </a> \
102
+ </div> \
103
+ </td> \
104
+ <td class="b"/> \
105
+ </tr> \
106
+ <tr> \
107
+ <td class="bl"/><td class="b"/><td class="br"/> \
108
+ </tr> \
109
+ </tbody> \
110
+ </table> \
111
+ </div> \
112
+ </div>'
113
+ },
114
+
115
+ loading: function() {
116
+ init()
117
+ if ($('#facebox .loading').length == 1) return true
118
+ showOverlay()
119
+
120
+ $('#facebox .content').empty()
121
+ $('#facebox .body').children().hide().end().
122
+ append('<div class="loading"><img src="'+$.facebox.settings.loadingImage+'"/></div>')
123
+
124
+ $('#facebox').css({
125
+ top: getPageScroll()[1] + (getPageHeight() / 10),
126
+ left: 385.5
127
+ }).show()
128
+
129
+ $(document).bind('keydown.facebox', function(e) {
130
+ if (e.keyCode == 27) $.facebox.close()
131
+ return true
132
+ })
133
+ $(document).trigger('loading.facebox')
134
+ },
135
+
136
+ reveal: function(data, klass) {
137
+ $(document).trigger('beforeReveal.facebox')
138
+ if (klass) $('#facebox .content').addClass(klass)
139
+ $('#facebox .content').append(data)
140
+ $('#facebox .loading').remove()
141
+ $('#facebox .body').children().fadeIn('normal')
142
+ $('#facebox').css('left', $(window).width() / 2 - ($('#facebox table').width() / 2))
143
+ $(document).trigger('reveal.facebox').trigger('afterReveal.facebox')
144
+ },
145
+
146
+ close: function() {
147
+ $(document).trigger('close.facebox')
148
+ return false
149
+ }
150
+ })
151
+
152
+ /*
153
+ * Public, $.fn methods
154
+ */
155
+
156
+ $.fn.facebox = function(settings) {
157
+ init(settings)
158
+
159
+ function clickHandler() {
160
+ $.facebox.loading(true)
161
+
162
+ // support for rel="facebox.inline_popup" syntax, to add a class
163
+ // also supports deprecated "facebox[.inline_popup]" syntax
164
+ var klass = this.rel.match(/facebox\[?\.(\w+)\]?/)
165
+ if (klass) klass = klass[1]
166
+
167
+ fillFaceboxFromHref(this.href, klass)
168
+ return false
169
+ }
170
+
171
+ return this.click(clickHandler)
172
+ }
173
+
174
+ /*
175
+ * Private methods
176
+ */
177
+
178
+ // called one time to setup facebox on this page
179
+ function init(settings) {
180
+ if ($.facebox.settings.inited) return true
181
+ else $.facebox.settings.inited = true
182
+
183
+ $(document).trigger('init.facebox')
184
+ makeCompatible()
185
+
186
+ var imageTypes = $.facebox.settings.imageTypes.join('|')
187
+ $.facebox.settings.imageTypesRegexp = new RegExp('\.' + imageTypes + '$', 'i')
188
+
189
+ if (settings) $.extend($.facebox.settings, settings)
190
+ $('body').append($.facebox.settings.faceboxHtml)
191
+
192
+ var preload = [ new Image(), new Image() ]
193
+ preload[0].src = $.facebox.settings.closeImage
194
+ preload[1].src = $.facebox.settings.loadingImage
195
+
196
+ $('#facebox').find('.b:first, .bl, .br, .tl, .tr').each(function() {
197
+ preload.push(new Image())
198
+ preload.slice(-1).src = $(this).css('background-image').replace(/url\((.+)\)/, '$1')
199
+ })
200
+
201
+ $('#facebox .close').click($.facebox.close)
202
+ $('#facebox .close_image').attr('src', $.facebox.settings.closeImage)
203
+ }
204
+
205
+ // getPageScroll() by quirksmode.com
206
+ function getPageScroll() {
207
+ var xScroll, yScroll;
208
+ if (self.pageYOffset) {
209
+ yScroll = self.pageYOffset;
210
+ xScroll = self.pageXOffset;
211
+ } else if (document.documentElement && document.documentElement.scrollTop) { // Explorer 6 Strict
212
+ yScroll = document.documentElement.scrollTop;
213
+ xScroll = document.documentElement.scrollLeft;
214
+ } else if (document.body) {// all other Explorers
215
+ yScroll = document.body.scrollTop;
216
+ xScroll = document.body.scrollLeft;
217
+ }
218
+ return new Array(xScroll,yScroll)
219
+ }
220
+
221
+ // Adapted from getPageSize() by quirksmode.com
222
+ function getPageHeight() {
223
+ var windowHeight
224
+ if (self.innerHeight) { // all except Explorer
225
+ windowHeight = self.innerHeight;
226
+ } else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
227
+ windowHeight = document.documentElement.clientHeight;
228
+ } else if (document.body) { // other Explorers
229
+ windowHeight = document.body.clientHeight;
230
+ }
231
+ return windowHeight
232
+ }
233
+
234
+ // Backwards compatibility
235
+ function makeCompatible() {
236
+ var $s = $.facebox.settings
237
+
238
+ $s.loadingImage = $s.loading_image || $s.loadingImage
239
+ $s.closeImage = $s.close_image || $s.closeImage
240
+ $s.imageTypes = $s.image_types || $s.imageTypes
241
+ $s.faceboxHtml = $s.facebox_html || $s.faceboxHtml
242
+ }
243
+
244
+ // Figures out what you want to display and displays it
245
+ // formats are:
246
+ // div: #id
247
+ // image: blah.extension
248
+ // ajax: anything else
249
+ function fillFaceboxFromHref(href, klass) {
250
+ // div
251
+ if (href.match(/#/)) {
252
+ var url = window.location.href.split('#')[0]
253
+ var target = href.replace(url,'')
254
+ $.facebox.reveal($(target).clone().show(), klass)
255
+
256
+ // image
257
+ } else if (href.match($.facebox.settings.imageTypesRegexp)) {
258
+ fillFaceboxFromImage(href, klass)
259
+ // ajax
260
+ } else {
261
+ fillFaceboxFromAjax(href, klass)
262
+ }
263
+ }
264
+
265
+ function fillFaceboxFromImage(href, klass) {
266
+ var image = new Image()
267
+ image.onload = function() {
268
+ $.facebox.reveal('<div class="image"><img src="' + image.src + '" /></div>', klass)
269
+ }
270
+ image.src = href
271
+ }
272
+
273
+ function fillFaceboxFromAjax(href, klass) {
274
+ $.get(href, function(data) { $.facebox.reveal(data, klass) })
275
+ }
276
+
277
+ function skipOverlay() {
278
+ return $.facebox.settings.overlay == false || $.facebox.settings.opacity === null
279
+ }
280
+
281
+ function showOverlay() {
282
+ if (skipOverlay()) return
283
+
284
+ if ($('facebox_overlay').length == 0)
285
+ $("body").append('<div id="facebox_overlay" class="facebox_hide"></div>')
286
+
287
+ $('#facebox_overlay').hide().addClass("facebox_overlayBG")
288
+ .css('opacity', $.facebox.settings.opacity)
289
+ .click(function() { $(document).trigger('close.facebox') })
290
+ .fadeIn(200)
291
+ return false
292
+ }
293
+
294
+ function hideOverlay() {
295
+ if (skipOverlay()) return
296
+
297
+ $('#facebox_overlay').fadeOut(200, function(){
298
+ $("#facebox_overlay").removeClass("facebox_overlayBG")
299
+ $("#facebox_overlay").addClass("facebox_hide")
300
+ $("#facebox_overlay").remove()
301
+ })
302
+
303
+ return false
304
+ }
305
+
306
+ /*
307
+ * Bindings
308
+ */
309
+
310
+ $(document).bind('close.facebox', function() {
311
+ $(document).unbind('keydown.facebox')
312
+ $('#facebox').fadeOut(function() {
313
+ $('#facebox .content').removeClass().addClass('content')
314
+ hideOverlay()
315
+ $('#facebox .loading').remove()
316
+ })
317
+ })
318
+
319
+ })(jQuery);
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,1171 @@
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
+ }());