bee_api 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. data/bin/bee_api +84 -0
  2. data/lib/mdpreview.rb +80 -0
  3. data/lib/mdpreview/translator.rb +60 -0
  4. data/lib/mdpreview/version.rb +3 -0
  5. data/test/mdptest.rb +100 -0
  6. data/vendor/HISTORY.md +237 -0
  7. data/vendor/Jakefile.js +316 -0
  8. data/vendor/LICENSE +176 -0
  9. data/vendor/NOTICE +17 -0
  10. data/vendor/README.md +102 -0
  11. data/vendor/app/chrome/documentation.txt +12 -0
  12. data/vendor/app/chrome/manifest.json +24 -0
  13. data/vendor/app/web/ajax.js +43 -0
  14. data/vendor/app/web/app.css +292 -0
  15. data/vendor/app/web/app.js +377 -0
  16. data/vendor/app/web/beta/index.html +17 -0
  17. data/vendor/app/web/datapolicy.txt +48 -0
  18. data/vendor/app/web/doc/doc.css +60 -0
  19. data/vendor/app/web/doc/img/actions_menu.png +0 -0
  20. data/vendor/app/web/doc/img/button_actions_menu.png +0 -0
  21. data/vendor/app/web/doc/img/button_dragarea.png +0 -0
  22. data/vendor/app/web/doc/img/jsoneditor.png +0 -0
  23. data/vendor/app/web/doc/img/jsonformatter.png +0 -0
  24. data/vendor/app/web/doc/img/main_menu.png +0 -0
  25. data/vendor/app/web/doc/img/splitter.png +0 -0
  26. data/vendor/app/web/doc/index.html +201 -0
  27. data/vendor/app/web/favicon.ico +0 -0
  28. data/vendor/app/web/fileretriever.css +54 -0
  29. data/vendor/app/web/fileretriever.js +567 -0
  30. data/vendor/app/web/fileretriever.php +120 -0
  31. data/vendor/app/web/googlea47c4a0b36d11021.html +1 -0
  32. data/vendor/app/web/hash.js +133 -0
  33. data/vendor/app/web/img/description.txt +20 -0
  34. data/vendor/app/web/img/header_background.png +0 -0
  35. data/vendor/app/web/img/icon_128.png +0 -0
  36. data/vendor/app/web/img/icon_16.png +0 -0
  37. data/vendor/app/web/img/icon_gray.svg +151 -0
  38. data/vendor/app/web/img/icon_gray_16.svg +150 -0
  39. data/vendor/app/web/img/icon_orange.svg +151 -0
  40. data/vendor/app/web/img/logo.png +0 -0
  41. data/vendor/app/web/img/logo.xcf +0 -0
  42. data/vendor/app/web/img/logo_app.png +0 -0
  43. data/vendor/app/web/img/logo_app.xcf +0 -0
  44. data/vendor/app/web/index.html +191 -0
  45. data/vendor/app/web/notify.js +150 -0
  46. data/vendor/app/web/queryparams.js +71 -0
  47. data/vendor/app/web/robots.txt +0 -0
  48. data/vendor/app/web/splitter.js +179 -0
  49. data/vendor/app/web/test.html +224 -0
  50. data/vendor/component.json +33 -0
  51. data/vendor/docs/api.md +188 -0
  52. data/vendor/docs/usage.md +137 -0
  53. data/vendor/examples/01_basic_usage.html +45 -0
  54. data/vendor/examples/02_viewer.html +38 -0
  55. data/vendor/examples/03_switch_mode.html +98 -0
  56. data/vendor/examples/cur.file +1 -0
  57. data/vendor/examples/jquery.js +2 -0
  58. data/vendor/examples/meta.js +1 -0
  59. data/vendor/examples/requirejs_demo/requirejs_demo.html +19 -0
  60. data/vendor/examples/requirejs_demo/scripts/main.js +25 -0
  61. data/vendor/examples/requirejs_demo/scripts/require.js +35 -0
  62. data/vendor/img/jsoneditor-icons.png +0 -0
  63. data/vendor/jsoneditor-min.css +1 -0
  64. data/vendor/jsoneditor-min.js +34 -0
  65. data/vendor/jsoneditor.css +597 -0
  66. data/vendor/jsoneditor.js +6069 -0
  67. data/vendor/jsoneditor/css/contextmenu.css +219 -0
  68. data/vendor/jsoneditor/css/img/description.txt +13 -0
  69. data/vendor/jsoneditor/css/img/export.sh +16 -0
  70. data/vendor/jsoneditor/css/img/jsoneditor-icons.png +0 -0
  71. data/vendor/jsoneditor/css/img/jsoneditor-icons.svg +861 -0
  72. data/vendor/jsoneditor/css/jsoneditor.css +220 -0
  73. data/vendor/jsoneditor/css/menu.css +81 -0
  74. data/vendor/jsoneditor/css/searchbox.css +73 -0
  75. data/vendor/jsoneditor/js/appendnode.js +211 -0
  76. data/vendor/jsoneditor/js/contextmenu.js +440 -0
  77. data/vendor/jsoneditor/js/header.js +32 -0
  78. data/vendor/jsoneditor/js/highlighter.js +82 -0
  79. data/vendor/jsoneditor/js/history.js +218 -0
  80. data/vendor/jsoneditor/js/jsoneditor.js +206 -0
  81. data/vendor/jsoneditor/js/module.js +50 -0
  82. data/vendor/jsoneditor/js/node.js +2864 -0
  83. data/vendor/jsoneditor/js/searchbox.js +288 -0
  84. data/vendor/jsoneditor/js/texteditor.js +311 -0
  85. data/vendor/jsoneditor/js/treeeditor.js +770 -0
  86. data/vendor/jsoneditor/js/util.js +582 -0
  87. data/vendor/lib/ace/ace.js +11 -0
  88. data/vendor/lib/ace/mode-json.js +1 -0
  89. data/vendor/lib/ace/theme-jsoneditor.js +144 -0
  90. data/vendor/lib/ace/theme-textmate.js +163 -0
  91. data/vendor/lib/ace/worker-json.js +1 -0
  92. data/vendor/lib/jsonlint/README.md +62 -0
  93. data/vendor/lib/jsonlint/jsonlint.js +432 -0
  94. data/vendor/misc/screenshots/actionsmenu_640x400.png +0 -0
  95. data/vendor/misc/screenshots/codeeditor_640x400.png +0 -0
  96. data/vendor/misc/screenshots/description.json +17 -0
  97. data/vendor/misc/screenshots/jsoneditoronline.png +0 -0
  98. data/vendor/misc/screenshots/jsoneditoronline_640x400.png +0 -0
  99. data/vendor/misc/screenshots/search_640x400.png +0 -0
  100. data/vendor/misc/screenshots/small_tile.xcf +0 -0
  101. data/vendor/misc/screenshots/small_tile_440x280.png +0 -0
  102. data/vendor/misc/todo.txt +101 -0
  103. data/vendor/package.json +28 -0
  104. data/vendor/test/couchdbeditor.html +100 -0
  105. data/vendor/test/largefile.json +12605 -0
  106. data/vendor/test/test_ace.html +60 -0
  107. data/vendor/test/test_editable_div.html +449 -0
  108. metadata +154 -0
