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.
- data/bin/bee_api +84 -0
- data/lib/mdpreview.rb +80 -0
- data/lib/mdpreview/translator.rb +60 -0
- data/lib/mdpreview/version.rb +3 -0
- data/test/mdptest.rb +100 -0
- data/vendor/HISTORY.md +237 -0
- data/vendor/Jakefile.js +316 -0
- data/vendor/LICENSE +176 -0
- data/vendor/NOTICE +17 -0
- data/vendor/README.md +102 -0
- data/vendor/app/chrome/documentation.txt +12 -0
- data/vendor/app/chrome/manifest.json +24 -0
- data/vendor/app/web/ajax.js +43 -0
- data/vendor/app/web/app.css +292 -0
- data/vendor/app/web/app.js +377 -0
- data/vendor/app/web/beta/index.html +17 -0
- data/vendor/app/web/datapolicy.txt +48 -0
- data/vendor/app/web/doc/doc.css +60 -0
- data/vendor/app/web/doc/img/actions_menu.png +0 -0
- data/vendor/app/web/doc/img/button_actions_menu.png +0 -0
- data/vendor/app/web/doc/img/button_dragarea.png +0 -0
- data/vendor/app/web/doc/img/jsoneditor.png +0 -0
- data/vendor/app/web/doc/img/jsonformatter.png +0 -0
- data/vendor/app/web/doc/img/main_menu.png +0 -0
- data/vendor/app/web/doc/img/splitter.png +0 -0
- data/vendor/app/web/doc/index.html +201 -0
- data/vendor/app/web/favicon.ico +0 -0
- data/vendor/app/web/fileretriever.css +54 -0
- data/vendor/app/web/fileretriever.js +567 -0
- data/vendor/app/web/fileretriever.php +120 -0
- data/vendor/app/web/googlea47c4a0b36d11021.html +1 -0
- data/vendor/app/web/hash.js +133 -0
- data/vendor/app/web/img/description.txt +20 -0
- data/vendor/app/web/img/header_background.png +0 -0
- data/vendor/app/web/img/icon_128.png +0 -0
- data/vendor/app/web/img/icon_16.png +0 -0
- data/vendor/app/web/img/icon_gray.svg +151 -0
- data/vendor/app/web/img/icon_gray_16.svg +150 -0
- data/vendor/app/web/img/icon_orange.svg +151 -0
- data/vendor/app/web/img/logo.png +0 -0
- data/vendor/app/web/img/logo.xcf +0 -0
- data/vendor/app/web/img/logo_app.png +0 -0
- data/vendor/app/web/img/logo_app.xcf +0 -0
- data/vendor/app/web/index.html +191 -0
- data/vendor/app/web/notify.js +150 -0
- data/vendor/app/web/queryparams.js +71 -0
- data/vendor/app/web/robots.txt +0 -0
- data/vendor/app/web/splitter.js +179 -0
- data/vendor/app/web/test.html +224 -0
- data/vendor/component.json +33 -0
- data/vendor/docs/api.md +188 -0
- data/vendor/docs/usage.md +137 -0
- data/vendor/examples/01_basic_usage.html +45 -0
- data/vendor/examples/02_viewer.html +38 -0
- data/vendor/examples/03_switch_mode.html +98 -0
- data/vendor/examples/cur.file +1 -0
- data/vendor/examples/jquery.js +2 -0
- data/vendor/examples/meta.js +1 -0
- data/vendor/examples/requirejs_demo/requirejs_demo.html +19 -0
- data/vendor/examples/requirejs_demo/scripts/main.js +25 -0
- data/vendor/examples/requirejs_demo/scripts/require.js +35 -0
- data/vendor/img/jsoneditor-icons.png +0 -0
- data/vendor/jsoneditor-min.css +1 -0
- data/vendor/jsoneditor-min.js +34 -0
- data/vendor/jsoneditor.css +597 -0
- data/vendor/jsoneditor.js +6069 -0
- data/vendor/jsoneditor/css/contextmenu.css +219 -0
- data/vendor/jsoneditor/css/img/description.txt +13 -0
- data/vendor/jsoneditor/css/img/export.sh +16 -0
- data/vendor/jsoneditor/css/img/jsoneditor-icons.png +0 -0
- data/vendor/jsoneditor/css/img/jsoneditor-icons.svg +861 -0
- data/vendor/jsoneditor/css/jsoneditor.css +220 -0
- data/vendor/jsoneditor/css/menu.css +81 -0
- data/vendor/jsoneditor/css/searchbox.css +73 -0
- data/vendor/jsoneditor/js/appendnode.js +211 -0
- data/vendor/jsoneditor/js/contextmenu.js +440 -0
- data/vendor/jsoneditor/js/header.js +32 -0
- data/vendor/jsoneditor/js/highlighter.js +82 -0
- data/vendor/jsoneditor/js/history.js +218 -0
- data/vendor/jsoneditor/js/jsoneditor.js +206 -0
- data/vendor/jsoneditor/js/module.js +50 -0
- data/vendor/jsoneditor/js/node.js +2864 -0
- data/vendor/jsoneditor/js/searchbox.js +288 -0
- data/vendor/jsoneditor/js/texteditor.js +311 -0
- data/vendor/jsoneditor/js/treeeditor.js +770 -0
- data/vendor/jsoneditor/js/util.js +582 -0
- data/vendor/lib/ace/ace.js +11 -0
- data/vendor/lib/ace/mode-json.js +1 -0
- data/vendor/lib/ace/theme-jsoneditor.js +144 -0
- data/vendor/lib/ace/theme-textmate.js +163 -0
- data/vendor/lib/ace/worker-json.js +1 -0
- data/vendor/lib/jsonlint/README.md +62 -0
- data/vendor/lib/jsonlint/jsonlint.js +432 -0
- data/vendor/misc/screenshots/actionsmenu_640x400.png +0 -0
- data/vendor/misc/screenshots/codeeditor_640x400.png +0 -0
- data/vendor/misc/screenshots/description.json +17 -0
- data/vendor/misc/screenshots/jsoneditoronline.png +0 -0
- data/vendor/misc/screenshots/jsoneditoronline_640x400.png +0 -0
- data/vendor/misc/screenshots/search_640x400.png +0 -0
- data/vendor/misc/screenshots/small_tile.xcf +0 -0
- data/vendor/misc/screenshots/small_tile_440x280.png +0 -0
- data/vendor/misc/todo.txt +101 -0
- data/vendor/package.json +28 -0
- data/vendor/test/couchdbeditor.html +100 -0
- data/vendor/test/largefile.json +12605 -0
- data/vendor/test/test_ace.html +60 -0
- data/vendor/test/test_editable_div.html +449 -0
- 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 = ' ';
|
|
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
|
+
};
|