2012-12-20 02:22:09 +08:00
|
|
|
/*!
|
2012-11-01 05:16:07 +08:00
|
|
|
* @file app.js
|
|
|
|
*
|
|
|
|
* @brief
|
|
|
|
* JSONEditor is an editor to display and edit JSON data in a treeview.
|
|
|
|
*
|
|
|
|
* Supported browsers: Chrome, Firefox, Safari, Opera, Internet Explorer 8+
|
|
|
|
*
|
|
|
|
* @license
|
|
|
|
* This json editor is open sourced with the intention to use the editor as
|
|
|
|
* a component in your own application. Not to just copy and monetize the editor
|
|
|
|
* as it is.
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
|
|
* use this file except in compliance with the License. You may obtain a copy
|
|
|
|
* of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
|
|
* License for the specific language governing permissions and limitations under
|
|
|
|
* the License.
|
|
|
|
*
|
2013-01-02 03:54:07 +08:00
|
|
|
* Copyright (C) 2011-2013 Jos de Jong, http://jsoneditoronline.org
|
2012-11-01 05:16:07 +08:00
|
|
|
*
|
|
|
|
* @author Jos de Jong, <wjosdejong@gmail.com>
|
2013-04-30 19:23:06 +08:00
|
|
|
* @date 2013-04-30
|
2012-11-01 05:16:07 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
2013-04-30 19:23:06 +08:00
|
|
|
var treeEditor = null;
|
|
|
|
var codeEditor = null;
|
2012-11-01 05:16:07 +08:00
|
|
|
|
|
|
|
var app = {};
|
|
|
|
|
|
|
|
/**
|
2013-03-09 04:32:30 +08:00
|
|
|
* Get the JSON from the code editor and load it in the tree editor
|
2012-11-01 05:16:07 +08:00
|
|
|
*/
|
2013-04-30 19:23:06 +08:00
|
|
|
app.CodeToTree = function() {
|
2012-11-01 05:16:07 +08:00
|
|
|
try {
|
2013-04-30 19:23:06 +08:00
|
|
|
treeEditor.set(codeEditor.get());
|
2012-11-01 05:16:07 +08:00
|
|
|
}
|
|
|
|
catch (err) {
|
2013-05-04 17:26:40 +08:00
|
|
|
app.notify.showError(app.formatError(err));
|
2012-11-01 05:16:07 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2013-03-09 04:32:30 +08:00
|
|
|
* Get the JSON from the tree editor and load it into the code editor
|
2012-11-01 05:16:07 +08:00
|
|
|
*/
|
2013-04-30 19:23:06 +08:00
|
|
|
app.treeToCode = function () {
|
2012-11-01 05:16:07 +08:00
|
|
|
try {
|
2013-04-30 19:23:06 +08:00
|
|
|
codeEditor.set(treeEditor.get());
|
2012-11-01 05:16:07 +08:00
|
|
|
}
|
|
|
|
catch (err) {
|
2013-05-04 17:26:40 +08:00
|
|
|
app.notify.showError(app.formatError(err));
|
2012-11-01 05:16:07 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2013-04-30 19:23:06 +08:00
|
|
|
* Load the interface (tree editor, code editor, splitter)
|
2012-11-01 05:16:07 +08:00
|
|
|
*/
|
|
|
|
// TODO: split the method load in multiple methods, it is too large
|
|
|
|
app.load = function() {
|
2012-11-03 18:18:38 +08:00
|
|
|
try {
|
2012-11-01 05:16:07 +08:00
|
|
|
// notification handler
|
2012-11-03 21:35:01 +08:00
|
|
|
app.notify = new Notify();
|
2012-11-01 05:16:07 +08:00
|
|
|
|
|
|
|
// retriever for loading/saving files
|
|
|
|
app.retriever = new FileRetriever({
|
2012-11-03 21:35:01 +08:00
|
|
|
scriptUrl: 'fileretriever.php',
|
|
|
|
notify: app.notify
|
2012-11-01 05:16:07 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
// default json document
|
|
|
|
var json = {
|
2012-12-30 22:04:14 +08:00
|
|
|
"array": [1, 2, 3],
|
|
|
|
"boolean": true,
|
|
|
|
"null": null,
|
|
|
|
"number": 123,
|
|
|
|
"object": {"a": "b", "c": "d", "e": "f"},
|
|
|
|
"string": "Hello World"
|
2012-11-01 05:16:07 +08:00
|
|
|
};
|
|
|
|
|
2012-11-03 22:20:15 +08:00
|
|
|
// load url if query parameters contains a url
|
|
|
|
if (window.QueryParams) {
|
|
|
|
var qp = new QueryParams();
|
|
|
|
var url = qp.getValue('url');
|
2012-11-01 05:16:07 +08:00
|
|
|
if (url) {
|
|
|
|
json = {};
|
|
|
|
app.openUrl(url);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-30 19:23:06 +08:00
|
|
|
// Store whether tree editor or code editor is last changed
|
2012-11-03 18:18:38 +08:00
|
|
|
app.lastChanged = undefined;
|
|
|
|
|
2013-04-30 19:23:06 +08:00
|
|
|
// code editor
|
|
|
|
var container = document.getElementById("codeEditor");
|
|
|
|
codeEditor = new jsoneditor.JSONEditor(container, {
|
2013-03-05 05:14:19 +08:00
|
|
|
mode: 'code',
|
2012-11-03 18:18:38 +08:00
|
|
|
change: function () {
|
2013-04-30 19:23:06 +08:00
|
|
|
app.lastChanged = codeEditor;
|
2013-05-04 17:26:40 +08:00
|
|
|
},
|
|
|
|
error: function (err) {
|
|
|
|
app.notify.showError(app.formatError(err));
|
2012-11-03 18:18:38 +08:00
|
|
|
}
|
|
|
|
});
|
2013-04-30 19:23:06 +08:00
|
|
|
codeEditor.set(json);
|
2012-11-01 05:16:07 +08:00
|
|
|
|
2013-04-30 19:23:06 +08:00
|
|
|
// tree editor
|
|
|
|
container = document.getElementById("treeEditor");
|
|
|
|
treeEditor = new jsoneditor.JSONEditor(container, {
|
|
|
|
mode: 'tree',
|
2012-11-03 18:18:38 +08:00
|
|
|
change: function () {
|
2013-04-30 19:23:06 +08:00
|
|
|
app.lastChanged = treeEditor;
|
2013-05-04 17:26:40 +08:00
|
|
|
},
|
|
|
|
error: function (err) {
|
|
|
|
app.notify.showError(app.formatError(err));
|
2013-01-06 06:18:56 +08:00
|
|
|
}
|
2012-11-03 18:18:38 +08:00
|
|
|
});
|
2013-04-30 19:23:06 +08:00
|
|
|
treeEditor.set(json);
|
|
|
|
// TODO: automatically synchronize data of code and tree editor? (tree editor should keep its state though)
|
2012-11-01 05:16:07 +08:00
|
|
|
|
|
|
|
// splitter
|
|
|
|
app.splitter = new Splitter({
|
2013-03-08 05:35:06 +08:00
|
|
|
container: document.getElementById('drag'),
|
2012-11-01 05:16:07 +08:00
|
|
|
change: function () {
|
|
|
|
app.resize();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2013-04-30 19:23:06 +08:00
|
|
|
// button Code-to-Tree
|
|
|
|
var toTree = document.getElementById('toTree');
|
|
|
|
toTree.onclick = function () {
|
2012-11-01 05:16:07 +08:00
|
|
|
this.focus();
|
2013-04-30 19:23:06 +08:00
|
|
|
app.CodeToTree();
|
2012-11-01 05:16:07 +08:00
|
|
|
};
|
2013-03-08 05:35:06 +08:00
|
|
|
|
2013-04-30 19:23:06 +08:00
|
|
|
// button Tree-to-Code
|
2013-03-09 04:32:30 +08:00
|
|
|
var toCode = document.getElementById('toCode');
|
|
|
|
toCode.onclick = function () {
|
2012-11-01 05:16:07 +08:00
|
|
|
this.focus();
|
2013-04-30 19:23:06 +08:00
|
|
|
app.treeToCode();
|
2012-11-01 05:16:07 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
// web page resize handler
|
2013-01-05 21:32:40 +08:00
|
|
|
jsoneditor.util.addEventListener(window, 'resize', app.resize);
|
2012-11-01 05:16:07 +08:00
|
|
|
|
|
|
|
// clear button
|
|
|
|
var domClear = document.getElementById('clear');
|
|
|
|
domClear.onclick = app.clearFile;
|
|
|
|
|
|
|
|
/* TODO: enable clicking on open to execute the default, "open file"
|
|
|
|
// open button
|
|
|
|
var domOpen = document.getElementById('open');
|
|
|
|
var domOpenMenuButton = document.getElementById('openMenuButton');
|
|
|
|
domOpen.onclick = function (event) {
|
|
|
|
event = event || window.event; // for IE8
|
|
|
|
var target = event.target || event.srcElement;
|
|
|
|
if (target == domOpenMenuButton ||
|
|
|
|
(event.offsetX > domOpen.offsetWidth - domOpenMenuButton.offsetWidth)) {
|
|
|
|
// clicked on the menu button
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
app.openFile();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
*/
|
|
|
|
|
|
|
|
// menu button open file
|
|
|
|
var domMenuOpenFile = document.getElementById('menuOpenFile');
|
|
|
|
domMenuOpenFile.onclick = function (event) {
|
|
|
|
app.openFile();
|
2013-01-05 21:32:40 +08:00
|
|
|
jsoneditor.util.stopPropagation(event);
|
|
|
|
jsoneditor.util.preventDefault(event);
|
2012-11-01 05:16:07 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
// menu button open url
|
|
|
|
var domMenuOpenUrl = document.getElementById('menuOpenUrl');
|
|
|
|
domMenuOpenUrl.onclick = function (event) {
|
|
|
|
app.openUrl();
|
2013-01-05 21:32:40 +08:00
|
|
|
jsoneditor.util.stopPropagation(event);
|
|
|
|
jsoneditor.util.preventDefault(event);
|
2012-11-01 05:16:07 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
// save button
|
|
|
|
var domSave = document.getElementById('save');
|
|
|
|
domSave.onclick = app.saveFile;
|
|
|
|
|
2013-04-30 19:23:06 +08:00
|
|
|
// set focus on the code editor
|
|
|
|
codeEditor.focus();
|
2012-11-01 05:16:07 +08:00
|
|
|
|
|
|
|
// enforce FireFox to not do spell checking on any input field
|
|
|
|
document.body.spellcheck = false;
|
2012-11-03 18:18:38 +08:00
|
|
|
} catch (err) {
|
2012-11-03 21:35:01 +08:00
|
|
|
app.notify.showError(err);
|
2012-11-03 18:18:38 +08:00
|
|
|
}
|
2012-11-01 05:16:07 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Callback method called when a file or url is opened.
|
|
|
|
* @param {Error} err
|
|
|
|
* @param {String} data
|
|
|
|
*/
|
|
|
|
app.openCallback = function (err, data) {
|
|
|
|
if (!err) {
|
2013-04-30 19:23:06 +08:00
|
|
|
if (data != null) {
|
|
|
|
codeEditor.setText(data);
|
2012-11-01 05:16:07 +08:00
|
|
|
try {
|
2013-01-05 21:32:40 +08:00
|
|
|
var json = jsoneditor.util.parse(data);
|
2013-04-30 19:23:06 +08:00
|
|
|
treeEditor.set(json);
|
2012-11-01 05:16:07 +08:00
|
|
|
}
|
|
|
|
catch (err) {
|
2013-04-30 19:23:06 +08:00
|
|
|
treeEditor.set({});
|
2013-05-04 17:26:40 +08:00
|
|
|
app.notify.showError(app.formatError(err));
|
2012-11-01 05:16:07 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2012-11-03 21:35:01 +08:00
|
|
|
app.notify.showError(err);
|
2012-11-01 05:16:07 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Open a file explorer to select a file and open the file
|
|
|
|
*/
|
|
|
|
app.openFile = function() {
|
|
|
|
app.retriever.loadFile(app.openCallback);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Open a url. If no url is provided as parameter, a dialog will be opened
|
|
|
|
* to select a url.
|
|
|
|
* @param {String} [url]
|
|
|
|
*/
|
|
|
|
app.openUrl = function (url) {
|
|
|
|
if (!url) {
|
|
|
|
app.retriever.loadUrlDialog(app.openCallback);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
app.retriever.loadUrl(url, app.openCallback);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Open a file explorer to save the file.
|
|
|
|
*/
|
|
|
|
app.saveFile = function () {
|
2013-04-30 19:23:06 +08:00
|
|
|
// first synchronize both editors contents
|
|
|
|
if (app.lastChanged == treeEditor) {
|
|
|
|
app.treeToCode();
|
2012-11-03 18:18:38 +08:00
|
|
|
}
|
2013-04-30 19:23:06 +08:00
|
|
|
/* TODO: also sync from code to tree editor? will clear the history ...
|
|
|
|
if (app.lastChanged == codeEditor) {
|
2013-03-09 04:32:30 +08:00
|
|
|
app.CodeToEditor();
|
2012-11-03 18:18:38 +08:00
|
|
|
}
|
|
|
|
*/
|
|
|
|
app.lastChanged = undefined;
|
|
|
|
|
2013-04-30 19:23:06 +08:00
|
|
|
// save the text from the code editor
|
2012-11-01 05:16:07 +08:00
|
|
|
// TODO: show a 'saving...' notification
|
2013-04-30 19:23:06 +08:00
|
|
|
var data = codeEditor.getText();
|
2012-11-01 05:16:07 +08:00
|
|
|
app.retriever.saveFile(data, function (err) {
|
|
|
|
if (err) {
|
2012-11-03 21:35:01 +08:00
|
|
|
app.notify.showError(err);
|
2012-11-01 05:16:07 +08:00
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2013-05-04 17:26:40 +08:00
|
|
|
/**
|
|
|
|
* Format a JSON parse/stringify error as HTML
|
|
|
|
* @param {Error} err
|
|
|
|
* @returns {string}
|
|
|
|
*/
|
|
|
|
app.formatError = function (err) {
|
|
|
|
var message = '<pre class="error">' + err.toString() + '</pre>';
|
|
|
|
if (typeof(jsonlint) != 'undefined') {
|
|
|
|
message +=
|
|
|
|
'<a class="error" href="http://zaach.github.com/jsonlint/" target="_blank">' +
|
|
|
|
'validated by jsonlint' +
|
|
|
|
'</a>';
|
|
|
|
}
|
|
|
|
return message;
|
|
|
|
};
|
|
|
|
|
2012-11-01 05:16:07 +08:00
|
|
|
/**
|
|
|
|
* Clear the current file
|
|
|
|
*/
|
|
|
|
app.clearFile = function () {
|
|
|
|
var json = {};
|
2013-04-30 19:23:06 +08:00
|
|
|
codeEditor.set(json);
|
|
|
|
treeEditor.set(json);
|
2012-11-01 05:16:07 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
app.resize = function() {
|
2013-02-21 15:54:25 +08:00
|
|
|
var domMenu = document.getElementById('menu');
|
2013-04-30 19:23:06 +08:00
|
|
|
var domTreeEditor = document.getElementById('treeEditor');
|
|
|
|
var domCodeEditor = document.getElementById('codeEditor');
|
2012-11-01 05:16:07 +08:00
|
|
|
var domSplitter = document.getElementById('splitter');
|
2013-03-08 05:35:06 +08:00
|
|
|
var domSplitterButtons = document.getElementById('buttons');
|
|
|
|
var domSplitterDrag = document.getElementById('drag');
|
2012-11-01 05:16:07 +08:00
|
|
|
var domAd = document.getElementById('ad');
|
|
|
|
|
2013-03-08 05:35:06 +08:00
|
|
|
var margin = 15;
|
|
|
|
var width = (window.innerWidth || document.body.offsetWidth ||
|
|
|
|
document.documentElement.offsetWidth);
|
2012-11-01 05:16:07 +08:00
|
|
|
var adWidth = domAd ? domAd.clientWidth : 0;
|
|
|
|
if (adWidth) {
|
2013-03-08 05:35:06 +08:00
|
|
|
width -= (adWidth + margin);
|
2012-11-01 05:16:07 +08:00
|
|
|
}
|
|
|
|
|
2013-01-06 05:19:10 +08:00
|
|
|
if (app.splitter) {
|
2013-03-08 05:35:06 +08:00
|
|
|
app.splitter.setWidth(width);
|
|
|
|
|
|
|
|
// calculate horizontal splitter position
|
|
|
|
var value = app.splitter.getValue();
|
2013-04-30 19:23:06 +08:00
|
|
|
var showCodeEditor = (value > 0);
|
|
|
|
var showTreeEditor = (value < 1);
|
|
|
|
var showButtons = showCodeEditor && showTreeEditor;
|
2013-03-08 05:35:06 +08:00
|
|
|
domSplitterButtons.style.display = showButtons ? '' : 'none';
|
|
|
|
|
|
|
|
var splitterWidth = domSplitter.clientWidth;
|
|
|
|
var splitterLeft;
|
2013-04-30 19:23:06 +08:00
|
|
|
if (!showCodeEditor) {
|
|
|
|
// code editor not visible
|
2013-03-08 05:35:06 +08:00
|
|
|
splitterLeft = 0;
|
|
|
|
domSplitterDrag.innerHTML = '›';
|
|
|
|
domSplitterDrag.title = 'Drag right to show the code editor';
|
|
|
|
}
|
2013-04-30 19:23:06 +08:00
|
|
|
else if (!showTreeEditor) {
|
|
|
|
// tree editor not visible
|
2013-03-08 05:35:06 +08:00
|
|
|
splitterLeft = width * value - splitterWidth;
|
|
|
|
domSplitterDrag.innerHTML = '‹';
|
|
|
|
domSplitterDrag.title = 'Drag left to show the tree editor';
|
|
|
|
}
|
|
|
|
else {
|
2013-04-30 19:23:06 +08:00
|
|
|
// both tree and code editor visible
|
2013-03-08 05:35:06 +08:00
|
|
|
splitterLeft = width * value - splitterWidth / 2;
|
2013-03-12 03:29:46 +08:00
|
|
|
|
|
|
|
// TODO: find a character with vertical dots that works on IE8 too, or use an image
|
|
|
|
var isIE8 = (jsoneditor.util.getInternetExplorerVersion() == 8);
|
|
|
|
domSplitterDrag.innerHTML = (!isIE8) ? '⋮' : '|';
|
2013-03-08 05:35:06 +08:00
|
|
|
domSplitterDrag.title = 'Drag left or right to change the width of the panels';
|
|
|
|
}
|
2012-11-01 05:16:07 +08:00
|
|
|
|
2013-04-30 19:23:06 +08:00
|
|
|
// resize code editor
|
|
|
|
domCodeEditor.style.display = (value == 0) ? 'none' : '';
|
|
|
|
domCodeEditor.style.width = Math.max(Math.round(splitterLeft), 0) + 'px';
|
|
|
|
codeEditor.resize();
|
2012-11-01 05:16:07 +08:00
|
|
|
|
2013-03-08 05:35:06 +08:00
|
|
|
// resize the splitter
|
|
|
|
domSplitterDrag.style.height = (domSplitter.clientHeight -
|
|
|
|
domSplitterButtons.clientHeight - 2 * margin -
|
|
|
|
(showButtons ? margin : 0)) + 'px';
|
|
|
|
domSplitterDrag.style.lineHeight = domSplitterDrag.style.height;
|
|
|
|
|
2013-04-30 19:23:06 +08:00
|
|
|
// resize tree editor
|
2013-01-06 05:19:10 +08:00
|
|
|
// the width has a -1 to prevent the width from being just half a pixel
|
|
|
|
// wider than the window, causing the content elements to wrap...
|
2013-04-30 19:23:06 +08:00
|
|
|
domTreeEditor.style.display = (value == 1) ? 'none' : '';
|
|
|
|
domTreeEditor.style.left = Math.round(splitterLeft + splitterWidth) + 'px';
|
|
|
|
domTreeEditor.style.width = Math.max(Math.round(width - splitterLeft - splitterWidth - 2), 0) + 'px';
|
2013-01-06 05:19:10 +08:00
|
|
|
}
|
2012-11-01 05:16:07 +08:00
|
|
|
|
2013-02-21 15:54:25 +08:00
|
|
|
// align main menu with ads
|
|
|
|
if (domMenu) {
|
|
|
|
if (adWidth) {
|
2013-03-08 05:35:06 +08:00
|
|
|
domMenu.style.right = (margin + (adWidth + margin)) + 'px';
|
2013-02-21 15:54:25 +08:00
|
|
|
}
|
|
|
|
else {
|
2013-03-08 05:35:06 +08:00
|
|
|
domMenu.style.right = margin + 'px';
|
2013-02-21 15:54:25 +08:00
|
|
|
}
|
2012-11-01 05:16:07 +08:00
|
|
|
}
|
|
|
|
};
|