- Implemented load/save functionality.
- Rearranged the code. - Updated the build file accordingly
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
* ajax
|
||||
* Utility to perform ajax get and post requests. Supported browsers:
|
||||
* Chrome, Firefox, Opera, Safari, Internet Explorer 7+.
|
||||
*/
|
||||
var ajax = (function () {
|
||||
function fetch (method, url, body, callback) {
|
||||
try {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState == 4) {
|
||||
callback(xhr.responseText, xhr.status);
|
||||
}
|
||||
};
|
||||
xhr.open(method, url, true);
|
||||
xhr.send(body);
|
||||
}
|
||||
catch (err) {
|
||||
callback(err, 0);
|
||||
}
|
||||
}
|
||||
|
||||
function get (url, callback) {
|
||||
fetch('GET', url, null, callback);
|
||||
}
|
||||
|
||||
function post (url, body, callback) {
|
||||
fetch('POST', url, body, callback)
|
||||
}
|
||||
|
||||
return {
|
||||
'fetch': fetch,
|
||||
'get': get,
|
||||
'post': post
|
||||
}
|
||||
})();
|
|
@ -17,7 +17,9 @@ span.header-light {
|
|||
#header {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
/* TODO
|
||||
overflow: hidden;
|
||||
*/
|
||||
|
||||
background: #4D4D4D url('img/header_background.png');
|
||||
color: white;
|
||||
|
@ -29,6 +31,99 @@ span.header-light {
|
|||
border: none;
|
||||
}
|
||||
|
||||
#menu {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 15px;
|
||||
font-size: 11pt;
|
||||
}
|
||||
|
||||
#menu ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
#menu ul li {
|
||||
color: #e6e6e6;
|
||||
background: none;
|
||||
border: none;
|
||||
border-right: 1px solid #737373;
|
||||
height: 30px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
float: left;
|
||||
position: relative;
|
||||
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#menu ul li:first-child {
|
||||
border-left: 1px solid #737373;
|
||||
}
|
||||
|
||||
#menu ul li:hover {
|
||||
color: white;
|
||||
background-color: #737373;
|
||||
}
|
||||
|
||||
#menu ul li ul {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#menu ul li:hover > ul {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#menu ul li ul {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
left: 0;
|
||||
z-index: 999;
|
||||
|
||||
background: #f5f5f5;
|
||||
border: 1px solid lightgray;
|
||||
box-shadow: 0 0 15px rgba(128, 128, 128, 0.5);
|
||||
}
|
||||
|
||||
#menu ul li ul li {
|
||||
color: #737373;
|
||||
background: none;
|
||||
border: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#menu ul li ul li:first-child {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
#menu ul li ul li:hover {
|
||||
background-color: white;
|
||||
color: #737373;
|
||||
}
|
||||
|
||||
#menu a {
|
||||
padding: 6px 10px;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#menu ul li ul li a {
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
#openMenuButton {
|
||||
font-size: 75%;
|
||||
margin-left: 2px;
|
||||
}
|
||||
#menu #open {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/* TODO: enable the menu with keys (when openMenuButton is active) */
|
||||
|
||||
#auto {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
@ -131,14 +226,23 @@ a.adInfo {
|
|||
text-decoration: underline;
|
||||
}
|
||||
|
||||
div.error {
|
||||
color: red;
|
||||
background-color: #FFC0CB;
|
||||
border: 1px solid red;
|
||||
div.error, div.notification {
|
||||
border-radius: 3px;
|
||||
padding: 5px;
|
||||
margin: 5px;
|
||||
box-shadow: 0 0 15px rgba(128, 128, 128, 0.5);
|
||||
|
||||
/* TODO: add some transition effect */
|
||||
}
|
||||
div.error {
|
||||
color: red;
|
||||
background-color: #FFC0CB;
|
||||
border: 1px solid red;
|
||||
}
|
||||
div.notification {
|
||||
color: #1a1a1a;
|
||||
background-color: #FFFFAB;
|
||||
border: 1px solid #e6d600;
|
||||
}
|
||||
pre.error {
|
||||
margin: 0 0 10px 0;
|
||||
|
@ -163,9 +267,9 @@ div.convert-right, div.convert-left {
|
|||
}
|
||||
|
||||
div.convert-right {
|
||||
background: url('../jsoneditor/img/jsoneditor-icons.png') -168px 0;
|
||||
background: url('lib/jsoneditor/img/jsoneditor-icons.png') -168px 0;
|
||||
}
|
||||
|
||||
div.convert-left {
|
||||
background: url('../jsoneditor/img/jsoneditor-icons.png') -192px 0;
|
||||
background: url('lib/jsoneditor/img/jsoneditor-icons.png') -192px 0;
|
||||
}
|
|
@ -0,0 +1,364 @@
|
|||
/**
|
||||
* @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.
|
||||
*
|
||||
* Copyright (C) 2011-2012 Jos de Jong, http://jsoneditoronline.org
|
||||
*
|
||||
* @author Jos de Jong, <wjosdejong@gmail.com>
|
||||
* @date 2012-10-31
|
||||
*/
|
||||
|
||||
|
||||
var editor = null;
|
||||
var formatter = null;
|
||||
|
||||
var app = {};
|
||||
|
||||
/**
|
||||
* Get the JSON from the formatter and load it in the editor
|
||||
*/
|
||||
app.formatterToEditor = function() {
|
||||
try {
|
||||
editor.set(formatter.get());
|
||||
}
|
||||
catch (err) {
|
||||
app.notifications.showError(err);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the JSON from the editor and load it into the formatter
|
||||
*/
|
||||
app.editorToFormatter = function () {
|
||||
try {
|
||||
formatter.set(editor.get());
|
||||
}
|
||||
catch (err) {
|
||||
app.notifications.showError(err);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Load the interface (editor, formatter, splitter)
|
||||
*/
|
||||
// TODO: split the method load in multiple methods, it is too large
|
||||
app.load = function() {
|
||||
//try {
|
||||
// notification handler
|
||||
app.notifications = new Notifications();
|
||||
|
||||
// retriever for loading/saving files
|
||||
app.retriever = new FileRetriever({
|
||||
scriptUrl: 'fileretriever.php'
|
||||
});
|
||||
|
||||
// default json document
|
||||
var json = {
|
||||
"name": "John Smith",
|
||||
"age": 32,
|
||||
"employed": true,
|
||||
"address": {
|
||||
"street": "701 First Ave.",
|
||||
"city": "Sunnyvale, CA 95125",
|
||||
"country": "United States"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"name": "Richard",
|
||||
"age": 7
|
||||
},
|
||||
{
|
||||
"name": "Susan",
|
||||
"age": 4
|
||||
},
|
||||
{
|
||||
"name": "James",
|
||||
"age": 3
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// load url if hash contains a url
|
||||
if (window.Hash) {
|
||||
var hash = new Hash();
|
||||
var url = hash.getValue('url');
|
||||
if (url) {
|
||||
json = {};
|
||||
app.openUrl(url);
|
||||
}
|
||||
}
|
||||
|
||||
// formatter
|
||||
var container = document.getElementById("jsonformatter");
|
||||
formatter = new JSONFormatter(container);
|
||||
formatter.set(json);
|
||||
formatter.onError = function (err) {
|
||||
app.notifications.showError(err);
|
||||
};
|
||||
|
||||
// editor
|
||||
container = document.getElementById("jsoneditor");
|
||||
editor = new JSONEditor(container);
|
||||
editor.set(json);
|
||||
|
||||
// splitter
|
||||
var domSplitter = document.getElementById('splitter');
|
||||
app.splitter = new Splitter({
|
||||
container: domSplitter,
|
||||
change: function () {
|
||||
app.resize();
|
||||
}
|
||||
});
|
||||
|
||||
// button Formatter-to-Editor
|
||||
domSplitter.appendChild(document.createElement('br'));
|
||||
domSplitter.appendChild(document.createElement('br'));
|
||||
domSplitter.appendChild(document.createElement('br'));
|
||||
var toForm = document.createElement('button');
|
||||
toForm.id = 'toForm';
|
||||
toForm.title = 'JSON to Editor';
|
||||
toForm.className = 'convert';
|
||||
toForm.innerHTML = '<div class="convert-right"></div>';
|
||||
toForm.onclick = function () {
|
||||
this.focus();
|
||||
app.formatterToEditor();
|
||||
};
|
||||
domSplitter.appendChild(toForm);
|
||||
|
||||
// button Editor-to-Formatter
|
||||
domSplitter.appendChild(document.createElement('br'));
|
||||
domSplitter.appendChild(document.createElement('br'));
|
||||
var toJSON = document.createElement('button');
|
||||
toJSON.id = 'toJSON';
|
||||
toJSON.title = 'Editor to JSON';
|
||||
toJSON.className = 'convert';
|
||||
toJSON.innerHTML = '<div class="convert-left"></div>';
|
||||
toJSON.onclick = function () {
|
||||
this.focus();
|
||||
app.editorToFormatter();
|
||||
};
|
||||
domSplitter.appendChild(toJSON);
|
||||
|
||||
// web page resize handler
|
||||
JSONEditor.Events.addEventListener(window, 'resize', app.resize);
|
||||
|
||||
// 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();
|
||||
JSONEditor.Events.stopPropagation(event);
|
||||
JSONEditor.Events.preventDefault(event);
|
||||
};
|
||||
|
||||
// menu button open url
|
||||
var domMenuOpenUrl = document.getElementById('menuOpenUrl');
|
||||
domMenuOpenUrl.onclick = function (event) {
|
||||
app.openUrl();
|
||||
JSONEditor.Events.stopPropagation(event);
|
||||
JSONEditor.Events.preventDefault(event);
|
||||
};
|
||||
|
||||
// save button
|
||||
var domSave = document.getElementById('save');
|
||||
domSave.onclick = app.saveFile;
|
||||
|
||||
// TODO: implement a focus method
|
||||
formatter.textarea.focus();
|
||||
|
||||
/* TODO: use checkChange
|
||||
// TODO: a nicer method to check for changes
|
||||
var formatterLastContent;
|
||||
var editorLastContent;
|
||||
function checkChange () {
|
||||
try {
|
||||
// check for change in formatter
|
||||
var formatterJSON = formatter.get();
|
||||
var formatterContent = JSON.stringify(formatterJSON);
|
||||
if (formatterContent != formatterLastContent) {
|
||||
formatterLastContent = formatterContent;
|
||||
editorLastContent = formatterContent;
|
||||
editor.set(formatterJSON);
|
||||
}
|
||||
else {
|
||||
// check for change in editor
|
||||
var editorJSON = editor.get();
|
||||
var editorContent = JSON.stringify(editorJSON);
|
||||
if (editorContent != editorLastContent) {
|
||||
editorLastContent = editorContent;
|
||||
formatterLastContent = editorContent;
|
||||
formatter.set(editorJSON);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
app.notifications.showError(err);
|
||||
}
|
||||
|
||||
setTimeout(checkChange, 1000);
|
||||
}
|
||||
checkChange();
|
||||
*/
|
||||
|
||||
// enforce FireFox to not do spell checking on any input field
|
||||
document.body.spellcheck = false;
|
||||
//} catch (err) {
|
||||
// app.notifications.showError(err);
|
||||
//}
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback method called when a file or url is opened.
|
||||
* @param {Error} err
|
||||
* @param {String} data
|
||||
*/
|
||||
app.openCallback = function (err, data) {
|
||||
if (!err) {
|
||||
if (data != undefined) {
|
||||
formatter.setText(data);
|
||||
try {
|
||||
var json = JSONEditor.parse(data);
|
||||
editor.set(json);
|
||||
}
|
||||
catch (err) {
|
||||
editor.set({});
|
||||
app.notifications.showError(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
app.notifications.showError(err);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Open a file explorer to select a file and open the file
|
||||
*/
|
||||
app.openFile = function() {
|
||||
// TODO: show a 'loading...' notification
|
||||
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) {
|
||||
// TODO: show a 'loading...' notification
|
||||
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 () {
|
||||
// TODO: get data from the most recently changed editor: formatter or editor
|
||||
// TODO: show a 'saving...' notification
|
||||
var data = formatter.getText();
|
||||
app.retriever.saveFile(data, function (err) {
|
||||
if (err) {
|
||||
app.notifications.showError(err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear the current file
|
||||
*/
|
||||
app.clearFile = function () {
|
||||
var json = {};
|
||||
formatter.set(json);
|
||||
editor.set(json);
|
||||
app.retriever.removeUrl();
|
||||
};
|
||||
|
||||
app.resize = function() {
|
||||
var domEditor = document.getElementById('jsoneditor');
|
||||
var domFormatter = document.getElementById('jsonformatter');
|
||||
var domSplitter = document.getElementById('splitter');
|
||||
var domAd = document.getElementById('ad');
|
||||
var domAdInfo = document.getElementById('adInfo');
|
||||
|
||||
var width = window.innerWidth || document.body.offsetWidth || document.documentElement.offsetWidth;
|
||||
var height = window.innerHeight || document.body.offsetHeight || document.documentElement.offsetHeight;
|
||||
var adWidth = domAd ? domAd.clientWidth : 0;
|
||||
var splitterWidth = domSplitter.clientWidth;
|
||||
if (adWidth) {
|
||||
width -= (adWidth + 15); // Not so nice, +15 here for the margin
|
||||
}
|
||||
|
||||
var splitterLeft = width * app.splitter.getValue();
|
||||
|
||||
// resize formatter
|
||||
domFormatter.style.width = Math.round(splitterLeft) + 'px';
|
||||
|
||||
// resize editor
|
||||
// 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...
|
||||
domEditor.style.left = Math.round(splitterLeft + splitterWidth) + 'px';
|
||||
domEditor.style.width = Math.round(width - splitterLeft - splitterWidth - 1) + 'px';
|
||||
|
||||
// resize ad text
|
||||
if (domAdInfo && domAd) {
|
||||
var infoHeight = domAdInfo.clientHeight;
|
||||
domAd.style.paddingTop = infoHeight + 'px';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Hide the advertisement
|
||||
*/
|
||||
app.hideAds = function() {
|
||||
var domAd = document.getElementById("ad");
|
||||
if (domAd) {
|
||||
domAd.parentNode.removeChild(domAd);
|
||||
app.resize();
|
||||
}
|
||||
};
|
|
@ -0,0 +1,47 @@
|
|||
JSON EDITOR ONLINE DATA POLICY
|
||||
http://jsoneditoronline.org
|
||||
|
||||
|
||||
This file describes the data policy of JSON Editor Online. When using the
|
||||
open/save functionality of the editor, files may need to be downloaded via the
|
||||
server, therefore:
|
||||
|
||||
DO NOT LOAD OR SAVE SENSITIVE DATA VIA THE EDITORS OPEN/SAVE FUNCTIONALITY.
|
||||
|
||||
Files which are downloaded via the server are sent unsecured and unencrypted.
|
||||
|
||||
|
||||
1. Opening files
|
||||
|
||||
If the browser in use supports HTML5 FileReader, files are directly loaded
|
||||
from disk into the editor. The files are not send to the server.
|
||||
|
||||
If HTML5 is not supported, the files are first uploaded to the server and then
|
||||
downloaded by the editor. The files are deleted from the server as soon as
|
||||
they are downloaded once. If a file is not downloaded for some reason, it will
|
||||
be deleted from the server after one hour.
|
||||
|
||||
|
||||
2. Saving files
|
||||
|
||||
If the browser in use supports HTML5 a.download, files are directly saved
|
||||
from the browser to disk. The files are not send to the server.
|
||||
|
||||
If HTML5 is not supported, the files are first uploaded to the server and then
|
||||
downloaded to disk. The files are deleted from the server as soon as they are
|
||||
downloaded once. If a file is not downloaded for some reason, it will be
|
||||
deleted from the server after one hour.
|
||||
|
||||
|
||||
3. Opening urls
|
||||
|
||||
When opening an url, the editor first opens the url directly. If this fails
|
||||
due to cross-domain restrictions, the url will be retrieved via the server.
|
||||
In that case, the retrieved data is sent directly to the browser and is not
|
||||
stored on the server.
|
||||
|
||||
|
||||
4. Cutting/pasting clipboard data
|
||||
|
||||
When cutting and pasting text in the editor using the system clipboard, no
|
||||
data is sent via the server.
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
@ -0,0 +1,54 @@
|
|||
|
||||
div.fileretriever-overlay, div.fileretriever-background {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
div.fileretriever-overlay {
|
||||
background-color: gray;
|
||||
opacity: 0.2;
|
||||
filter: alpha(opacity = 20);
|
||||
}
|
||||
|
||||
div.fileretriever-border {
|
||||
width: 410px;
|
||||
margin: 100px auto;
|
||||
padding: 20px;
|
||||
background-color: white;
|
||||
border: 1px solid gray;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
form.fileretriever-form {
|
||||
}
|
||||
|
||||
div.fileretriever-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.fileretriever-contents {
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
div.fileretriever-buttons {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
input.fileretriever-field[type="file"] {
|
||||
width: 400px;
|
||||
padding: 3px;
|
||||
}
|
||||
input.fileretriever-field[type="text"] {
|
||||
width: 400px;
|
||||
border: 1px solid lightgray;
|
||||
border-radius: 2px;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
input.fileretriever-submit, input.fileretriever-cancel {
|
||||
margin-left: 10px;
|
||||
}
|
|
@ -0,0 +1,526 @@
|
|||
/**
|
||||
* @file fileretriever.js
|
||||
*
|
||||
* FileRetriever manages client side loading and saving of files.
|
||||
* It requires a server script (fileretriever.php). Loading and saving
|
||||
* files is done purely clientside using HTML5 techniques when supported
|
||||
* by the browser.
|
||||
*
|
||||
* Requires ajax.js and optionally hash.js.
|
||||
*
|
||||
* Supported browsers: Chrome, Firefox, Opera, Safari,
|
||||
* Internet Explorer 8+.
|
||||
*
|
||||
* Example usage:
|
||||
* var retriever = new FileRetriever({
|
||||
* 'serverUrl': 'fileretriever.php'
|
||||
* });
|
||||
* retriever.loadFile(function (err, data) {
|
||||
* console.log('file loaded:', data);
|
||||
* });
|
||||
* retriever.loadUrl(function (err, data) {
|
||||
* console.log('url loaded:', data);
|
||||
* });
|
||||
* retriever.saveFile("some text");
|
||||
*
|
||||
* @constructor FileRetriever
|
||||
* @param {String} options Available options:
|
||||
* {string} serverUrl Server side script for
|
||||
* handling files, for
|
||||
* example "fileretriever.php"
|
||||
* {Number} [maxSize] Maximum allowed file size
|
||||
* in bytes. (this should
|
||||
* be the same as maximum
|
||||
* size allowed by the server
|
||||
* side script). Default is
|
||||
* 1024 * 1024 bytes.
|
||||
* {Boolean} [html5] Use HTML5 solutions
|
||||
* to load/save files when
|
||||
* supported by the browser.
|
||||
* True by default.
|
||||
* {Boolean} [hash] Use hash to store the last opened
|
||||
* filename or url. True by default.
|
||||
*
|
||||
* @license
|
||||
* 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.
|
||||
*
|
||||
* Copyright (c) 2012 Jos de Jong, http://jsoneditoronline.org
|
||||
*
|
||||
* @author Jos de Jong, <wjosdejong@gmail.com>
|
||||
* @date 2012-10-31
|
||||
*/
|
||||
var FileRetriever = function (options) {
|
||||
// set options and variables
|
||||
options = options || {};
|
||||
this.options = {
|
||||
maxSize: ((options.maxSize != undefined) ? options.maxSize : 1024 * 1024),
|
||||
html5: ((options.html5 != undefined) ? options.html5 : true)
|
||||
};
|
||||
this.scriptUrl = options.scriptUrl || 'fileretriever.php';
|
||||
this.defaultFilename = 'document.json';
|
||||
this.loadCallback = function () {};
|
||||
this.dom = {};
|
||||
var me = this;
|
||||
|
||||
if (options.hash !== false) {
|
||||
if (window.Hash) {
|
||||
this.hash = new Hash();
|
||||
}
|
||||
else {
|
||||
console.log('WARNING: missing resource hash.js');
|
||||
}
|
||||
}
|
||||
|
||||
// make an element invisible
|
||||
function hide(elem) {
|
||||
elem.style.visibility = 'hidden';
|
||||
elem.style.position = 'absolute';
|
||||
elem.style.left = '-1000px';
|
||||
elem.style.top = '-1000px';
|
||||
elem.style.width = '0';
|
||||
elem.style.height = '0';
|
||||
}
|
||||
|
||||
// create an iframe to save a file
|
||||
var downloadIframe = document.createElement('iframe');
|
||||
hide(downloadIframe);
|
||||
document.body.appendChild(downloadIframe);
|
||||
this.dom.downloadIframe = downloadIframe;
|
||||
|
||||
// create an iframe for uploading files
|
||||
// the iframe must have an unique name, allowing multiple
|
||||
// FileRetrievers. The name is needed as target for the uploadForm
|
||||
var uploadIframe = document.createElement('iframe');
|
||||
uploadIframe.name = 'fileretriever-upload-' + Math.round(Math.random() * 1E15);
|
||||
hide(uploadIframe);
|
||||
document.body.appendChild(uploadIframe);
|
||||
uploadIframe.onload = function () {
|
||||
// when a downloaded file is retrieved, send a callback with
|
||||
// the retrieved data
|
||||
var id = uploadIframe.contentWindow.document.body.innerHTML;
|
||||
var url = me.scriptUrl + '?id=' + id + '&filename=' + me.getFilename();
|
||||
//console.log('uploadIframe.load ', id, ' ', url)
|
||||
ajax.get(url, function (data, status) {
|
||||
//console.log('ajax.get ', url, ' ', data, ' ', status);
|
||||
if (status == 200) {
|
||||
me.loadCallback(null, data);
|
||||
}
|
||||
else {
|
||||
//console.log('Error loading file ' + url, status, data);
|
||||
var err = new Error('Error loading file ' + me.getFilename());
|
||||
me.loadCallback(err, null);
|
||||
}
|
||||
});
|
||||
};
|
||||
this.dom.uploadIframe = uploadIframe;
|
||||
|
||||
// create a form to upload a file
|
||||
var uploadForm = document.createElement('form');
|
||||
uploadForm.action = this.scriptUrl;
|
||||
uploadForm.method = 'POST';
|
||||
uploadForm.enctype = 'multipart/form-data';
|
||||
uploadForm.target = uploadIframe.name;
|
||||
hide(uploadForm);
|
||||
this.dom.form = uploadForm;
|
||||
var domFile = document.createElement('input');
|
||||
domFile.type = 'file';
|
||||
domFile.name = 'file';
|
||||
domFile.onchange = function () {
|
||||
setTimeout(function () { // Timeout needed for IE
|
||||
var filename = domFile.value;
|
||||
//console.log('load file:' + filename + '.');
|
||||
if (filename.length) {
|
||||
if (me.options.html5 && window.File && window.FileReader) {
|
||||
// load file via HTML5 FileReader (no size limits)
|
||||
var file = domFile.files[0];
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(event) {
|
||||
var data = event.target.result;
|
||||
me.loadCallback(null, data);
|
||||
};
|
||||
|
||||
// Read in the image file as a data URL.
|
||||
reader.readAsText(file);
|
||||
}
|
||||
else {
|
||||
// load by uploading to server
|
||||
// TODO: how to check the file size? (on older browsers)
|
||||
//console.log('submitting...');
|
||||
uploadForm.submit();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// cancel
|
||||
me.loadCallback(null, null);
|
||||
}
|
||||
}, 0);
|
||||
};
|
||||
uploadForm.appendChild(domFile);
|
||||
this.dom.file = domFile;
|
||||
document.body.appendChild(uploadForm);
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete all HTML DOM elements created by the FileRetriever.
|
||||
* The FileRetriever cannot be used after its DOM elements are deleted.
|
||||
*/
|
||||
FileRetriever.prototype.remove = function () {
|
||||
var dom = this.dom;
|
||||
for (var prop in dom) {
|
||||
if (dom.hasOwnProperty(prop)) {
|
||||
var elem = dom[prop];
|
||||
if (elem.parentNode) {
|
||||
elem.parentNode.removeChild(elem);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.dom = {};
|
||||
};
|
||||
|
||||
/**
|
||||
* get a filename from a path or url.
|
||||
* For example "http://site.com/files/example.json" will return "example.json"
|
||||
* @param {String} path A filename, path, or url
|
||||
* @return {String} filename
|
||||
* @private
|
||||
*/
|
||||
FileRetriever.prototype._getFilename = function (path) {
|
||||
// http://stackoverflow.com/a/423385/1262753
|
||||
return path ? path.replace(/^.*[\\\/]/, '') : '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the last url
|
||||
* @param {String} url
|
||||
*/
|
||||
FileRetriever.prototype.setUrl = function (url) {
|
||||
this.url = url;
|
||||
if (this.hash) {
|
||||
this.hash.setValue('url', url);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get last filename
|
||||
* @return {String} filename
|
||||
*/
|
||||
FileRetriever.prototype.getFilename = function () {
|
||||
if (this.hash) {
|
||||
var url = this.hash.getValue('url');
|
||||
if (url) {
|
||||
return this._getFilename(url) || this.defaultFilename;
|
||||
}
|
||||
}
|
||||
return this.defaultFilename;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the last url
|
||||
* @return {String | undefined} url
|
||||
*/
|
||||
FileRetriever.prototype.getUrl = function () {
|
||||
if (this.hash) {
|
||||
var url = this.hash.getValue('url');
|
||||
if (url) {
|
||||
this.url = url;
|
||||
}
|
||||
}
|
||||
return this.url;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove last url from hash
|
||||
*/
|
||||
FileRetriever.prototype.removeUrl = function () {
|
||||
if (this.hash) {
|
||||
var url = this.hash.removeValue('url');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Load a url
|
||||
* @param {String} url The url to be retrieved
|
||||
* @param {function} callback Callback method, called with parameters:
|
||||
* {Error} error
|
||||
* {string} data
|
||||
*/
|
||||
FileRetriever.prototype.loadUrl = function (url, callback) {
|
||||
// set current filename (will be used when saving a file again)
|
||||
this.setUrl(url);
|
||||
|
||||
// try to fetch to the url directly (may result in a cross-domain error)
|
||||
var scriptUrl = this.scriptUrl;
|
||||
ajax.get(url, function(data, status) {
|
||||
if (status == 200) {
|
||||
// success. great. no cross-domain error
|
||||
callback(null, data);
|
||||
}
|
||||
else {
|
||||
// cross-domain error (or other). retrieve the url via the server
|
||||
var indirectUrl = scriptUrl + '?url=' + encodeURIComponent(url);
|
||||
var err;
|
||||
ajax.get(indirectUrl, function(data, status) {
|
||||
if (status == 200) {
|
||||
callback(null, data);
|
||||
}
|
||||
else if (status == 404) {
|
||||
console.log('Error: url "' + url + '" not found', status, data);
|
||||
err = new Error('Error: url "' + url + '" not found');
|
||||
callback(err, null);
|
||||
}
|
||||
else {
|
||||
console.log('Error: failed to load url "' + url + '"', status, data);
|
||||
err = new Error('Error: failed to load url "' + url + '"');
|
||||
callback(err, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Load a file from disk.
|
||||
* A file explorer will be opened to select a file and press ok.
|
||||
* In case of Internet Explorer, an upload form will be shown where the
|
||||
* user has to select a file via a file explorer after that click load.
|
||||
* @param {function} callback Callback method, called with parameters:
|
||||
* {Error} error
|
||||
* {string} data
|
||||
*/
|
||||
FileRetriever.prototype.loadFile = function (callback) {
|
||||
this.removeUrl();
|
||||
|
||||
var isIE = (navigator.appName == 'Microsoft Internet Explorer');
|
||||
if (!isIE) {
|
||||
// immediate file upload
|
||||
this.loadCallback = callback || function () {};
|
||||
this.dom.file.click();
|
||||
}
|
||||
else {
|
||||
// immediate file upload not supported in IE thanks to all the
|
||||
// security limitations. A form is needed with a manual submit.
|
||||
this.loadFileDialog(callback);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Show a dialog to select and load a file.
|
||||
* Needed to load files on Internet Explorer.
|
||||
* @param {function} callback Callback method, called with parameters:
|
||||
* {Error} error
|
||||
* {String} data
|
||||
*/
|
||||
FileRetriever.prototype.loadFileDialog = function (callback) {
|
||||
this.removeUrl();
|
||||
this.loadCallback = callback;
|
||||
|
||||
this.prompt({
|
||||
title: 'Open file',
|
||||
titleSubmit: 'Open',
|
||||
inputType: 'file',
|
||||
inputName: 'file',
|
||||
formAction: this.scriptUrl,
|
||||
formMethod: 'POST',
|
||||
formTarget: this.dom.uploadIframe.name
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Show a dialog to select and load an url.
|
||||
* @param {function} callback Callback method, called with parameters:
|
||||
* {Error} error
|
||||
* {String} data
|
||||
*/
|
||||
FileRetriever.prototype.loadUrlDialog = function (callback) {
|
||||
var me = this;
|
||||
this.prompt({
|
||||
title: 'Open url',
|
||||
titleSubmit: 'Open',
|
||||
inputType: 'text',
|
||||
inputName: 'url',
|
||||
inputDefault: this.getUrl(),
|
||||
callback: function (url) {
|
||||
if (url) {
|
||||
me.loadUrl(url, callback);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Show a prompt.
|
||||
* The propmt can either:
|
||||
* - Post a form when formTarget, formAction, and formMethod are provided
|
||||
* - Call the callback method "callback" with the entered value as parameter.
|
||||
* This happens when a callback parameter is provided.
|
||||
* @param {Object} params Available parameters:
|
||||
* {String} title
|
||||
* {String} titleSubmit
|
||||
* {String} titleCancel
|
||||
* {String} inputType
|
||||
* {String} inputName
|
||||
* {String} inputDefault
|
||||
* {String} formTarget
|
||||
* {String} formAction
|
||||
* {String} formMethod
|
||||
* {function} callback
|
||||
*/
|
||||
FileRetriever.prototype.prompt = function (params) {
|
||||
var removeDialog = function () {
|
||||
// remove the form
|
||||
if (background.parentNode) {
|
||||
background.parentNode.removeChild(background);
|
||||
}
|
||||
if (overlay.parentNode) {
|
||||
overlay.parentNode.removeChild(overlay);
|
||||
}
|
||||
|
||||
JSONEditor.Events.removeEventListener(document, 'keydown', onKeyDown);
|
||||
};
|
||||
|
||||
var onCancel = function () {
|
||||
removeDialog();
|
||||
if(params.callback) {
|
||||
params.callback(null);
|
||||
}
|
||||
};
|
||||
|
||||
var onKeyDown = JSONEditor.Events.addEventListener(document, 'keydown', function (event) {
|
||||
event = event || window.event;
|
||||
var keynum = event.which || event.keyCode;
|
||||
if (keynum == 27) { // ESC
|
||||
onCancel();
|
||||
JSONEditor.Events.preventDefault(event);
|
||||
JSONEditor.Events.stopPropagation(event);
|
||||
}
|
||||
});
|
||||
|
||||
var overlay = document.createElement('div');
|
||||
overlay.className = 'fileretriever-overlay';
|
||||
document.body.appendChild(overlay);
|
||||
|
||||
var form = document.createElement('form');
|
||||
form.className = 'fileretriever-form';
|
||||
form.target = params.formTarget || '';
|
||||
form.action = params.formAction || '';
|
||||
form.method = params.formMethod || 'POST';
|
||||
form.enctype = 'multipart/form-data';
|
||||
form.encoding = 'multipart/form-data'; // needed for IE8 and older
|
||||
form.onsubmit = function () {
|
||||
if (field.value) {
|
||||
setTimeout(function () {
|
||||
// remove after the submit has taken place!
|
||||
removeDialog();
|
||||
}, 0);
|
||||
if (params.callback) {
|
||||
params.callback(field.value);
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
alert('Enter a ' + params.inputName + ' first...');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
var title = document.createElement('div');
|
||||
title.className = 'fileretriever-title';
|
||||
title.appendChild(document.createTextNode(params.title || 'Dialog'));
|
||||
form.appendChild(title);
|
||||
|
||||
var field = document.createElement('input');
|
||||
field.className = 'fileretriever-field';
|
||||
field.type = params.inputType || 'text';
|
||||
field.name = params.inputName || 'text';
|
||||
field.value = params.inputDefault || '';
|
||||
|
||||
var contents = document.createElement('div');
|
||||
contents.className = 'fileretriever-contents';
|
||||
contents.appendChild(field);
|
||||
form.appendChild(contents);
|
||||
|
||||
var cancel = document.createElement('input');
|
||||
cancel.className = 'fileretriever-cancel';
|
||||
cancel.type = 'button';
|
||||
cancel.value = params.titleCancel || 'Cancel';
|
||||
cancel.onclick = onCancel;
|
||||
|
||||
var submit = document.createElement('input');
|
||||
submit.className = 'fileretriever-submit';
|
||||
submit.type = 'submit';
|
||||
submit.value = params.titleSubmit || 'Ok';
|
||||
|
||||
var buttons = document.createElement('div');
|
||||
buttons.className = 'fileretriever-buttons';
|
||||
buttons.appendChild(cancel);
|
||||
buttons.appendChild(submit);
|
||||
form.appendChild(buttons);
|
||||
|
||||
var border = document.createElement('div');
|
||||
border.className = 'fileretriever-border';
|
||||
border.appendChild(form);
|
||||
|
||||
var background = document.createElement('div');
|
||||
background.className = 'fileretriever-background';
|
||||
background.appendChild(border);
|
||||
document.body.appendChild(background);
|
||||
|
||||
field.focus();
|
||||
field.select();
|
||||
};
|
||||
|
||||
/**
|
||||
* Save data to disk
|
||||
* @param {String} data
|
||||
* @param {function} [callback] Callback when the file is saved, called
|
||||
* with parameter:
|
||||
* {Error} error
|
||||
*/
|
||||
FileRetriever.prototype.saveFile = function (data, callback) {
|
||||
callback = callback || function () {};
|
||||
|
||||
// create an anchor to save files to disk (if supported by the browser)
|
||||
var a = document.createElement('a');
|
||||
if (this.options.html5 && a.download != undefined) {
|
||||
// save file directly using a data URL
|
||||
a.href = 'data:application/json;charset=utf-8,' + encodeURIComponent(data);
|
||||
a.download = this.getFilename();
|
||||
a.click();
|
||||
callback()
|
||||
}
|
||||
else {
|
||||
// save file by uploading it to the server and then downloading
|
||||
// it via an iframe
|
||||
var me = this;
|
||||
if (data.length < this.options.maxSize) {
|
||||
ajax.post(me.scriptUrl, data, function(id, status) {
|
||||
if (status == 200) {
|
||||
var iframe = me.dom.downloadIframe;
|
||||
iframe.src = me.scriptUrl + '?id=' + id + '&filename=' + me.getFilename();
|
||||
callback();
|
||||
// TODO: give a callback after the file is saved (iframe load?), not before
|
||||
}
|
||||
else {
|
||||
callback(new Error('Error saving file'));
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
callback(new Error('Maximum allowed file size exceeded (' +
|
||||
this.options.maxSize + ' bytes)'));
|
||||
}
|
||||
}
|
||||
};
|
|
@ -0,0 +1,114 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Script to load and save files from the Javascript client to disk and url.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* POST file.php with a JSON document as body
|
||||
* Will store the JSON document on disk and return the id of the document.
|
||||
*
|
||||
* POST file.php with a JSON document with name "file" as body multipart/form-data
|
||||
* Will store the JSON document on disk and return the id of the document.
|
||||
*
|
||||
* GET file.php?url=....
|
||||
* Will fetch the url and return it (resolves cross-domain security issues)
|
||||
*
|
||||
* GET file.php?id=...
|
||||
* GET file.php?id=...&filename=...
|
||||
* Will return the file with the id, and remove the file from disk.
|
||||
* Optionally specify a filename for the download. Default is 'document.json'
|
||||
*/
|
||||
|
||||
// TODO: neatly handle exceeding of the max size
|
||||
$tmp = 'tmp'; // directory for temporarily storing the files
|
||||
|
||||
$method = $_SERVER['REQUEST_METHOD'];
|
||||
|
||||
// make temporary directory to store the file (if not existing)
|
||||
if (!is_dir(getcwd() . '/' . $tmp)) {
|
||||
mkdir(getcwd() . '/' . $tmp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a filename from given id
|
||||
* @param {String} id id of the file
|
||||
* @return {String} filename path to the file
|
||||
*/
|
||||
function getFilename($id) {
|
||||
global $tmp;
|
||||
return "$tmp/$id";
|
||||
}
|
||||
|
||||
if ($method == 'GET') {
|
||||
$filename = isset($_GET['filename']) ? $_GET['filename'] : 'document.json';
|
||||
if (isset($_GET['url'])) {
|
||||
// download a file from url and return the file
|
||||
$url = $_GET['url'];
|
||||
$body = file_get_contents($url);
|
||||
if ($body != false) {
|
||||
header("Content-Disposition: attachment; filename=\"$filename\"");
|
||||
header('Content-type: application/json');
|
||||
echo $body;
|
||||
}
|
||||
else {
|
||||
header('HTTP/1.1 404 Not Found');
|
||||
}
|
||||
}
|
||||
else if (isset($_GET['id'])) {
|
||||
// retrieve the file with given id from disk, return it,
|
||||
// and remove it from disk
|
||||
$id = $_GET['id'];
|
||||
$body = file_get_contents(getFilename($id));
|
||||
if ($body !== false) {
|
||||
header("Content-Disposition: attachment; filename=\"$filename\"");
|
||||
header('Content-type: application/json');
|
||||
echo $body;
|
||||
unlink(getFilename($id));
|
||||
}
|
||||
else {
|
||||
header('HTTP/1.1 404 Not Found');
|
||||
}
|
||||
}
|
||||
else {
|
||||
// TODO: error
|
||||
}
|
||||
}
|
||||
else if ($method == 'POST') {
|
||||
// retrieve the data, save it on disk with a random id,
|
||||
// and return the id.
|
||||
|
||||
if (isset($_FILES['file'])) {
|
||||
// read body from uploaded form
|
||||
$file = $_FILES['file'];
|
||||
$id = uniqid();
|
||||
$filename = getFilename($id);
|
||||
move_uploaded_file($file['tmp_name'], $filename);
|
||||
echo $id;
|
||||
}
|
||||
else {
|
||||
// read raw body from post request
|
||||
$body = @file_get_contents('php://input');
|
||||
if ($body === false) {
|
||||
$body = '';
|
||||
}
|
||||
$id = uniqid();
|
||||
file_put_contents(getFilename($id), $body);
|
||||
echo $id;
|
||||
}
|
||||
}
|
||||
|
||||
// cleanup files older than 1 hour
|
||||
// http://stackoverflow.com/q/6411451/1262753
|
||||
if ($dir = opendir($tmp)) {
|
||||
$now = time();
|
||||
while (false !== ($file = readdir($dir))) {
|
||||
$filename = "$tmp/$file";
|
||||
if (is_file($filename) && filemtime($filename) <= ($now - 60 * 60) ) {
|
||||
unlink($filename);
|
||||
}
|
||||
}
|
||||
closedir($dir);
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,133 @@
|
|||
|
||||
/**
|
||||
* @prototype Hash
|
||||
* This prototype contains methods to manipulate the hash of the web page
|
||||
*/
|
||||
function Hash() {}
|
||||
|
||||
/**
|
||||
* get an object value with all parameters in the hash
|
||||
* @return {Object} query object containing key/values
|
||||
*/
|
||||
Hash.prototype.getQuery = function () {
|
||||
var hash = window.location.hash.substring(1); // skip the # character
|
||||
var params = hash.split('&');
|
||||
var query = {};
|
||||
for (var i = 0, iMax = params.length; i < iMax; i++) {
|
||||
var keyvalue = params[i].split('=');
|
||||
if (keyvalue.length == 2) {
|
||||
var key = decodeURIComponent(keyvalue[0]);
|
||||
var value = decodeURIComponent(keyvalue[1]);
|
||||
query[key] = value;
|
||||
}
|
||||
}
|
||||
return query;
|
||||
};
|
||||
|
||||
/**
|
||||
* Register a callback function which will be called when the hash of the web
|
||||
* page changes.
|
||||
* @param {String} key
|
||||
* @param {function} callback Will be called with the new value as parameter
|
||||
*/
|
||||
Hash.prototype.onChange = function (key, callback) {
|
||||
this.prevHash = '';
|
||||
var me = this;
|
||||
if (!me.callbacks) {
|
||||
me.callbacks = [];
|
||||
}
|
||||
me.callbacks.push({
|
||||
'key': key,
|
||||
'value': undefined,
|
||||
'callback': callback
|
||||
});
|
||||
|
||||
function checkForChanges() {
|
||||
for (var i = 0; i < me.callbacks.length; i++) {
|
||||
var obj = me.callbacks[i];
|
||||
var value = me.getValue(obj.key);
|
||||
var changed = (value !== obj.value);
|
||||
obj.value = value;
|
||||
if (changed) {
|
||||
obj.callback(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// source: http://stackoverflow.com/questions/2161906/handle-url-anchor-change-event-in-js
|
||||
if ('onhashchange' in window) {
|
||||
window.onhashchange = function () {
|
||||
checkForChanges();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// onhashchange event not supported
|
||||
me.prevHash = window.location.hash;
|
||||
window.setInterval(function () {
|
||||
var hash = window.location.hash;
|
||||
if (hash != me.prevHash) {
|
||||
me.prevHash = hash;
|
||||
checkForChanges();
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Set hash parameters
|
||||
* @param {Object} query object with strings
|
||||
*/
|
||||
Hash.prototype.setQuery = function (query) {
|
||||
var hash = '';
|
||||
|
||||
for (var key in query) {
|
||||
if (query.hasOwnProperty(key)) {
|
||||
var value = query[key];
|
||||
if (value != undefined) {
|
||||
if (hash.length) {
|
||||
hash += '&';
|
||||
}
|
||||
hash += encodeURIComponent(key);
|
||||
hash += '=';
|
||||
hash += encodeURIComponent(query[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.location.hash = (hash.length ? ('#' + hash) : '');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve a parameter value from the hash
|
||||
* @param {String} key
|
||||
* @return {String | undefined} value undefined when the value is not found
|
||||
*/
|
||||
Hash.prototype.getValue = function (key) {
|
||||
var query = this.getQuery();
|
||||
return query[key];
|
||||
};
|
||||
|
||||
/**
|
||||
* Set an hash parameter
|
||||
* @param {String} key
|
||||
* @param {String} value
|
||||
*/
|
||||
Hash.prototype.setValue = function (key, value) {
|
||||
var query = this.getQuery();
|
||||
query[key] = value;
|
||||
this.setQuery(query);
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove an hash parameter
|
||||
* @param {String} key
|
||||
*/
|
||||
Hash.prototype.removeValue = function (key) {
|
||||
var query = this.getQuery();
|
||||
if (query[key]) {
|
||||
delete query[key];
|
||||
this.setQuery(query);
|
||||
}
|
||||
};
|
Before Width: | Height: | Size: 187 B After Width: | Height: | Size: 187 B |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 601 B After Width: | Height: | Size: 601 B |
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
@ -37,7 +37,7 @@
|
|||
Copyright (C) 2011-2012 Jos de Jong, http://jsoneditoronline.org
|
||||
|
||||
@author Jos de Jong, <wjosdejong@gmail.com>
|
||||
@date 2012-10-19
|
||||
@date 2012-10-31
|
||||
-->
|
||||
|
||||
<meta name="description" content="JSON Editor Online is a web-based tool to view, edit, and format JSON. It shows your data side by side in a clear, editable treeview and in formatted plain text.">
|
||||
|
@ -46,23 +46,50 @@
|
|||
|
||||
<link rel="shortcut icon" href="favicon.ico">
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="interface/interface.css">
|
||||
<link rel="stylesheet" type="text/css" href="jsoneditor/jsoneditor-min.css">
|
||||
<link rel="stylesheet" type="text/css" href="app-min.css">
|
||||
<link rel="stylesheet" type="text/css" href="lib/jsoneditor/jsoneditor-min.css">
|
||||
<!-- TODO: droid font
|
||||
<link href='http://fonts.googleapis.com/css?family=Droid+Sans+Mono' rel='stylesheet' type='text/css'>
|
||||
-->
|
||||
|
||||
<script type="text/javascript" src="jsoneditor/jsoneditor-min.js"></script>
|
||||
<script type="text/javascript" src="interface/interface.js"></script>
|
||||
<script type="text/javascript" src="lib/jsoneditor/jsoneditor-min.js"></script>
|
||||
<script type="text/javascript" src="app-min.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="header">
|
||||
<a href="http://jsoneditoronline.org" class="header">
|
||||
<img alt="JSON Editor Online" title="JSON Editor Online" src="interface/img/logo.png" id="logo">
|
||||
<img alt="JSON Editor Online" title="JSON Editor Online" src="img/logo.png" id="logo">
|
||||
</a>
|
||||
|
||||
<div id="menu">
|
||||
<ul>
|
||||
<li>
|
||||
<a id="clear" title="Clear contents">Clear</a>
|
||||
</li>
|
||||
<li>
|
||||
<a id="open" title="Open file from disk">
|
||||
Open
|
||||
<span id="openMenuButton" title="Open file from disk or url">
|
||||
▼
|
||||
</span>
|
||||
</a>
|
||||
<ul id="openMenu">
|
||||
<li>
|
||||
<a id="menuOpenFile" title="Open file from disk">Open file</a>
|
||||
</li>
|
||||
<li>
|
||||
<a id="menuOpenUrl" title="Open file from url">Open url</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a id="save" title="Save file to disk">Save</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- TODO: info, links, faq -->
|
||||
<!--
|
||||
<div class="info" style="display:none;">
|
||||
|
@ -99,14 +126,14 @@
|
|||
<div id="jsoneditor"></div>
|
||||
|
||||
<script type="text/javascript">
|
||||
main.load();
|
||||
main.resize();
|
||||
app.load();
|
||||
app.resize();
|
||||
</script>
|
||||
|
||||
<div id="ad" title="advertisement" >
|
||||
<div id="adInfo">
|
||||
ADVERTISEMENT<br>
|
||||
<a class="adInfo" href="javascript: main.hideAds();">hide for now</a><br>
|
||||
<a class="adInfo" href="javascript: app.hideAds();">hide for now</a><br>
|
||||
</div>
|
||||
|
||||
<div class="adSpace"></div>
|
||||
|
@ -136,15 +163,17 @@
|
|||
•
|
||||
<a href="https://github.com/wjosdejong/jsoneditoronline" target="_blank" class="footer">Sourcecode</a>
|
||||
•
|
||||
<a href="datapolicy.txt" target="_blank" class="footer">Data policy</a>
|
||||
•
|
||||
<a href="NOTICE" target="_blank" class="footer">Copyright 2011-2012 Jos de Jong</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
main.resize();
|
||||
app.resize();
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="interface/lib/jsonlint/jsonlint.js"></script>
|
||||
<script type="text/javascript" src="lib/jsonlint/jsonlint.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,62 @@
|
|||
JSON Lint
|
||||
=========
|
||||
|
||||
A pure [JavaScript version](http://zaach.github.com/jsonlint/) of the service provided at [jsonlint.com](http://jsonlint.com).
|
||||
|
||||
## Command line interface
|
||||
Install jsonlint with npm to use the command line interface:
|
||||
|
||||
npm install jsonlint -g
|
||||
|
||||
Validate a file like so:
|
||||
|
||||
jsonlint myfile.json
|
||||
|
||||
or pipe input into stdin:
|
||||
|
||||
cat myfile.json | jsonlint
|
||||
|
||||
jsonlint will either report a syntax error with details or pretty print the source if it is valid.
|
||||
|
||||
### Options
|
||||
|
||||
$ jsonlint -h
|
||||
|
||||
usage: jsonlint <file> [options]
|
||||
|
||||
file file to parse; otherwise uses stdin
|
||||
|
||||
options:
|
||||
-v, --version print version and exit
|
||||
-s, --sort-keys sort object keys
|
||||
-i, --in-place overwrite the file
|
||||
-t CHAR, --indent CHAR character(s) to use for indentation
|
||||
-c, --compact compact error display
|
||||
-V, --validate a JSON schema to use for validation
|
||||
-e, --environment which specification of JSON Schema the validation file uses
|
||||
|
||||
|
||||
## Module interface
|
||||
|
||||
I'm not sure why you wouldn't use the built in `JSON.parse` but you can use jsonlint from a CommonJS module:
|
||||
|
||||
var jsonlint = require("jsonlint");
|
||||
|
||||
jsonlint.parse('{"creative?": false}');
|
||||
|
||||
It returns the parsed object or throws an `Error`.
|
||||
|
||||
## Vim Plugins
|
||||
|
||||
* [Syntastic](http://www.vim.org/scripts/script.php?script_id=2736)
|
||||
* [sourcebeautify](http://www.vim.org/scripts/script.php?script_id=4079)
|
||||
|
||||
## MIT License
|
||||
|
||||
Copyright (C) 2012 Zachary Carter
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,150 @@
|
|||
/**
|
||||
* Utility to display notifications and error messages.
|
||||
* The messages are displayed on the top center of the web page
|
||||
* @constructor Notifications
|
||||
*/
|
||||
function Notifications () {
|
||||
this.dom = {};
|
||||
this.notifications = [];
|
||||
|
||||
// TODO: attach the event as soon as there are one or multiple messages displayed,
|
||||
// remove it as soon as they are all gone
|
||||
var me = this;
|
||||
JSONEditor.Events.addEventListener(document, 'keydown', function (event) {
|
||||
me.onKeyDown(event);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a notification
|
||||
* @param {String} message
|
||||
* @return {Element} messageObject
|
||||
*/
|
||||
Notifications.prototype.showNotification = function (message) {
|
||||
return this.showMessage({
|
||||
type: 'notification',
|
||||
message: message,
|
||||
closeButton: false
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Show an error message
|
||||
* @param {Error} error
|
||||
* @return {Element} messageObject
|
||||
*/
|
||||
Notifications.prototype.showError = function (error) {
|
||||
return this.showMessage({
|
||||
type: 'error',
|
||||
message: (error.message || error.toString()),
|
||||
closeButton: true
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Show a message
|
||||
* @param {Object} params Available parameters:
|
||||
* {String} message
|
||||
* {String} type 'error', 'notification'
|
||||
* {Boolean} closeButton
|
||||
* @return {Element} messageObject
|
||||
*/
|
||||
Notifications.prototype.showMessage = function (params) {
|
||||
var frame = this.dom.frame;
|
||||
if (!frame) {
|
||||
var width = 500;
|
||||
var top = 5;
|
||||
var windowWidth = document.body.offsetWidth || window.innerWidth;
|
||||
frame = document.createElement('div');
|
||||
frame.style.position = 'absolute';
|
||||
frame.style.left = (windowWidth - width) / 2 + 'px';
|
||||
frame.style.width = width + 'px';
|
||||
frame.style.top = top + 'px';
|
||||
document.body.appendChild(frame);
|
||||
this.dom.frame = frame;
|
||||
}
|
||||
|
||||
var type = params.type || 'notification';
|
||||
var closeable = (params.closeButton !== false);
|
||||
var divMessage = document.createElement('div');
|
||||
divMessage.className = type;
|
||||
divMessage.type = type;
|
||||
divMessage.closeable = closeable;
|
||||
divMessage.style.position = 'relative';
|
||||
frame.appendChild(divMessage);
|
||||
|
||||
var table = document.createElement('table');
|
||||
table.style.width = '100%';
|
||||
divMessage.appendChild(table);
|
||||
var tbody = document.createElement('tbody');
|
||||
table.appendChild(tbody);
|
||||
var tr = document.createElement('tr');
|
||||
tbody.appendChild(tr);
|
||||
|
||||
var tdMessage = document.createElement('td');
|
||||
tdMessage.innerHTML = params.message || '';
|
||||
tr.appendChild(tdMessage);
|
||||
|
||||
if (closeable) {
|
||||
var tdClose = document.createElement('td');
|
||||
tdClose.style.textAlign = 'right';
|
||||
tdClose.style.verticalAlign = 'top';
|
||||
tr.appendChild(tdClose);
|
||||
|
||||
var closeDiv = document.createElement('button');
|
||||
closeDiv.innerHTML = '×';
|
||||
closeDiv.title = 'Close message (ESC)';
|
||||
tdClose.appendChild(closeDiv);
|
||||
var me = this;
|
||||
closeDiv.onclick = function () {
|
||||
me.removeMessage(divMessage);
|
||||
}
|
||||
}
|
||||
|
||||
return divMessage;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a message from the list with messages
|
||||
* @param {Element} [message] The HTML DOM of a message
|
||||
* If undefined, the first closeable message will
|
||||
* closed.
|
||||
*/
|
||||
Notifications.prototype.removeMessage = function (message) {
|
||||
var frame = this.dom.frame;
|
||||
if (!message && frame) {
|
||||
// find the first closable message in the list with displayed messages
|
||||
var child = frame.firstChild;
|
||||
while (child && !child.closeable) {
|
||||
child = child.nextSibling;
|
||||
}
|
||||
if (child && child.closeable) {
|
||||
message = child;
|
||||
}
|
||||
}
|
||||
|
||||
if (message && message.parentNode == frame) {
|
||||
message.parentNode.removeChild(message);
|
||||
}
|
||||
|
||||
if (frame && frame.childNodes.length == 0) {
|
||||
frame.parentNode.removeChild(frame);
|
||||
delete this.dom.frame;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle key down event.
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
Notifications.prototype.onKeyDown = function (event) {
|
||||
event = event || window.event;
|
||||
var keynum = event.which || event.keyCode;
|
||||
if (keynum == 27) { // ESC
|
||||
// remove the oldest open and closeable message
|
||||
this.removeMessage();
|
||||
JSONEditor.Events.preventDefault(event);
|
||||
JSONEditor.Events.stopPropagation(event);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,138 @@
|
|||
/**
|
||||
* A splitter control.
|
||||
* Turns an existing HTML element into an horizontal splitter control.
|
||||
* @constructor Splitter
|
||||
* @param {Object} params Available parameters:
|
||||
* {Element} container HTML container representing
|
||||
* the splitter
|
||||
* {function} [change] Callback method called when
|
||||
* the splitter value has changed.
|
||||
* The callback is called with
|
||||
* the new value as parameter
|
||||
*/
|
||||
function Splitter (params) {
|
||||
if (!params || !params.container) {
|
||||
throw new Error('params.container undefined in Splitter constructor');
|
||||
}
|
||||
|
||||
var me = this;
|
||||
JSONEditor.Events.addEventListener(params.container, "mousedown", function (event) {
|
||||
me.onMouseDown(event);
|
||||
});
|
||||
|
||||
this.container = params.container;
|
||||
this.onChange = (params.change) ? params.change : function () {};
|
||||
this.params = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle mouse down event. Start dragging the splitter.
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
Splitter.prototype.onMouseDown = function (event) {
|
||||
var me = this;
|
||||
var leftButtonDown = event.which ? (event.which == 1) : (event.button == 1);
|
||||
if (!leftButtonDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.params.mousedown) {
|
||||
this.params.mousedown = true;
|
||||
this.params.mousemove =
|
||||
JSONEditor.Events.addEventListener(document, 'mousemove', function (event) {
|
||||
me.onMouseMove(event);
|
||||
});
|
||||
this.params.mouseup =
|
||||
JSONEditor.Events.addEventListener(document, 'mouseup', function (event) {
|
||||
me.onMouseUp(event);
|
||||
});
|
||||
this.params.screenX = event.screenX;
|
||||
this.params.value = this.getValue();
|
||||
}
|
||||
JSONEditor.Events.preventDefault(event);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle on mouse move event. Used to drag the splitter
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
Splitter.prototype.onMouseMove = function (event) {
|
||||
var width = (window.innerWidth || document.body.offsetWidth ||
|
||||
document.documentElement.offsetWidth);
|
||||
|
||||
var diff = event.screenX - this.params.screenX;
|
||||
|
||||
var value = this.params.value + diff / width;
|
||||
value = this.setValue(value);
|
||||
|
||||
this.onChange(value);
|
||||
|
||||
JSONEditor.Events.preventDefault(event);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle on mouse up event
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
Splitter.prototype.onMouseUp = function (event) {
|
||||
if (this.params.mousedown) {
|
||||
JSONEditor.Events.removeEventListener(document, 'mousemove', this.params.mousemove);
|
||||
JSONEditor.Events.removeEventListener(document, 'mouseup', this.params.mouseup);
|
||||
this.params.mousemove = undefined;
|
||||
this.params.mouseup = undefined;
|
||||
this.params.mousedown = false;
|
||||
}
|
||||
JSONEditor.Events.preventDefault(event);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set a value for the splitter (UI is not adjusted)
|
||||
* @param {Number} value A number between 0.1 and 0.9
|
||||
* @return {Number} value The stored value
|
||||
*/
|
||||
Splitter.prototype.setValue = function (value) {
|
||||
value = Number(value);
|
||||
if (value < 0.1) {
|
||||
value = 0.1;
|
||||
}
|
||||
if (value > 0.9) {
|
||||
value = 0.9;
|
||||
}
|
||||
|
||||
this.value = value;
|
||||
|
||||
try {
|
||||
localStorage['splitterValue'] = value;
|
||||
}
|
||||
catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the splitter value from local storage
|
||||
* @return {Number} value A value between 0.1 and 0.9
|
||||
*/
|
||||
Splitter.prototype.getValue = function () {
|
||||
var value = this.value;
|
||||
if (value == undefined) {
|
||||
// read from localStorage once
|
||||
try {
|
||||
if (localStorage['splitterValue'] != undefined) {
|
||||
value = Number(localStorage['splitterValue']); // read
|
||||
value = this.setValue(value); // verify and store
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
if (value == undefined) {
|
||||
value = this.setValue(0.5);
|
||||
}
|
||||
return value;
|
||||
};
|
|
@ -36,7 +36,7 @@
|
|||
Copyright (C) 2011-2012 Jos de Jong, http://jsoneditoronline.org
|
||||
|
||||
@author Jos de Jong, <wjosdejong@gmail.com>
|
||||
@date 2012-10-19
|
||||
@date 2012-10-31
|
||||
-->
|
||||
|
||||
<meta name="description" content="JSON Editor Online is a web-based tool to view, edit, and format JSON. It shows your data side by side in a clear, editable treeview and in formatted plain text.">
|
||||
|
@ -45,25 +45,65 @@
|
|||
|
||||
<link rel="shortcut icon" href="favicon.ico">
|
||||
|
||||
<link href="interface/interface.css" rel="stylesheet" type="text/css">
|
||||
<link href="jsoneditor/jsoneditor.css" rel="stylesheet" type="text/css">
|
||||
<link href="app.css" rel="stylesheet" type="text/css">
|
||||
<link href="fileretriever.css" rel="stylesheet" type="text/css">
|
||||
<link href="../../jsoneditor/jsoneditor.css" rel="stylesheet" type="text/css">
|
||||
<!-- TODO: droid font
|
||||
<link href='http://fonts.googleapis.com/css?family=Droid+Sans+Mono' rel='stylesheet' type='text/css'>
|
||||
-->
|
||||
|
||||
<script type="text/javascript" src="jsoneditor/jsoneditor.js"></script>
|
||||
<script type="text/javascript" src="interface/interface.js"></script>
|
||||
<script type="text/javascript" src="interface/lib/jsonlint/jsonlint.js"></script>
|
||||
<script type="text/javascript" src="../../jsoneditor/jsoneditor.js"></script>
|
||||
<script type="text/javascript" src="hash.js"></script>
|
||||
<script type="text/javascript" src="ajax.js"></script>
|
||||
<script type="text/javascript" src="fileretriever.js"></script>
|
||||
<script type="text/javascript" src="notifications.js"></script>
|
||||
<script type="text/javascript" src="splitter.js"></script>
|
||||
<script type="text/javascript" src="app.js"></script>
|
||||
<script type="text/javascript" src="lib/jsonlint/jsonlint.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
div.convert-right {
|
||||
background: url('../../jsoneditor/img/jsoneditor-icons.png') -168px 0;
|
||||
}
|
||||
div.convert-left {
|
||||
background: url('../../jsoneditor/img/jsoneditor-icons.png') -192px 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body spellcheck="false" >
|
||||
<body>
|
||||
|
||||
<div id="header" >
|
||||
<a href="http://jsoneditoronline.org" class="header">
|
||||
<img alt="JSON Editor Online" title="JSON Editor Online" src="interface/img/logo.png" id="logo">
|
||||
<img alt="JSON Editor Online" title="JSON Editor Online" src="img/logo.png" id="logo">
|
||||
</a>
|
||||
|
||||
<div id="menu">
|
||||
<ul>
|
||||
<li>
|
||||
<a id="clear" title="Clear contents">Clear</a>
|
||||
</li>
|
||||
<li>
|
||||
<a id="open" title="Open file from disk">
|
||||
Open
|
||||
<span id="openMenuButton" title="Open file from disk or url">
|
||||
▼
|
||||
</span>
|
||||
</a>
|
||||
<ul id="openMenu">
|
||||
<li>
|
||||
<a id="menuOpenFile" title="Open file from disk">Open file</a>
|
||||
</li>
|
||||
<li>
|
||||
<a id="menuOpenUrl" title="Open file from url">Open url</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a id="save" title="Save file to disk">Save</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- TODO: info, links, faq -->
|
||||
<!--
|
||||
|
@ -101,17 +141,14 @@
|
|||
<div id="jsoneditor"></div>
|
||||
|
||||
<script type="text/javascript">
|
||||
main.load();
|
||||
main.resize();
|
||||
app.load();
|
||||
app.resize();
|
||||
</script>
|
||||
|
||||
<div id="ad" title="advertisement" >
|
||||
<div id="adInfo">
|
||||
ADVERTISEMENT<br>
|
||||
<a class="adInfo" href="javascript: main.hideAds();">hide for now</a><br>
|
||||
<div id="removeAds">
|
||||
<a class="adInfo" href="javascript: main.removeAds()">get rid of the ads</a>
|
||||
</div>
|
||||
<a class="adInfo" href="javascript: app.hideAds();">hide for now</a><br>
|
||||
<div id="chromeAppInfo" style='display: none;'>
|
||||
<br>If you want to get rid of the ads,
|
||||
you can buy the Chrome App.
|
||||
|
@ -148,16 +185,18 @@
|
|||
<div id="footer-inner">
|
||||
<a href="http://jsoneditoronline.org" class="footer">JSON Editor Online 1.6.0</a>
|
||||
•
|
||||
<a href="changelog.txt" target="_blank" class="footer">Changelog</a>
|
||||
<a href="../../changelog.txt" target="_blank" class="footer">Changelog</a>
|
||||
•
|
||||
<a href="https://github.com/wjosdejong/jsoneditoronline" target="_blank" class="footer">Sourcecode</a>
|
||||
•
|
||||
<a href="NOTICE" target="_blank" class="footer">Copyright 2011-2012 Jos de Jong</a>
|
||||
<a href="datapolicy.txt" target="_blank" class="footer">Data policy</a>
|
||||
•
|
||||
<a href="../../NOTICE" target="_blank" class="footer">Copyright 2011-2012 Jos de Jong</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
main.resize();
|
||||
app.resize();
|
||||
</script>
|
||||
|
||||
</body>
|
91
build.xml
|
@ -7,11 +7,12 @@
|
|||
|
||||
<property name="root" location="" />
|
||||
<property name="lib" location="build/lib" />
|
||||
<property name="web" location="build/web" />
|
||||
<property name="web_app" location="build/app/web" />
|
||||
<property name="chrome_app" location="build/app/chrome" />
|
||||
<property name="web_app_src" location="app/web" />
|
||||
<property name="compressor" value="tools/yuicompressor-2.4.7.jar" />
|
||||
|
||||
<target name="minify" description="minify jsoneditor libraries">
|
||||
<target name="minify_lib" description="minify jsoneditor library">
|
||||
<java jar="${compressor}" dir="jsoneditor" fork="true" failonerror="true">
|
||||
<arg value="-o"/>
|
||||
<arg value="jsoneditor-min.js"/>
|
||||
|
@ -24,7 +25,7 @@
|
|||
</java>
|
||||
</target>
|
||||
|
||||
<target name="zip" depends="minify" description="create zipped jsoneditor libraries">
|
||||
<target name="zip_lib" depends="minify_lib" description="create zip file with the jsoneditor library">
|
||||
<mkdir dir="${lib}" />
|
||||
|
||||
<!-- create a zip file with non-minified jsoneditor -->
|
||||
|
@ -56,41 +57,75 @@
|
|||
</zip>
|
||||
</target>
|
||||
|
||||
<target name="build_site" depends="minify" description="copy all files for the site to the build directory">
|
||||
<delete dir="${web}" />
|
||||
<mkdir dir="${web}" />
|
||||
<copy file="LICENSE" todir="${web}" />
|
||||
<copy file="NOTICE" todir="${web}" />
|
||||
<copy file="README.md" todir="${web}" />
|
||||
<copy file="robots.txt" todir="${web}" />
|
||||
<copy file="changelog.txt" todir="${web}" />
|
||||
<copy file="index.html" todir="${web}" />
|
||||
<copy file="favicon.ico" todir="${web}" />
|
||||
<copy file="interface/interface.js" todir="${web}/interface" />
|
||||
<copy file="interface/interface.css" todir="${web}/interface" />
|
||||
<copy file="interface/lib/jsonlint/jsonlint.js" todir="${web}/interface/lib/jsonlint" />
|
||||
<copy file="interface/img/logo.png" todir="${web}/interface/img" />
|
||||
<copy file="interface/img/header_background.png" todir="${web}/interface/img" />
|
||||
<copy file="jsoneditor/jsoneditor-min.js" todir="${web}/jsoneditor" />
|
||||
<copy file="jsoneditor/jsoneditor-min.css" todir="${web}/jsoneditor" />
|
||||
<copy file="jsoneditor/img/jsoneditor-icons.png" todir="${web}/jsoneditor/img" />
|
||||
<target name="build_web_app" depends="minify_lib" description="copy all files for the web application to the build directory">
|
||||
<delete dir="${web_app}" />
|
||||
<mkdir dir="${web_app}" />
|
||||
|
||||
<!-- concatenate the javascript and css app files -->
|
||||
<concat destfile="${web_app}/app.js">
|
||||
<fileset dir="${web_app_src}" includes="hash.js"/>
|
||||
<fileset dir="${web_app_src}" includes="ajax.js"/>
|
||||
<fileset dir="${web_app_src}" includes="fileretriever.js"/>
|
||||
<fileset dir="${web_app_src}" includes="notifications.js"/>
|
||||
<fileset dir="${web_app_src}" includes="splitter.js"/>
|
||||
<fileset dir="${web_app_src}" includes="app.js"/>
|
||||
</concat>
|
||||
<concat destfile="${web_app}/app.css">
|
||||
<fileset dir="${web_app_src}" includes="fileretriever.css"/>
|
||||
<fileset dir="${web_app_src}" includes="app.css"/>
|
||||
</concat>
|
||||
|
||||
<!-- copy all other files and libraries-->
|
||||
<copy file="changelog.txt" todir="${web_app}" />
|
||||
<copy file="README.md" todir="${web_app}" />
|
||||
<copy file="LICENSE" todir="${web_app}" />
|
||||
<copy file="NOTICE" todir="${web_app}" />
|
||||
<copy file="${web_app_src}/robots.txt" todir="${web_app}" />
|
||||
<copy file="${web_app_src}/datapolicy.txt" todir="${web_app}" />
|
||||
<copy file="${web_app_src}/index.html" todir="${web_app}" />
|
||||
<copy file="${web_app_src}/favicon.ico" todir="${web_app}" />
|
||||
<copy file="${web_app_src}/fileretriever.php" todir="${web_app}" />
|
||||
<copy file="${web_app_src}/img/logo.png" todir="${web_app}/img" />
|
||||
<copy file="${web_app_src}/img/header_background.png" todir="${web_app}/img" />
|
||||
|
||||
<copy file="${web_app_src}/lib/jsonlint/jsonlint.js" todir="${web_app}/lib/jsonlint" />
|
||||
<copy file="jsoneditor/jsoneditor-min.js" todir="${web_app}/lib/jsoneditor" />
|
||||
<copy file="jsoneditor/jsoneditor-min.css" todir="${web_app}/lib/jsoneditor" />
|
||||
<copy file="jsoneditor/img/jsoneditor-icons.png" todir="${web_app}/lib/jsoneditor/img" />
|
||||
|
||||
|
||||
<!-- minify the javascript files -->
|
||||
<java jar="${compressor}" dir="${web_app}" fork="true" failonerror="true">
|
||||
<arg value="-o"/>
|
||||
<arg value="app-min.js"/>
|
||||
<arg value="app.js"/>
|
||||
</java>
|
||||
<java jar="${compressor}" dir="${web_app}" fork="true" failonerror="true">
|
||||
<arg value="-o"/>
|
||||
<arg value="app-min.css"/>
|
||||
<arg value="app.css"/>
|
||||
</java>
|
||||
|
||||
<!-- delete non-minified app files -->
|
||||
<delete file="${web_app}/app.js" />
|
||||
<delete file="${web_app}/app.css" />
|
||||
</target>
|
||||
|
||||
<target name="build_chrome_app" depends="minify" description="copy all files for the chrome app to the build directory">
|
||||
<target name="build_chrome_app" depends="minify_lib" description="copy and zip all files for the chrome app">
|
||||
<!-- hosted app -->
|
||||
<delete dir="${chrome_app}" />
|
||||
<mkdir dir="${chrome_app}" />
|
||||
<copy file="app/chrome/manifest.json" todir="${chrome_app}" />
|
||||
<copy file="interface/img/icon_16.png" todir="${chrome_app}" />
|
||||
<copy file="interface/img/icon_128.png" todir="${chrome_app}" />
|
||||
</target>
|
||||
<copy file="${web_app_src}/img/icon_16.png" todir="${chrome_app}" />
|
||||
<copy file="${web_app_src}/img/icon_128.png" todir="${chrome_app}" />
|
||||
|
||||
<target name="zip_chrome_app" depends="build_chrome_app" description="zip the chrome application">
|
||||
<zip destfile="build/app/chrome_app.zip">
|
||||
<zip destfile="build/app/chrome.zip">
|
||||
<fileset dir="${chrome_app}" />
|
||||
</zip>
|
||||
|
||||
<delete dir="${chrome_app}" />
|
||||
</target>
|
||||
|
||||
<target name="main" depends="minify, zip, build_site, build_chrome_app, zip_chrome_app" />
|
||||
<target name="main" depends="minify_lib, zip_lib, build_web_app, build_chrome_app" />
|
||||
|
||||
</project>
|
|
@ -1,2 +1 @@
|
|||
web
|
||||
app
|
|
@ -4,7 +4,9 @@ http://jsoneditoronline.org
|
|||
|
||||
<not yet released>, version 1.6.0
|
||||
|
||||
- Improved error messages, using JSONLint.
|
||||
- Added feature to the web application to load and save files to disk and from
|
||||
url.
|
||||
- Improved error messages in the web application using JSONLint.
|
||||
- Made the web application pass the W3C markup validation service.
|
||||
|
||||
|
||||
|
|
|
@ -1,431 +0,0 @@
|
|||
/**
|
||||
* @file interface.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.
|
||||
*
|
||||
* Copyright (C) 2011-2012 Jos de Jong, http://jsoneditoronline.org
|
||||
*
|
||||
* @author Jos de Jong, <wjosdejong@gmail.com>
|
||||
* @date 2012-10-19
|
||||
*/
|
||||
|
||||
|
||||
var editor = null;
|
||||
var formatter = null;
|
||||
|
||||
var main = {};
|
||||
|
||||
/**
|
||||
* Get the JSON from the formatter and load it in the editor
|
||||
*/
|
||||
main.formatterToEditor = function() {
|
||||
try {
|
||||
editor.set(formatter.get());
|
||||
}
|
||||
catch (err) {
|
||||
main.showError(err);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the JSON from the editor and load it into the formatter
|
||||
*/
|
||||
main.editorToFormatter = function () {
|
||||
try {
|
||||
formatter.set(editor.get());
|
||||
}
|
||||
catch (err) {
|
||||
main.showError(err);
|
||||
}
|
||||
};
|
||||
|
||||
main.eventParams = {};
|
||||
|
||||
/**
|
||||
* Handle key down event.
|
||||
* @param {Event} event
|
||||
*/
|
||||
main.onKeyDown = function (event) {
|
||||
event = event || window.event;
|
||||
var keynum = event.which || event.keyCode;
|
||||
if (keynum == 27) { // ESC
|
||||
// remove the oldest open error message
|
||||
main.removeError();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle mouse down event. Start dragging the splitter.
|
||||
* @param {Event} event
|
||||
*/
|
||||
main.onMouseDown = function (event) {
|
||||
var leftButtonDown = event.which ? (event.which == 1) : (event.button == 1);
|
||||
if (!leftButtonDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!main.eventParams.mousedown) {
|
||||
main.eventParams.mousedown = true;
|
||||
main.eventParams.mousemove =
|
||||
JSONEditor.Events.addEventListener(document, 'mousemove', main.onMouseMove);
|
||||
main.eventParams.mouseup =
|
||||
JSONEditor.Events.addEventListener(document, 'mouseup', main.onMouseUp);
|
||||
main.eventParams.screenX = event.screenX;
|
||||
main.eventParams.splitterValue = main.getSplitterValue();
|
||||
}
|
||||
JSONEditor.Events.preventDefault(event);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle on mouse move event. Used to drag the spitter
|
||||
* @param {Event} event
|
||||
*/
|
||||
main.onMouseMove = function (event) {
|
||||
var width = (window.innerWidth || document.body.offsetWidth ||
|
||||
document.documentElement.offsetWidth);
|
||||
|
||||
var diff = event.screenX - main.eventParams.screenX;
|
||||
|
||||
var value = main.eventParams.splitterValue + diff / width;
|
||||
main.setSplitterValue(value);
|
||||
|
||||
main.resize();
|
||||
|
||||
JSONEditor.Events.preventDefault(event);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle on mouse up event
|
||||
* @param {Event} event
|
||||
*/
|
||||
main.onMouseUp = function (event) {
|
||||
if (main.eventParams.mousedown) {
|
||||
JSONEditor.Events.removeEventListener(document, 'mousemove', main.eventParams.mousemove);
|
||||
JSONEditor.Events.removeEventListener(document, 'mouseup', main.eventParams.mouseup);
|
||||
main.eventParams.mousemove = undefined;
|
||||
main.eventParams.mouseup = undefined;
|
||||
main.eventParams.mousedown = false;
|
||||
}
|
||||
JSONEditor.Events.preventDefault(event);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set a value for the splitter (UI is not adjusted)
|
||||
* @param {Number} value A number between 0.1 and 0.9
|
||||
* @return {Number} value The stored value
|
||||
*/
|
||||
main.setSplitterValue = function (value) {
|
||||
value = Number(value);
|
||||
if (value < 0.1) {
|
||||
value = 0.1;
|
||||
}
|
||||
if (value > 0.9) {
|
||||
value = 0.9;
|
||||
}
|
||||
|
||||
main.splitterValue = value;
|
||||
|
||||
try {
|
||||
localStorage['splitterValue'] = value;
|
||||
}
|
||||
catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the splitter value from local storage
|
||||
* @return {Number} value A value between 0.1 and 0.9
|
||||
*/
|
||||
main.getSplitterValue = function () {
|
||||
var value = main.splitterValue;
|
||||
if (value == undefined) {
|
||||
// read from localStorage once
|
||||
try {
|
||||
if (localStorage['splitterValue'] != undefined) {
|
||||
value = Number(localStorage['splitterValue']); // read
|
||||
value = main.setSplitterValue(value); // verify and store
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
if (value == undefined) {
|
||||
value = main.setSplitterValue(0.5);
|
||||
}
|
||||
if (value == undefined) {
|
||||
value = 0.5;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Load the interface (editor, formatter, splitter)
|
||||
*/
|
||||
main.load = function() {
|
||||
var json = {
|
||||
"name": "John Smith",
|
||||
"age": 32,
|
||||
"employed": true,
|
||||
"address": {
|
||||
"street": "701 First Ave.",
|
||||
"city": "Sunnyvale, CA 95125",
|
||||
"country": "United States"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"name": "Richard",
|
||||
"age": 7
|
||||
},
|
||||
{
|
||||
"name": "Susan",
|
||||
"age": 4
|
||||
},
|
||||
{
|
||||
"name": "James",
|
||||
"age": 3
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
try {
|
||||
// formatter
|
||||
var container = document.getElementById("jsonformatter");
|
||||
formatter = new JSONFormatter(container);
|
||||
formatter.set(json);
|
||||
formatter.onError = function (err) {
|
||||
main.showError(err);
|
||||
};
|
||||
|
||||
// editor
|
||||
container = document.getElementById("jsoneditor");
|
||||
editor = new JSONEditor(container);
|
||||
editor.set(json);
|
||||
|
||||
// splitter
|
||||
var domSplitter = document.getElementById('splitter');
|
||||
|
||||
domSplitter.appendChild(document.createElement('br'));
|
||||
domSplitter.appendChild(document.createElement('br'));
|
||||
domSplitter.appendChild(document.createElement('br'));
|
||||
var toForm = document.createElement('button');
|
||||
toForm.id = 'toForm';
|
||||
toForm.title = 'JSON to Editor';
|
||||
toForm.className = 'convert';
|
||||
toForm.innerHTML = '<div class="convert-right"></div>';
|
||||
toForm.onclick = function () {
|
||||
this.focus();
|
||||
main.formatterToEditor();
|
||||
};
|
||||
domSplitter.appendChild(toForm);
|
||||
domSplitter.appendChild(document.createElement('br'));
|
||||
domSplitter.appendChild(document.createElement('br'));
|
||||
var toJSON = document.createElement('button');
|
||||
toJSON.id = 'toJSON';
|
||||
toJSON.title = 'Editor to JSON';
|
||||
toJSON.className = 'convert';
|
||||
toJSON.innerHTML = '<div class="convert-left"></div>';
|
||||
toJSON.onclick = function () {
|
||||
this.focus();
|
||||
main.editorToFormatter();
|
||||
};
|
||||
domSplitter.appendChild(toJSON);
|
||||
|
||||
JSONEditor.Events.addEventListener(domSplitter, "mousedown", main.onMouseDown);
|
||||
JSONEditor.Events.addEventListener(window, 'keydown', main.onKeyDown);
|
||||
|
||||
// resize
|
||||
JSONEditor.Events.addEventListener(window, 'resize', main.resize);
|
||||
|
||||
|
||||
// TODO: implement a focus method
|
||||
formatter.textarea.focus();
|
||||
|
||||
// TODO: a nicer method to check for changes
|
||||
var formatterLastContent;
|
||||
var editorLastContent;
|
||||
function checkChange () {
|
||||
try {
|
||||
// check for change in formatter
|
||||
var formatterJSON = formatter.get();
|
||||
var formatterContent = JSON.stringify(formatterJSON);
|
||||
if (formatterContent != formatterLastContent) {
|
||||
formatterLastContent = formatterContent;
|
||||
editorLastContent = formatterContent;
|
||||
editor.set(formatterJSON);
|
||||
}
|
||||
else {
|
||||
// check for change in editor
|
||||
var editorJSON = editor.get();
|
||||
var editorContent = JSON.stringify(editorJSON);
|
||||
if (editorContent != editorLastContent) {
|
||||
editorLastContent = editorContent;
|
||||
formatterLastContent = editorContent;
|
||||
formatter.set(editorJSON);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
main.showError(err);
|
||||
}
|
||||
|
||||
setTimeout(checkChange, 1000);
|
||||
}
|
||||
/* TODO: use checkChange
|
||||
checkChange();
|
||||
*/
|
||||
|
||||
// enforce FireFox to not do spell checking on any input field
|
||||
document.body.spellcheck = false;
|
||||
} catch (err) {
|
||||
main.showError(err);
|
||||
}
|
||||
};
|
||||
|
||||
main.resize = function() {
|
||||
var domEditor = document.getElementById('jsoneditor');
|
||||
var domFormatter = document.getElementById('jsonformatter');
|
||||
var domSplitter = document.getElementById('splitter');
|
||||
var domAd = document.getElementById('ad');
|
||||
var domAdInfo = document.getElementById('adInfo');
|
||||
|
||||
var width = window.innerWidth || document.body.offsetWidth || document.documentElement.offsetWidth;
|
||||
var height = window.innerHeight || document.body.offsetHeight || document.documentElement.offsetHeight;
|
||||
var adWidth = domAd ? domAd.clientWidth : 0;
|
||||
var splitterWidth = domSplitter.clientWidth;
|
||||
if (adWidth) {
|
||||
width -= (adWidth + 15); // Not so nice, +15 here for the margin
|
||||
}
|
||||
|
||||
var splitterLeft = width * main.getSplitterValue();
|
||||
|
||||
// resize formatter
|
||||
domFormatter.style.width = Math.round(splitterLeft) + 'px';
|
||||
|
||||
// resize editor
|
||||
// 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...
|
||||
domEditor.style.left = Math.round(splitterLeft + splitterWidth) + 'px';
|
||||
domEditor.style.width = Math.round(width - splitterLeft - splitterWidth - 1) + 'px';
|
||||
//editor.onResize(); // TODO
|
||||
|
||||
// resize ad text
|
||||
if (domAdInfo && domAd) {
|
||||
var infoHeight = domAdInfo.clientHeight;
|
||||
domAd.style.paddingTop = infoHeight + 'px';
|
||||
}
|
||||
};
|
||||
|
||||
main.errorFrame = undefined;
|
||||
|
||||
/**
|
||||
* Show an error message top center of the interface
|
||||
* @param {Error} error
|
||||
*/
|
||||
main.showError = function (error) {
|
||||
if (!error) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!main.errorFrame) {
|
||||
var width = 500;
|
||||
var top = 5;
|
||||
var windowWidth = document.body.offsetWidth || window.innerWidth;
|
||||
main.errorFrame = document.createElement('div');
|
||||
main.errorFrame.style.position = 'absolute';
|
||||
main.errorFrame.style.left = (windowWidth - width) / 2 + 'px';
|
||||
main.errorFrame.style.width = width + 'px';
|
||||
main.errorFrame.style.top = top + 'px';
|
||||
document.body.appendChild(main.errorFrame);
|
||||
}
|
||||
|
||||
var divError = document.createElement('div');
|
||||
divError.className = 'error';
|
||||
divError.style.position = 'relative';
|
||||
main.errorFrame.appendChild(divError);
|
||||
|
||||
var table = document.createElement('table');
|
||||
table.style.width = '100%';
|
||||
divError.appendChild(table);
|
||||
var tbody = document.createElement('tbody');
|
||||
table.appendChild(tbody);
|
||||
var tr = document.createElement('tr');
|
||||
tbody.appendChild(tr);
|
||||
|
||||
var tdMessage = document.createElement('td');
|
||||
tdMessage.innerHTML = error.message || error.toString();
|
||||
tr.appendChild(tdMessage);
|
||||
|
||||
var tdClose = document.createElement('td');
|
||||
tdClose.style.textAlign = 'right';
|
||||
tdClose.style.verticalAlign = 'top';
|
||||
tr.appendChild(tdClose);
|
||||
|
||||
var closeDiv = document.createElement('button');
|
||||
closeDiv.innerHTML = '×';
|
||||
closeDiv.title = 'Close error message';
|
||||
tdClose.appendChild(closeDiv);
|
||||
closeDiv.onclick = function () {
|
||||
main.removeError(divError);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove an error from the list with errors
|
||||
* @param {Element} [error] The HTML DOM of an error message
|
||||
* If undefined, the first error message will be
|
||||
* removed.
|
||||
*/
|
||||
main.removeError = function (error) {
|
||||
if (!error && main.errorFrame) {
|
||||
error = main.errorFrame.firstChild;
|
||||
}
|
||||
|
||||
if (error && error.parentNode == main.errorFrame) {
|
||||
error.parentNode.removeChild(error);
|
||||
}
|
||||
|
||||
if (main.errorFrame && main.errorFrame.childNodes.length == 0) {
|
||||
main.errorFrame.parentNode.removeChild(main.errorFrame);
|
||||
main.errorFrame = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
main.hideAds = function() {
|
||||
var domAd = document.getElementById("ad");
|
||||
if (domAd) {
|
||||
domAd.parentNode.removeChild(domAd);
|
||||
main.resize();
|
||||
}
|
||||
};
|
||||
|
||||
main.removeAds = function () {
|
||||
var domRemoveAds = document.getElementById('removeAds');
|
||||
if (domRemoveAds) {
|
||||
domRemoveAds.style.display = 'none';
|
||||
}
|
||||
main.resize();
|
||||
};
|
|
@ -27,7 +27,7 @@
|
|||
* Copyright (c) 2011-2012 Jos de Jong, http://jsoneditoronline.org
|
||||
*
|
||||
* @author Jos de Jong, <wjosdejong@gmail.com>
|
||||
* @date 2012-10-19
|
||||
* @date 2012-10-31
|
||||
*/
|
||||
|
||||
|
||||
|
@ -3105,6 +3105,13 @@ JSONFormatter.prototype.getText = function() {
|
|||
return this.textarea.value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the text contents of the JSONFormatter
|
||||
* @param {String} text
|
||||
*/
|
||||
JSONFormatter.prototype.setText = function(text) {
|
||||
this.textarea.value = text;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set a callback method for the onchange event
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<meta name="keywords" content="json, editor, couchdb, online, javascript, javascript object notation, treeview, open source, free">
|
||||
<meta name="author" content="Jos de Jong">
|
||||
|
||||
<link rel="shortcut icon" href="../favicon.ico">
|
||||
<link rel="shortcut icon" href="../app/web/favicon.ico">
|
||||
|
||||
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../jsoneditor/jsoneditor.js"></script>
|
||||
|
|