@@ -0,0 +1,770 @@
1
+ /**
2
+ * @constructor TreeEditor
3
+ * @param {Element} container Container element
4
+ * @param {Object} [options] Object with options. available options:
5
+ * {String} mode Editor mode. Available values:
6
+ * 'tree' (default), 'view',
7
+ * and 'form'.
8
+ * {Boolean} search Enable search box.
9
+ * True by default
10
+ * {Boolean} history Enable history (undo/redo).
11
+ * True by default
12
+ * {function} change Callback method, triggered
13
+ * on change of contents
14
+ * {String} name Field name for the root node.
15
+ * @param {Object | undefined} json JSON object
16
+ */
17
+ function TreeEditor(container, options, json) {
18
+ if (!(this instanceof TreeEditor)) {
19
+ throw new Error('TreeEditor constructor called without "new".');
20
+ }
21
+
22
+ this._create(container, options, json);
23
+ }
24
+
25
+ /**
26
+ * Create the TreeEditor
27
+ * @param {Element} container Container element
28
+ * @param {Object} [options] See description in constructor
29
+ * @param {Object | undefined} json JSON object
30
+ * @private
31
+ */
32
+ TreeEditor.prototype._create = function (container, options, json) {
33
+ // check availability of JSON parser (not available in IE7 and older)
34
+ if (typeof(JSON) == 'undefined') {
35
+ throw new Error ('Your browser does not support JSON. \n\n' +
36
+ 'Please install the newest version of your browser.\n' +
37
+ '(all modern browsers support JSON).');
38
+ }
39
+
40
+ if (!container) {
41
+ throw new Error('No container element provided.');
42
+ }
43
+ this.container = container;
44
+ this.dom = {};
45
+ this.highlighter = new Highlighter();
46
+ this.selection = undefined; // will hold the last input selection
47
+
48
+ this._setOptions(options);
49
+
50
+ if (this.options.history && !this.mode.view) {
51
+ this.history = new History(this);
52
+ }
53
+
54
+ this._createFrame();
55
+ this._createTable();
56
+
57
+ this.set(json || {});
58
+ };
59
+
60
+ /**
61
+ * Detach the editor from the DOM
62
+ * @private
63
+ */
64
+ TreeEditor.prototype._delete = function () {
65
+ if (this.frame && this.container && this.frame.parentNode == this.container) {
66
+ this.container.removeChild(this.frame);
67
+ }
68
+ };
69
+
70
+ /**
71
+ * Initialize and set default options
72
+ * @param {Object} [options] See description in constructor
73
+ * @private
74
+ */
75
+ TreeEditor.prototype._setOptions = function (options) {
76
+ this.options = {
77
+ search: true,
78
+ history: true,
79
+ mode: 'tree',
80
+ name: undefined // field name of root node
81
+ };
82
+
83
+ // copy all options
84
+ if (options) {
85
+ for (var prop in options) {
86
+ if (options.hasOwnProperty(prop)) {
87
+ this.options[prop] = options[prop];
88
+ }
89
+ }
90
+
91
+ // check for deprecated options
92
+ if (options['enableSearch']) {
93
+ // deprecated since version 1.6.0, 2012-11-03
94
+ this.options.search = options['enableSearch'];
95
+ util.log('WARNING: Option "enableSearch" is deprecated. Use "search" instead.');
96
+ }
97
+ if (options['enableHistory']) {
98
+ // deprecated since version 1.6.0, 2012-11-03
99
+ this.options.history = options['enableHistory'];
100
+ util.log('WARNING: Option "enableHistory" is deprecated. Use "history" instead.');
101
+ }
102
+ if (options['mode'] == 'editor') {
103
+ // deprecated since version 2.2.0, 2013-04-30
104
+ this.options.mode = 'tree';
105
+ util.log('WARNING: Mode "editor" is deprecated. Use "tree" instead.');
106
+ }
107
+ if (options['mode'] == 'viewer') {
108
+ // deprecated since version 2.2.0, 2013-04-30
109
+ this.options.mode = 'view';
110
+ util.log('WARNING: Mode "viewer" is deprecated. Use "view" instead.');
111
+ }
112
+ }
113
+
114
+ // interpret the mode options
115
+ this.mode = {
116
+ edit: (this.options.mode != 'view' && this.options.mode != 'form'),
117
+ view: (this.options.mode == 'view'),
118
+ form: (this.options.mode == 'form')
119
+ };
120
+ };
121
+
122
+ // node currently being edited
123
+ TreeEditor.focusNode = undefined;
124
+
125
+ /**
126
+ * Set JSON object in editor
127
+ * @param {Object | undefined} json JSON data
128
+ * @param {String} [name] Optional field name for the root node.
129
+ * Can also be set using setName(name).
130
+ */
131
+ TreeEditor.prototype.set = function (json, name) {
132
+ // adjust field name for root node
133
+ if (name) {
134
+ // TODO: deprecated since version 2.2.0. Cleanup some day.
135
+ util.log('Warning: second parameter "name" is deprecated. ' +
136
+ 'Use setName(name) instead.');
137
+ this.options.name = name;
138
+ }
139
+
140
+ // verify if json is valid JSON, ignore when a function
141
+ if (json instanceof Function || (json === undefined)) {
142
+ this.clear();
143
+ }
144
+ else {
145
+ this.content.removeChild(this.table); // Take the table offline
146
+
147
+ // replace the root node
148
+ var params = {
149
+ 'field': this.options.name,
150
+ 'value': json
151
+ };
152
+ var node = new Node(this, params);
153
+ this._setRoot(node);
154
+
155
+ // expand
156
+ var recurse = false;
157
+ this.node.expand(recurse);
158
+
159
+ this.content.appendChild(this.table); // Put the table online again
160
+ }
161
+
162
+ // TODO: maintain history, store last state and previous document
163
+ if (this.history) {
164
+ this.history.clear();
165
+ }
166
+ };
167
+
168
+ /**
169
+ * Get JSON object from editor
170
+ * @return {Object | undefined} json
171
+ */
172
+ TreeEditor.prototype.get = function () {
173
+ // remove focus from currently edited node
174
+ if (TreeEditor.focusNode) {
175
+ TreeEditor.focusNode.blur();
176
+ }
177
+
178
+ if (this.node) {
179
+ return this.node.getValue();
180
+ }
181
+ else {
182
+ return undefined;
183
+ }
184
+ };
185
+
186
+ /**
187
+ * Get the text contents of the TreeEditor
188
+ * @return {String} jsonText
189
+ */
190
+ TreeEditor.prototype.getText = function() {
191
+ return JSON.stringify(this.get());
192
+ };
193
+
194
+ /**
195
+ * Set the text contents of the TreeEditor
196
+ * @param {String} jsonText
197
+ */
198
+ TreeEditor.prototype.setText = function(jsonText) {
199
+ this.set(util.parse(jsonText));
200
+ };
201
+
202
+ /**
203
+ * Set a field name for the root node.
204
+ * @param {String | undefined} name
205
+ */
206
+ TreeEditor.prototype.setName = function (name) {
207
+ this.options.name = name;
208
+ if (this.node) {
209
+ this.node.updateField(this.options.name);
210
+ }
211
+ };
212
+
213
+ /**
214
+ * Get the field name for the root node.
215
+ * @return {String | undefined} name
216
+ */
217
+ TreeEditor.prototype.getName = function () {
218
+ return this.options.name;
219
+ };
220
+
221
+ /**
222
+ * Remove the root node from the editor
223
+ */
224
+ TreeEditor.prototype.clear = function () {
225
+ if (this.node) {
226
+ this.node.collapse();
227
+ this.tbody.removeChild(this.node.getDom());
228
+ delete this.node;
229
+ }
230
+ };
231
+
232
+ /**
233
+ * Set the root node for the json editor
234
+ * @param {Node} node
235
+ * @private
236
+ */
237
+ TreeEditor.prototype._setRoot = function (node) {
238
+ this.clear();
239
+
240
+ this.node = node;
241
+
242
+ // append to the dom
243
+ this.tbody.appendChild(node.getDom());
244
+ };
245
+
246
+ /**
247
+ * Search text in all nodes
248
+ * The nodes will be expanded when the text is found one of its childs,
249
+ * else it will be collapsed. Searches are case insensitive.
250
+ * @param {String} text
251
+ * @return {Object[]} results Array with nodes containing the search results
252
+ * The result objects contains fields:
253
+ * - {Node} node,
254
+ * - {String} elem the dom element name where
255
+ * the result is found ('field' or
256
+ * 'value')
257
+ */
258
+ TreeEditor.prototype.search = function (text) {
259
+ var results;
260
+ if (this.node) {
261
+ this.content.removeChild(this.table); // Take the table offline
262
+ results = this.node.search(text);
263
+ this.content.appendChild(this.table); // Put the table online again
264
+ }
265
+ else {
266
+ results = [];
267
+ }
268
+
269
+ return results;
270
+ };
271
+
272
+ /**
273
+ * Expand all nodes
274
+ */
275
+ TreeEditor.prototype.expandAll = function () {
276
+ if (this.node) {
277
+ this.content.removeChild(this.table); // Take the table offline
278
+ this.node.expand();
279
+ this.content.appendChild(this.table); // Put the table online again
280
+ }
281
+ };
282
+
283
+ /**
284
+ * Collapse all nodes
285
+ */
286
+ TreeEditor.prototype.collapseAll = function () {
287
+ if (this.node) {
288
+ this.content.removeChild(this.table); // Take the table offline
289
+ this.node.collapse();
290
+ this.content.appendChild(this.table); // Put the table online again
291
+ }
292
+ };
293
+
294
+ /**
295
+ * The method onChange is called whenever a field or value is changed, created,
296
+ * deleted, duplicated, etc.
297
+ * @param {String} action Change action. Available values: "editField",
298
+ * "editValue", "changeType", "appendNode",
299
+ * "removeNode", "duplicateNode", "moveNode", "expand",
300
+ * "collapse".
301
+ * @param {Object} params Object containing parameters describing the change.
302
+ * The parameters in params depend on the action (for
303
+ * example for "editValue" the Node, old value, and new
304
+ * value are provided). params contains all information
305
+ * needed to undo or redo the action.
306
+ * @private
307
+ */
308
+ TreeEditor.prototype._onAction = function (action, params) {
309
+ // add an action to the history
310
+ if (this.history) {
311
+ this.history.add(action, params);
312
+ }
313
+
314
+ // trigger the onChange callback
315
+ if (this.options.change) {
316
+ try {
317
+ this.options.change();
318
+ }
319
+ catch (err) {
320
+ util.log('Error in change callback: ', err);
321
+ }
322
+ }
323
+ };
324
+
325
+ /**
326
+ * Start autoscrolling when given mouse position is above the top of the
327
+ * editor contents, or below the bottom.
328
+ * @param {Number} mouseY Absolute mouse position in pixels
329
+ */
330
+ TreeEditor.prototype.startAutoScroll = function (mouseY) {
331
+ var me = this;
332
+ var content = this.content;
333
+ var top = util.getAbsoluteTop(content);
334
+ var height = content.clientHeight;
335
+ var bottom = top + height;
336
+ var margin = 24;
337
+ var interval = 50; // ms
338
+
339
+ if ((mouseY < top + margin) && content.scrollTop > 0) {
340
+ this.autoScrollStep = ((top + margin) - mouseY) / 3;
341
+ }
342
+ else if (mouseY > bottom - margin &&
343
+ height + content.scrollTop < content.scrollHeight) {
344
+ this.autoScrollStep = ((bottom - margin) - mouseY) / 3;
345
+ }
346
+ else {
347
+ this.autoScrollStep = undefined;
348
+ }
349
+
350
+ if (this.autoScrollStep) {
351
+ if (!this.autoScrollTimer) {
352
+ this.autoScrollTimer = setInterval(function () {
353
+ if (me.autoScrollStep) {
354
+ content.scrollTop -= me.autoScrollStep;
355
+ }
356
+ else {
357
+ me.stopAutoScroll();
358
+ }
359
+ }, interval);
360
+ }
361
+ }
362
+ else {
363
+ this.stopAutoScroll();
364
+ }
365
+ };
366
+
367
+ /**
368
+ * Stop auto scrolling. Only applicable when scrolling
369
+ */
370
+ TreeEditor.prototype.stopAutoScroll = function () {
371
+ if (this.autoScrollTimer) {
372
+ clearTimeout(this.autoScrollTimer);
373
+ delete this.autoScrollTimer;
374
+ }
375
+ if (this.autoScrollStep) {
376
+ delete this.autoScrollStep;
377
+ }
378
+ };
379
+
380
+
381
+ /**
382
+ * Set the focus to an element in the TreeEditor, set text selection, and
383
+ * set scroll position.
384
+ * @param {Object} selection An object containing fields:
385
+ * {Element | undefined} dom The dom element
386
+ * which has focus
387
+ * {Range | TextRange} range A text selection
388
+ * {Number} scrollTop Scroll position
389
+ */
390
+ TreeEditor.prototype.setSelection = function (selection) {
391
+ if (!selection) {
392
+ return;
393
+ }
394
+
395
+ if ('scrollTop' in selection && this.content) {
396
+ // TODO: animated scroll
397
+ this.content.scrollTop = selection.scrollTop;
398
+ }
399
+ if (selection.range) {
400
+ util.setSelectionOffset(selection.range);
401
+ }
402
+ if (selection.dom) {
403
+ selection.dom.focus();
404
+ }
405
+ };
406
+
407
+ /**
408
+ * Get the current focus
409
+ * @return {Object} selection An object containing fields:
410
+ * {Element | undefined} dom The dom element
411
+ * which has focus
412
+ * {Range | TextRange} range A text selection
413
+ * {Number} scrollTop Scroll position
414
+ */
415
+ TreeEditor.prototype.getSelection = function () {
416
+ return {
417
+ dom: TreeEditor.domFocus,
418
+ scrollTop: this.content ? this.content.scrollTop : 0,
419
+ range: util.getSelectionOffset()
420
+ };
421
+ };
422
+
423
+ /**
424
+ * Adjust the scroll position such that given top position is shown at 1/4
425
+ * of the window height.
426
+ * @param {Number} top
427
+ * @param {function(boolean)} [callback] Callback, executed when animation is
428
+ * finished. The callback returns true
429
+ * when animation is finished, or false
430
+ * when not.
431
+ */
432
+ TreeEditor.prototype.scrollTo = function (top, callback) {
433
+ var content = this.content;
434
+ if (content) {
435
+ var editor = this;
436
+ // cancel any running animation
437
+ if (editor.animateTimeout) {
438
+ clearTimeout(editor.animateTimeout);
439
+ delete editor.animateTimeout;
440
+ }
441
+ if (editor.animateCallback) {
442
+ editor.animateCallback(false);
443
+ delete editor.animateCallback;
444
+ }
445
+
446
+ // calculate final scroll position
447
+ var height = content.clientHeight;
448
+ var bottom = content.scrollHeight - height;
449
+ var finalScrollTop = Math.min(Math.max(top - height / 4, 0), bottom);
450
+
451
+ // animate towards the new scroll position
452
+ var animate = function () {
453
+ var scrollTop = content.scrollTop;
454
+ var diff = (finalScrollTop - scrollTop);
455
+ if (Math.abs(diff) > 3) {
456
+ content.scrollTop += diff / 3;
457
+ editor.animateCallback = callback;
458
+ editor.animateTimeout = setTimeout(animate, 50);
459
+ }
460
+ else {
461
+ // finished
462
+ if (callback) {
463
+ callback(true);
464
+ }
465
+ content.scrollTop = finalScrollTop;
466
+ delete editor.animateTimeout;
467
+ delete editor.animateCallback;
468
+ }
469
+ };
470
+ animate();
471
+ }
472
+ else {
473
+ if (callback) {
474
+ callback(false);
475
+ }
476
+ }
477
+ };
478
+
479
+ /**
480
+ * Create main frame
481
+ * @private
482
+ */
483
+ TreeEditor.prototype._createFrame = function () {
484
+ // create the frame
485
+ this.frame = document.createElement('div');
486
+ this.frame.className = 'jsoneditor';
487
+ this.container.appendChild(this.frame);
488
+
489
+ // create one global event listener to handle all events from all nodes
490
+ var editor = this;
491
+ var onEvent = function (event) {
492
+ editor._onEvent(event);
493
+ };
494
+ this.frame.onclick = function (event) {
495
+ event = event || window.event;
496
+ var target = event.target || event.srcElement;
497
+
498
+ onEvent(event);
499
+
500
+ // prevent default submit action of buttons when TreeEditor is located
501
+ // inside a form
502
+ if (target.nodeName == 'BUTTON') {
503
+ util.preventDefault(event);
504
+ }
505
+ };
506
+ this.frame.oninput = onEvent;
507
+ this.frame.onchange = onEvent;
508
+ this.frame.onkeydown = onEvent;
509
+ this.frame.onkeyup = onEvent;
510
+ this.frame.oncut = onEvent;
511
+ this.frame.onpaste = onEvent;
512
+ this.frame.onmousedown = onEvent;
513
+ this.frame.onmouseup = onEvent;
514
+ this.frame.onmouseover = onEvent;
515
+ this.frame.onmouseout = onEvent;
516
+ // Note: focus and blur events do not propagate, therefore they defined
517
+ // using an eventListener with useCapture=true
518
+ // see http://www.quirksmode.org/blog/archives/2008/04/delegating_the.html
519
+ util.addEventListener(this.frame, 'focus', onEvent, true);
520
+ util.addEventListener(this.frame, 'blur', onEvent, true);
521
+ this.frame.onfocusin = onEvent; // for IE
522
+ this.frame.onfocusout = onEvent; // for IE
523
+
524
+ // create menu
525
+ this.menu = document.createElement('div');
526
+ this.menu.className = 'menu';
527
+ this.frame.appendChild(this.menu);
528
+
529
+ // create expand all button
530
+ var expandAll = document.createElement('button');
531
+ expandAll.className = 'expand-all';
532
+ expandAll.title = 'Expand all fields';
533
+ expandAll.onclick = function () {
534
+ editor.expandAll();
535
+ };
536
+ this.menu.appendChild(expandAll);
537
+
538
+ // create expand all button
539
+ var collapseAll = document.createElement('button');
540
+ collapseAll.title = 'Collapse all fields';
541
+ collapseAll.className = 'collapse-all';
542
+ collapseAll.onclick = function () {
543
+ editor.collapseAll();
544
+ };
545
+ this.menu.appendChild(collapseAll);
546
+
547
+ // create undo/redo buttons
548
+ if (this.history) {
549
+ // create separator
550
+ var separator = document.createElement('span');
551
+ separator.innerHTML = '&nbsp;';
552
+ this.menu.appendChild(separator);
553
+
554
+ // create undo button
555
+ var undo = document.createElement('button');
556
+ undo.className = 'undo';
557
+ undo.title = 'Undo last action (Ctrl+Z)';
558
+ undo.onclick = function () {
559
+ editor._onUndo();
560
+ };
561
+ this.menu.appendChild(undo);
562
+ this.dom.undo = undo;
563
+
564
+ // create redo button
565
+ var redo = document.createElement('button');
566
+ redo.className = 'redo';
567
+ redo.title = 'Redo (Ctrl+Shift+Z)';
568
+ redo.onclick = function () {
569
+ editor._onRedo();
570
+ };
571
+ this.menu.appendChild(redo);
572
+ this.dom.redo = redo;
573
+
574
+ // register handler for onchange of history
575
+ this.history.onChange = function () {
576
+ undo.disabled = !editor.history.canUndo();
577
+ redo.disabled = !editor.history.canRedo();
578
+ };
579
+ this.history.onChange();
580
+ }
581
+
582
+ // create search box
583
+ if (this.options.search) {
584
+ this.searchBox = new SearchBox(this, this.menu);
585
+ }
586
+ };
587
+
588
+ /**
589
+ * Perform an undo action
590
+ * @private
591
+ */
592
+ TreeEditor.prototype._onUndo = function () {
593
+ if (this.history) {
594
+ // undo last action
595
+ this.history.undo();
596
+
597
+ // trigger change callback
598
+ if (this.options.change) {
599
+ this.options.change();
600
+ }
601
+ }
602
+ };
603
+
604
+ /**
605
+ * Perform a redo action
606
+ * @private
607
+ */
608
+ TreeEditor.prototype._onRedo = function () {
609
+ if (this.history) {
610
+ // redo last action
611
+ this.history.redo();
612
+
613
+ // trigger change callback
614
+ if (this.options.change) {
615
+ this.options.change();
616
+ }
617
+ }
618
+ };
619
+
620
+ /**
621
+ * Event handler
622
+ * @param event
623
+ * @private
624
+ */
625
+ TreeEditor.prototype._onEvent = function (event) {
626
+ event = event || window.event;
627
+ var target = event.target || event.srcElement;
628
+
629
+ if (event.type == 'keydown') {
630
+ this._onKeyDown(event);
631
+ }
632
+
633
+ if (event.type == 'focus') {
634
+ TreeEditor.domFocus = target;
635
+ }
636
+
637
+ var node = Node.getNodeFromTarget(target);
638
+ if (node) {
639
+ node.onEvent(event);
640
+ }
641
+ };
642
+
643
+ /**
644
+ * Event handler for keydown. Handles shortcut keys
645
+ * @param {Event} event
646
+ * @private
647
+ */
648
+ TreeEditor.prototype._onKeyDown = function (event) {
649
+ var keynum = event.which || event.keyCode;
650
+ var ctrlKey = event.ctrlKey;
651
+ var shiftKey = event.shiftKey;
652
+ var handled = false;
653
+
654
+ if (keynum == 9) { // Tab or Shift+Tab
655
+ // FIXME: selecting all text on tab key does not work on IE8 (-> put selectContentEditable() in keyup too?)
656
+ //Node.select(TreeEditor.domFocus);
657
+ setTimeout(function () {
658
+ // select all text when moving focus to an editable div
659
+ util.selectContentEditable(TreeEditor.domFocus);
660
+ }, 0);
661
+ }
662
+
663
+ if (this.searchBox) {
664
+ if (ctrlKey && keynum == 70) { // Ctrl+F
665
+ this.searchBox.dom.search.focus();
666
+ this.searchBox.dom.search.select();
667
+ handled = true;
668
+ }
669
+ else if (keynum == 114 || (ctrlKey && keynum == 71)) { // F3 or Ctrl+G
670
+ var focus = true;
671
+ if (!shiftKey) {
672
+ // select next search result (F3 or Ctrl+G)
673
+ this.searchBox.next(focus);
674
+ }
675
+ else {
676
+ // select previous search result (Shift+F3 or Ctrl+Shift+G)
677
+ this.searchBox.previous(focus);
678
+ }
679
+
680
+ handled = true;
681
+ }
682
+ }
683
+
684
+ if (this.history) {
685
+ if (ctrlKey && !shiftKey && keynum == 90) { // Ctrl+Z
686
+ // undo
687
+ this._onUndo();
688
+ handled = true;
689
+ }
690
+ else if (ctrlKey && shiftKey && keynum == 90) { // Ctrl+Shift+Z
691
+ // redo
692
+ this._onRedo();
693
+ handled = true;
694
+ }
695
+ }
696
+
697
+ if (handled) {
698
+ util.preventDefault(event);
699
+ util.stopPropagation(event);
700
+ }
701
+ };
702
+
703
+ /**
704
+ * Create main table
705
+ * @private
706
+ */
707
+ TreeEditor.prototype._createTable = function () {
708
+ var contentOuter = document.createElement('div');
709
+ contentOuter.className = 'outer';
710
+ this.contentOuter = contentOuter;
711
+
712
+ this.content = document.createElement('div');
713
+ this.content.className = 'content';
714
+ contentOuter.appendChild(this.content);
715
+
716
+ this.table = document.createElement('table');
717
+ this.table.className = 'content';
718
+ this.content.appendChild(this.table);
719
+
720
+ // IE8 does not handle overflow='auto' correctly.
721
+ // Therefore, set overflow to 'scroll'
722
+ var ieVersion = util.getInternetExplorerVersion();
723
+ if (ieVersion == 8) {
724
+ this.content.style.overflow = 'scroll';
725
+ }
726
+
727
+ // create colgroup where the first two columns don't have a fixed
728
+ // width, and the edit columns do have a fixed width
729
+ var col;
730
+ this.colgroupContent = document.createElement('colgroup');
731
+ if (this.mode.edit) {
732
+ col = document.createElement('col');
733
+ col.width = "24px";
734
+ this.colgroupContent.appendChild(col);
735
+ }
736
+ col = document.createElement('col');
737
+ col.width = "24px";
738
+ this.colgroupContent.appendChild(col);
739
+ col = document.createElement('col');
740
+ this.colgroupContent.appendChild(col);
741
+ this.table.appendChild(this.colgroupContent);
742
+
743
+ this.tbody = document.createElement('tbody');
744
+ this.table.appendChild(this.tbody);
745
+
746
+ this.frame.appendChild(contentOuter);
747
+ };
748
+
749
+ // register modes at the JSONEditor
750
+ JSONEditor.modes.tree = {
751
+ editor: TreeEditor,
752
+ data: 'json'
753
+ };
754
+ JSONEditor.modes.view = {
755
+ editor: TreeEditor,
756
+ data: 'json'
757
+ };
758
+ JSONEditor.modes.form = {
759
+ editor: TreeEditor,
760
+ data: 'json'
761
+ };
762
+ // Deprecated modes (deprecated since version 2.2.0)
763
+ JSONEditor.modes.editor = {
764
+ editor: TreeEditor,
765
+ data: 'json'
766
+ };
767
+ JSONEditor.modes.viewer = {
768
+ editor: TreeEditor,
769
+ data: 'json'
770
+ };