Refactored JSONEditor sourcecode to AMD modules
This commit is contained in:
parent
6eea2b6335
commit
d6da7f548e
|
@ -5,6 +5,8 @@ http://jsoneditoronline.org
|
|||
|
||||
## not yet released, version 3.0.0
|
||||
|
||||
- Editor must be loaded as `new JSONEditor(...)` instead of
|
||||
`new jsoneditor.JSONEditor(...)`.
|
||||
- Large code reorganization.
|
||||
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ download:
|
|||
<script type="text/javascript" >
|
||||
// create the editor
|
||||
var container = document.getElementById("jsoneditor");
|
||||
var editor = new jsoneditor.JSONEditor(container);
|
||||
var editor = new JSONEditor(container);
|
||||
|
||||
// set json
|
||||
var json = {
|
||||
|
|
|
@ -52,7 +52,7 @@ Constructs a new JSONEditor.
|
|||
|
||||
*Returns:*
|
||||
|
||||
- `{jsoneditor.JSONEditor} editor`
|
||||
- `{JSONEditor} editor`
|
||||
New instance of a JSONEditor.
|
||||
|
||||
|
||||
|
@ -133,7 +133,7 @@ var options = {
|
|||
"mode": "tree",
|
||||
"search": true
|
||||
};
|
||||
var editor = new jsoneditor.JSONEditor (container, options);
|
||||
var editor = new JSONEditor (container, options);
|
||||
var json = {
|
||||
"Array": [1, 2, 3],
|
||||
"Boolean": true,
|
||||
|
@ -155,7 +155,7 @@ var options = {
|
|||
"mode": "text",
|
||||
"indentation": 2
|
||||
};
|
||||
var editor = new jsoneditor.JSONEditor (container, options);
|
||||
var editor = new JSONEditor (container, options);
|
||||
var json = {
|
||||
"Array": [1, 2, 3],
|
||||
"Boolean": true,
|
||||
|
|
|
@ -65,7 +65,7 @@ var container = document.getElementById("jsoneditor");
|
|||
var options = {
|
||||
mode: 'tree'
|
||||
};
|
||||
var editor = new jsoneditor.JSONEditor(container, options);
|
||||
var editor = new JSONEditor(container, options);
|
||||
```
|
||||
|
||||
To set JSON data in the editor:
|
||||
|
@ -108,7 +108,7 @@ var json = editor.get();
|
|||
<script type="text/javascript" >
|
||||
// create the editor
|
||||
var container = document.getElementById("jsoneditor");
|
||||
var editor = new jsoneditor.JSONEditor(container);
|
||||
var editor = new JSONEditor(container);
|
||||
|
||||
// set json
|
||||
function setJSON () {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>JSONEditor | Basic usage</title>
|
||||
<link rel="stylesheet" type="text/css" href="../jsoneditor.css">
|
||||
<script type="text/javascript" src="../jsoneditor.js"></script>
|
||||
<style type="text/css">
|
||||
|
@ -20,7 +21,7 @@
|
|||
<script type="text/javascript" >
|
||||
// create the editor
|
||||
var container = document.getElementById('jsoneditor');
|
||||
var editor = new jsoneditor.JSONEditor(container);
|
||||
var editor = new JSONEditor(container);
|
||||
|
||||
// set json
|
||||
document.getElementById('setJSON').onclick = function () {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>JSONEditor | Viewer</title>
|
||||
<link rel="stylesheet" type="text/css" href="../jsoneditor.css">
|
||||
<script type="text/javascript" src="../jsoneditor.js"></script>
|
||||
<style type="text/css">
|
||||
|
@ -34,7 +35,7 @@
|
|||
'string': 'Hello World'
|
||||
};
|
||||
|
||||
var editor = new jsoneditor.JSONEditor(container, options, json);
|
||||
var editor = new JSONEditor(container, options, json);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>JSONEditor | Switch mode</title>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|
||||
|
||||
<!-- json editor -->
|
||||
|
@ -61,7 +62,7 @@
|
|||
"string": "Hello World"
|
||||
};
|
||||
|
||||
var editor = new jsoneditor.JSONEditor(container, options, json);
|
||||
var editor = new JSONEditor(container, options, json);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>JSONEditor | Require.js demo</title>
|
||||
<style type="text/css">
|
||||
#jsoneditor {
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" type="text/css" href="../../jsoneditor.css">
|
||||
<script data-main="scripts/main" src="scripts/require.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
var module = '../../../jsoneditor';
|
||||
require([module], function (jsoneditor) {
|
||||
require([module], function (JSONEditor) {
|
||||
// create the editor
|
||||
var container = document.getElementById('jsoneditor');
|
||||
var editor = new jsoneditor.JSONEditor(container);
|
||||
var editor = new JSONEditor(container);
|
||||
|
||||
// set json
|
||||
document.getElementById('setJSON').onclick = function () {
|
||||
|
|
|
@ -1,35 +1,36 @@
|
|||
/*
|
||||
RequireJS 2.1.2 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.
|
||||
RequireJS 2.1.13 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved.
|
||||
Available via the MIT or new BSD license.
|
||||
see: http://github.com/jrburke/requirejs for details
|
||||
*/
|
||||
var requirejs,require,define;
|
||||
(function(Y){function H(b){return"[object Function]"===L.call(b)}function I(b){return"[object Array]"===L.call(b)}function x(b,c){if(b){var d;for(d=0;d<b.length&&(!b[d]||!c(b[d],d,b));d+=1);}}function M(b,c){if(b){var d;for(d=b.length-1;-1<d&&(!b[d]||!c(b[d],d,b));d-=1);}}function r(b,c){return da.call(b,c)}function i(b,c){return r(b,c)&&b[c]}function E(b,c){for(var d in b)if(r(b,d)&&c(b[d],d))break}function Q(b,c,d,i){c&&E(c,function(c,h){if(d||!r(b,h))i&&"string"!==typeof c?(b[h]||(b[h]={}),Q(b[h],
|
||||
c,d,i)):b[h]=c});return b}function t(b,c){return function(){return c.apply(b,arguments)}}function Z(b){if(!b)return b;var c=Y;x(b.split("."),function(b){c=c[b]});return c}function J(b,c,d,i){c=Error(c+"\nhttp://requirejs.org/docs/errors.html#"+b);c.requireType=b;c.requireModules=i;d&&(c.originalError=d);return c}function ea(b){function c(a,g,v){var e,n,b,c,d,j,f,h=g&&g.split("/");e=h;var l=m.map,k=l&&l["*"];if(a&&"."===a.charAt(0))if(g){e=i(m.pkgs,g)?h=[g]:h.slice(0,h.length-1);g=a=e.concat(a.split("/"));
|
||||
for(e=0;g[e];e+=1)if(n=g[e],"."===n)g.splice(e,1),e-=1;else if(".."===n)if(1===e&&(".."===g[2]||".."===g[0]))break;else 0<e&&(g.splice(e-1,2),e-=2);e=i(m.pkgs,g=a[0]);a=a.join("/");e&&a===g+"/"+e.main&&(a=g)}else 0===a.indexOf("./")&&(a=a.substring(2));if(v&&(h||k)&&l){g=a.split("/");for(e=g.length;0<e;e-=1){b=g.slice(0,e).join("/");if(h)for(n=h.length;0<n;n-=1)if(v=i(l,h.slice(0,n).join("/")))if(v=i(v,b)){c=v;d=e;break}if(c)break;!j&&(k&&i(k,b))&&(j=i(k,b),f=e)}!c&&j&&(c=j,d=f);c&&(g.splice(0,d,
|
||||
c),a=g.join("/"))}return a}function d(a){z&&x(document.getElementsByTagName("script"),function(g){if(g.getAttribute("data-requiremodule")===a&&g.getAttribute("data-requirecontext")===j.contextName)return g.parentNode.removeChild(g),!0})}function y(a){var g=i(m.paths,a);if(g&&I(g)&&1<g.length)return d(a),g.shift(),j.require.undef(a),j.require([a]),!0}function f(a){var g,b=a?a.indexOf("!"):-1;-1<b&&(g=a.substring(0,b),a=a.substring(b+1,a.length));return[g,a]}function h(a,g,b,e){var n,u,d=null,h=g?g.name:
|
||||
null,l=a,m=!0,k="";a||(m=!1,a="_@r"+(L+=1));a=f(a);d=a[0];a=a[1];d&&(d=c(d,h,e),u=i(p,d));a&&(d?k=u&&u.normalize?u.normalize(a,function(a){return c(a,h,e)}):c(a,h,e):(k=c(a,h,e),a=f(k),d=a[0],k=a[1],b=!0,n=j.nameToUrl(k)));b=d&&!u&&!b?"_unnormalized"+(M+=1):"";return{prefix:d,name:k,parentMap:g,unnormalized:!!b,url:n,originalName:l,isDefine:m,id:(d?d+"!"+k:k)+b}}function q(a){var g=a.id,b=i(k,g);b||(b=k[g]=new j.Module(a));return b}function s(a,g,b){var e=a.id,n=i(k,e);if(r(p,e)&&(!n||n.defineEmitComplete))"defined"===
|
||||
g&&b(p[e]);else q(a).on(g,b)}function C(a,g){var b=a.requireModules,e=!1;if(g)g(a);else if(x(b,function(g){if(g=i(k,g))g.error=a,g.events.error&&(e=!0,g.emit("error",a))}),!e)l.onError(a)}function w(){R.length&&(fa.apply(F,[F.length-1,0].concat(R)),R=[])}function A(a,g,b){var e=a.map.id;a.error?a.emit("error",a.error):(g[e]=!0,x(a.depMaps,function(e,c){var d=e.id,h=i(k,d);h&&(!a.depMatched[c]&&!b[d])&&(i(g,d)?(a.defineDep(c,p[d]),a.check()):A(h,g,b))}),b[e]=!0)}function B(){var a,g,b,e,n=(b=1E3*m.waitSeconds)&&
|
||||
j.startTime+b<(new Date).getTime(),c=[],h=[],f=!1,l=!0;if(!T){T=!0;E(k,function(b){a=b.map;g=a.id;if(b.enabled&&(a.isDefine||h.push(b),!b.error))if(!b.inited&&n)y(g)?f=e=!0:(c.push(g),d(g));else if(!b.inited&&(b.fetched&&a.isDefine)&&(f=!0,!a.prefix))return l=!1});if(n&&c.length)return b=J("timeout","Load timeout for modules: "+c,null,c),b.contextName=j.contextName,C(b);l&&x(h,function(a){A(a,{},{})});if((!n||e)&&f)if((z||$)&&!U)U=setTimeout(function(){U=0;B()},50);T=!1}}function D(a){r(p,a[0])||
|
||||
q(h(a[0],null,!0)).init(a[1],a[2])}function G(a){var a=a.currentTarget||a.srcElement,b=j.onScriptLoad;a.detachEvent&&!V?a.detachEvent("onreadystatechange",b):a.removeEventListener("load",b,!1);b=j.onScriptError;(!a.detachEvent||V)&&a.removeEventListener("error",b,!1);return{node:a,id:a&&a.getAttribute("data-requiremodule")}}function K(){var a;for(w();F.length;){a=F.shift();if(null===a[0])return C(J("mismatch","Mismatched anonymous define() module: "+a[a.length-1]));D(a)}}var T,W,j,N,U,m={waitSeconds:7,
|
||||
baseUrl:"./",paths:{},pkgs:{},shim:{},map:{},config:{}},k={},X={},F=[],p={},S={},L=1,M=1;N={require:function(a){return a.require?a.require:a.require=j.makeRequire(a.map)},exports:function(a){a.usingExports=!0;if(a.map.isDefine)return a.exports?a.exports:a.exports=p[a.map.id]={}},module:function(a){return a.module?a.module:a.module={id:a.map.id,uri:a.map.url,config:function(){return m.config&&i(m.config,a.map.id)||{}},exports:p[a.map.id]}}};W=function(a){this.events=i(X,a.id)||{};this.map=a;this.shim=
|
||||
i(m.shim,a.id);this.depExports=[];this.depMaps=[];this.depMatched=[];this.pluginMaps={};this.depCount=0};W.prototype={init:function(a,b,c,e){e=e||{};if(!this.inited){this.factory=b;if(c)this.on("error",c);else this.events.error&&(c=t(this,function(a){this.emit("error",a)}));this.depMaps=a&&a.slice(0);this.errback=c;this.inited=!0;this.ignore=e.ignore;e.enabled||this.enabled?this.enable():this.check()}},defineDep:function(a,b){this.depMatched[a]||(this.depMatched[a]=!0,this.depCount-=1,this.depExports[a]=
|
||||
b)},fetch:function(){if(!this.fetched){this.fetched=!0;j.startTime=(new Date).getTime();var a=this.map;if(this.shim)j.makeRequire(this.map,{enableBuildCallback:!0})(this.shim.deps||[],t(this,function(){return a.prefix?this.callPlugin():this.load()}));else return a.prefix?this.callPlugin():this.load()}},load:function(){var a=this.map.url;S[a]||(S[a]=!0,j.load(this.map.id,a))},check:function(){if(this.enabled&&!this.enabling){var a,b,c=this.map.id;b=this.depExports;var e=this.exports,n=this.factory;
|
||||
if(this.inited)if(this.error)this.emit("error",this.error);else{if(!this.defining){this.defining=!0;if(1>this.depCount&&!this.defined){if(H(n)){if(this.events.error)try{e=j.execCb(c,n,b,e)}catch(d){a=d}else e=j.execCb(c,n,b,e);this.map.isDefine&&((b=this.module)&&void 0!==b.exports&&b.exports!==this.exports?e=b.exports:void 0===e&&this.usingExports&&(e=this.exports));if(a)return a.requireMap=this.map,a.requireModules=[this.map.id],a.requireType="define",C(this.error=a)}else e=n;this.exports=e;if(this.map.isDefine&&
|
||||
!this.ignore&&(p[c]=e,l.onResourceLoad))l.onResourceLoad(j,this.map,this.depMaps);delete k[c];this.defined=!0}this.defining=!1;this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=!0)}}else this.fetch()}},callPlugin:function(){var a=this.map,b=a.id,d=h(a.prefix);this.depMaps.push(d);s(d,"defined",t(this,function(e){var n,d;d=this.map.name;var v=this.map.parentMap?this.map.parentMap.name:null,f=j.makeRequire(a.parentMap,{enableBuildCallback:!0,
|
||||
skipMap:!0});if(this.map.unnormalized){if(e.normalize&&(d=e.normalize(d,function(a){return c(a,v,!0)})||""),e=h(a.prefix+"!"+d,this.map.parentMap),s(e,"defined",t(this,function(a){this.init([],function(){return a},null,{enabled:!0,ignore:!0})})),d=i(k,e.id)){this.depMaps.push(e);if(this.events.error)d.on("error",t(this,function(a){this.emit("error",a)}));d.enable()}}else n=t(this,function(a){this.init([],function(){return a},null,{enabled:!0})}),n.error=t(this,function(a){this.inited=!0;this.error=
|
||||
a;a.requireModules=[b];E(k,function(a){0===a.map.id.indexOf(b+"_unnormalized")&&delete k[a.map.id]});C(a)}),n.fromText=t(this,function(e,c){var d=a.name,u=h(d),v=O;c&&(e=c);v&&(O=!1);q(u);r(m.config,b)&&(m.config[d]=m.config[b]);try{l.exec(e)}catch(k){throw Error("fromText eval for "+d+" failed: "+k);}v&&(O=!0);this.depMaps.push(u);j.completeLoad(d);f([d],n)}),e.load(a.name,f,n,m)}));j.enable(d,this);this.pluginMaps[d.id]=d},enable:function(){this.enabling=this.enabled=!0;x(this.depMaps,t(this,function(a,
|
||||
b){var c,e;if("string"===typeof a){a=h(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=i(N,a.id)){this.depExports[b]=c(this);return}this.depCount+=1;s(a,"defined",t(this,function(a){this.defineDep(b,a);this.check()}));this.errback&&s(a,"error",this.errback)}c=a.id;e=k[c];!r(N,c)&&(e&&!e.enabled)&&j.enable(a,this)}));E(this.pluginMaps,t(this,function(a){var b=i(k,a.id);b&&!b.enabled&&j.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c=
|
||||
this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){x(this.events[a],function(a){a(b)});"error"===a&&delete this.events[a]}};j={config:m,contextName:b,registry:k,defined:p,urlFetched:S,defQueue:F,Module:W,makeModuleMap:h,nextTick:l.nextTick,configure:function(a){a.baseUrl&&"/"!==a.baseUrl.charAt(a.baseUrl.length-1)&&(a.baseUrl+="/");var b=m.pkgs,c=m.shim,e={paths:!0,config:!0,map:!0};E(a,function(a,b){e[b]?"map"===b?Q(m[b],a,!0,!0):Q(m[b],a,!0):m[b]=a});a.shim&&(E(a.shim,function(a,
|
||||
b){I(a)&&(a={deps:a});if((a.exports||a.init)&&!a.exportsFn)a.exportsFn=j.makeShimExports(a);c[b]=a}),m.shim=c);a.packages&&(x(a.packages,function(a){a="string"===typeof a?{name:a}:a;b[a.name]={name:a.name,location:a.location||a.name,main:(a.main||"main").replace(ga,"").replace(aa,"")}}),m.pkgs=b);E(k,function(a,b){!a.inited&&!a.map.unnormalized&&(a.map=h(b))});if(a.deps||a.callback)j.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b;a.init&&(b=a.init.apply(Y,arguments));
|
||||
return b||a.exports&&Z(a.exports)}},makeRequire:function(a,d){function f(e,c,u){var i,m;d.enableBuildCallback&&(c&&H(c))&&(c.__requireJsBuild=!0);if("string"===typeof e){if(H(c))return C(J("requireargs","Invalid require call"),u);if(a&&r(N,e))return N[e](k[a.id]);if(l.get)return l.get(j,e,a);i=h(e,a,!1,!0);i=i.id;return!r(p,i)?C(J("notloaded",'Module name "'+i+'" has not been loaded yet for context: '+b+(a?"":". Use require([])"))):p[i]}K();j.nextTick(function(){K();m=q(h(null,a));m.skipMap=d.skipMap;
|
||||
m.init(e,c,u,{enabled:!0});B()});return f}d=d||{};Q(f,{isBrowser:z,toUrl:function(b){var d=b.lastIndexOf("."),g=null;-1!==d&&(g=b.substring(d,b.length),b=b.substring(0,d));return j.nameToUrl(c(b,a&&a.id,!0),g)},defined:function(b){return r(p,h(b,a,!1,!0).id)},specified:function(b){b=h(b,a,!1,!0).id;return r(p,b)||r(k,b)}});a||(f.undef=function(b){w();var c=h(b,a,!0),d=i(k,b);delete p[b];delete S[c.url];delete X[b];d&&(d.events.defined&&(X[b]=d.events),delete k[b])});return f},enable:function(a){i(k,
|
||||
a.id)&&q(a).enable()},completeLoad:function(a){var b,c,d=i(m.shim,a)||{},h=d.exports;for(w();F.length;){c=F.shift();if(null===c[0]){c[0]=a;if(b)break;b=!0}else c[0]===a&&(b=!0);D(c)}c=i(k,a);if(!b&&!r(p,a)&&c&&!c.inited){if(m.enforceDefine&&(!h||!Z(h)))return y(a)?void 0:C(J("nodefine","No define call for "+a,null,[a]));D([a,d.deps||[],d.exportsFn])}B()},nameToUrl:function(a,b){var c,d,h,f,j,k;if(l.jsExtRegExp.test(a))f=a+(b||"");else{c=m.paths;d=m.pkgs;f=a.split("/");for(j=f.length;0<j;j-=1)if(k=
|
||||
f.slice(0,j).join("/"),h=i(d,k),k=i(c,k)){I(k)&&(k=k[0]);f.splice(0,j,k);break}else if(h){c=a===h.name?h.location+"/"+h.main:h.location;f.splice(0,j,c);break}f=f.join("/");f+=b||(/\?/.test(f)?"":".js");f=("/"===f.charAt(0)||f.match(/^[\w\+\.\-]+:/)?"":m.baseUrl)+f}return m.urlArgs?f+((-1===f.indexOf("?")?"?":"&")+m.urlArgs):f},load:function(a,b){l.load(j,a,b)},execCb:function(a,b,c,d){return b.apply(d,c)},onScriptLoad:function(a){if("load"===a.type||ha.test((a.currentTarget||a.srcElement).readyState))P=
|
||||
null,a=G(a),j.completeLoad(a.id)},onScriptError:function(a){var b=G(a);if(!y(b.id))return C(J("scripterror","Script error",a,[b.id]))}};j.require=j.makeRequire();return j}var l,w,A,D,s,G,P,K,ba,ca,ia=/(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,ja=/[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,aa=/\.js$/,ga=/^\.\//;w=Object.prototype;var L=w.toString,da=w.hasOwnProperty,fa=Array.prototype.splice,z=!!("undefined"!==typeof window&&navigator&&document),$=!z&&"undefined"!==typeof importScripts,ha=z&&
|
||||
"PLAYSTATION 3"===navigator.platform?/^complete$/:/^(complete|loaded)$/,V="undefined"!==typeof opera&&"[object Opera]"===opera.toString(),B={},q={},R=[],O=!1;if("undefined"===typeof define){if("undefined"!==typeof requirejs){if(H(requirejs))return;q=requirejs;requirejs=void 0}"undefined"!==typeof require&&!H(require)&&(q=require,require=void 0);l=requirejs=function(b,c,d,y){var f,h="_";!I(b)&&"string"!==typeof b&&(f=b,I(c)?(b=c,c=d,d=y):b=[]);f&&f.context&&(h=f.context);(y=i(B,h))||(y=B[h]=l.s.newContext(h));
|
||||
f&&y.configure(f);return y.require(b,c,d)};l.config=function(b){return l(b)};l.nextTick="undefined"!==typeof setTimeout?function(b){setTimeout(b,4)}:function(b){b()};require||(require=l);l.version="2.1.2";l.jsExtRegExp=/^\/|:|\?|\.js$/;l.isBrowser=z;w=l.s={contexts:B,newContext:ea};l({});x(["toUrl","undef","defined","specified"],function(b){l[b]=function(){var c=B._;return c.require[b].apply(c,arguments)}});if(z&&(A=w.head=document.getElementsByTagName("head")[0],D=document.getElementsByTagName("base")[0]))A=
|
||||
w.head=D.parentNode;l.onError=function(b){throw b;};l.load=function(b,c,d){var i=b&&b.config||{},f;if(z)return f=i.xhtml?document.createElementNS("http://www.w3.org/1999/xhtml","html:script"):document.createElement("script"),f.type=i.scriptType||"text/javascript",f.charset="utf-8",f.async=!0,f.setAttribute("data-requirecontext",b.contextName),f.setAttribute("data-requiremodule",c),f.attachEvent&&!(f.attachEvent.toString&&0>f.attachEvent.toString().indexOf("[native code"))&&!V?(O=!0,f.attachEvent("onreadystatechange",
|
||||
b.onScriptLoad)):(f.addEventListener("load",b.onScriptLoad,!1),f.addEventListener("error",b.onScriptError,!1)),f.src=d,K=f,D?A.insertBefore(f,D):A.appendChild(f),K=null,f;$&&(importScripts(d),b.completeLoad(c))};z&&M(document.getElementsByTagName("script"),function(b){A||(A=b.parentNode);if(s=b.getAttribute("data-main"))return q.baseUrl||(G=s.split("/"),ba=G.pop(),ca=G.length?G.join("/")+"/":"./",q.baseUrl=ca,s=ba),s=s.replace(aa,""),q.deps=q.deps?q.deps.concat(s):[s],!0});define=function(b,c,d){var i,
|
||||
f;"string"!==typeof b&&(d=c,c=b,b=null);I(c)||(d=c,c=[]);!c.length&&H(d)&&d.length&&(d.toString().replace(ia,"").replace(ja,function(b,d){c.push(d)}),c=(1===d.length?["require"]:["require","exports","module"]).concat(c));if(O){if(!(i=K))P&&"interactive"===P.readyState||M(document.getElementsByTagName("script"),function(b){if("interactive"===b.readyState)return P=b}),i=P;i&&(b||(b=i.getAttribute("data-requiremodule")),f=B[i.getAttribute("data-requirecontext")])}(f?f.defQueue:R).push([b,c,d])};define.amd=
|
||||
{jQuery:!0};l.exec=function(b){return eval(b)};l(q)}})(this);
|
||||
(function(ba){function G(b){return"[object Function]"===K.call(b)}function H(b){return"[object Array]"===K.call(b)}function v(b,c){if(b){var d;for(d=0;d<b.length&&(!b[d]||!c(b[d],d,b));d+=1);}}function T(b,c){if(b){var d;for(d=b.length-1;-1<d&&(!b[d]||!c(b[d],d,b));d-=1);}}function t(b,c){return fa.call(b,c)}function m(b,c){return t(b,c)&&b[c]}function B(b,c){for(var d in b)if(t(b,d)&&c(b[d],d))break}function U(b,c,d,e){c&&B(c,function(c,g){if(d||!t(b,g))e&&"object"===typeof c&&c&&!H(c)&&!G(c)&&!(c instanceof
|
||||
RegExp)?(b[g]||(b[g]={}),U(b[g],c,d,e)):b[g]=c});return b}function u(b,c){return function(){return c.apply(b,arguments)}}function ca(b){throw b;}function da(b){if(!b)return b;var c=ba;v(b.split("."),function(b){c=c[b]});return c}function C(b,c,d,e){c=Error(c+"\nhttp://requirejs.org/docs/errors.html#"+b);c.requireType=b;c.requireModules=e;d&&(c.originalError=d);return c}function ga(b){function c(a,k,b){var f,l,c,d,e,g,i,p,k=k&&k.split("/"),h=j.map,n=h&&h["*"];if(a){a=a.split("/");l=a.length-1;j.nodeIdCompat&&
|
||||
Q.test(a[l])&&(a[l]=a[l].replace(Q,""));"."===a[0].charAt(0)&&k&&(l=k.slice(0,k.length-1),a=l.concat(a));l=a;for(c=0;c<l.length;c++)if(d=l[c],"."===d)l.splice(c,1),c-=1;else if(".."===d&&!(0===c||1==c&&".."===l[2]||".."===l[c-1])&&0<c)l.splice(c-1,2),c-=2;a=a.join("/")}if(b&&h&&(k||n)){l=a.split("/");c=l.length;a:for(;0<c;c-=1){e=l.slice(0,c).join("/");if(k)for(d=k.length;0<d;d-=1)if(b=m(h,k.slice(0,d).join("/")))if(b=m(b,e)){f=b;g=c;break a}!i&&(n&&m(n,e))&&(i=m(n,e),p=c)}!f&&i&&(f=i,g=p);f&&(l.splice(0,
|
||||
g,f),a=l.join("/"))}return(f=m(j.pkgs,a))?f:a}function d(a){z&&v(document.getElementsByTagName("script"),function(k){if(k.getAttribute("data-requiremodule")===a&&k.getAttribute("data-requirecontext")===i.contextName)return k.parentNode.removeChild(k),!0})}function e(a){var k=m(j.paths,a);if(k&&H(k)&&1<k.length)return k.shift(),i.require.undef(a),i.makeRequire(null,{skipMap:!0})([a]),!0}function n(a){var k,c=a?a.indexOf("!"):-1;-1<c&&(k=a.substring(0,c),a=a.substring(c+1,a.length));return[k,a]}function p(a,
|
||||
k,b,f){var l,d,e=null,g=k?k.name:null,j=a,p=!0,h="";a||(p=!1,a="_@r"+(K+=1));a=n(a);e=a[0];a=a[1];e&&(e=c(e,g,f),d=m(r,e));a&&(e?h=d&&d.normalize?d.normalize(a,function(a){return c(a,g,f)}):c(a,g,f):(h=c(a,g,f),a=n(h),e=a[0],h=a[1],b=!0,l=i.nameToUrl(h)));b=e&&!d&&!b?"_unnormalized"+(O+=1):"";return{prefix:e,name:h,parentMap:k,unnormalized:!!b,url:l,originalName:j,isDefine:p,id:(e?e+"!"+h:h)+b}}function s(a){var k=a.id,b=m(h,k);b||(b=h[k]=new i.Module(a));return b}function q(a,k,b){var f=a.id,c=m(h,
|
||||
f);if(t(r,f)&&(!c||c.defineEmitComplete))"defined"===k&&b(r[f]);else if(c=s(a),c.error&&"error"===k)b(c.error);else c.on(k,b)}function w(a,b){var c=a.requireModules,f=!1;if(b)b(a);else if(v(c,function(b){if(b=m(h,b))b.error=a,b.events.error&&(f=!0,b.emit("error",a))}),!f)g.onError(a)}function x(){R.length&&(ha.apply(A,[A.length,0].concat(R)),R=[])}function y(a){delete h[a];delete V[a]}function F(a,b,c){var f=a.map.id;a.error?a.emit("error",a.error):(b[f]=!0,v(a.depMaps,function(f,d){var e=f.id,g=
|
||||
m(h,e);g&&(!a.depMatched[d]&&!c[e])&&(m(b,e)?(a.defineDep(d,r[e]),a.check()):F(g,b,c))}),c[f]=!0)}function D(){var a,b,c=(a=1E3*j.waitSeconds)&&i.startTime+a<(new Date).getTime(),f=[],l=[],g=!1,h=!0;if(!W){W=!0;B(V,function(a){var i=a.map,j=i.id;if(a.enabled&&(i.isDefine||l.push(a),!a.error))if(!a.inited&&c)e(j)?g=b=!0:(f.push(j),d(j));else if(!a.inited&&(a.fetched&&i.isDefine)&&(g=!0,!i.prefix))return h=!1});if(c&&f.length)return a=C("timeout","Load timeout for modules: "+f,null,f),a.contextName=
|
||||
i.contextName,w(a);h&&v(l,function(a){F(a,{},{})});if((!c||b)&&g)if((z||ea)&&!X)X=setTimeout(function(){X=0;D()},50);W=!1}}function E(a){t(r,a[0])||s(p(a[0],null,!0)).init(a[1],a[2])}function I(a){var a=a.currentTarget||a.srcElement,b=i.onScriptLoad;a.detachEvent&&!Y?a.detachEvent("onreadystatechange",b):a.removeEventListener("load",b,!1);b=i.onScriptError;(!a.detachEvent||Y)&&a.removeEventListener("error",b,!1);return{node:a,id:a&&a.getAttribute("data-requiremodule")}}function J(){var a;for(x();A.length;){a=
|
||||
A.shift();if(null===a[0])return w(C("mismatch","Mismatched anonymous define() module: "+a[a.length-1]));E(a)}}var W,Z,i,L,X,j={waitSeconds:7,baseUrl:"./",paths:{},bundles:{},pkgs:{},shim:{},config:{}},h={},V={},$={},A=[],r={},S={},aa={},K=1,O=1;L={require:function(a){return a.require?a.require:a.require=i.makeRequire(a.map)},exports:function(a){a.usingExports=!0;if(a.map.isDefine)return a.exports?r[a.map.id]=a.exports:a.exports=r[a.map.id]={}},module:function(a){return a.module?a.module:a.module=
|
||||
{id:a.map.id,uri:a.map.url,config:function(){return m(j.config,a.map.id)||{}},exports:a.exports||(a.exports={})}}};Z=function(a){this.events=m($,a.id)||{};this.map=a;this.shim=m(j.shim,a.id);this.depExports=[];this.depMaps=[];this.depMatched=[];this.pluginMaps={};this.depCount=0};Z.prototype={init:function(a,b,c,f){f=f||{};if(!this.inited){this.factory=b;if(c)this.on("error",c);else this.events.error&&(c=u(this,function(a){this.emit("error",a)}));this.depMaps=a&&a.slice(0);this.errback=c;this.inited=
|
||||
!0;this.ignore=f.ignore;f.enabled||this.enabled?this.enable():this.check()}},defineDep:function(a,b){this.depMatched[a]||(this.depMatched[a]=!0,this.depCount-=1,this.depExports[a]=b)},fetch:function(){if(!this.fetched){this.fetched=!0;i.startTime=(new Date).getTime();var a=this.map;if(this.shim)i.makeRequire(this.map,{enableBuildCallback:!0})(this.shim.deps||[],u(this,function(){return a.prefix?this.callPlugin():this.load()}));else return a.prefix?this.callPlugin():this.load()}},load:function(){var a=
|
||||
this.map.url;S[a]||(S[a]=!0,i.load(this.map.id,a))},check:function(){if(this.enabled&&!this.enabling){var a,b,c=this.map.id;b=this.depExports;var f=this.exports,l=this.factory;if(this.inited)if(this.error)this.emit("error",this.error);else{if(!this.defining){this.defining=!0;if(1>this.depCount&&!this.defined){if(G(l)){if(this.events.error&&this.map.isDefine||g.onError!==ca)try{f=i.execCb(c,l,b,f)}catch(d){a=d}else f=i.execCb(c,l,b,f);this.map.isDefine&&void 0===f&&((b=this.module)?f=b.exports:this.usingExports&&
|
||||
(f=this.exports));if(a)return a.requireMap=this.map,a.requireModules=this.map.isDefine?[this.map.id]:null,a.requireType=this.map.isDefine?"define":"require",w(this.error=a)}else f=l;this.exports=f;if(this.map.isDefine&&!this.ignore&&(r[c]=f,g.onResourceLoad))g.onResourceLoad(i,this.map,this.depMaps);y(c);this.defined=!0}this.defining=!1;this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=!0)}}else this.fetch()}},callPlugin:function(){var a=
|
||||
this.map,b=a.id,d=p(a.prefix);this.depMaps.push(d);q(d,"defined",u(this,function(f){var l,d;d=m(aa,this.map.id);var e=this.map.name,P=this.map.parentMap?this.map.parentMap.name:null,n=i.makeRequire(a.parentMap,{enableBuildCallback:!0});if(this.map.unnormalized){if(f.normalize&&(e=f.normalize(e,function(a){return c(a,P,!0)})||""),f=p(a.prefix+"!"+e,this.map.parentMap),q(f,"defined",u(this,function(a){this.init([],function(){return a},null,{enabled:!0,ignore:!0})})),d=m(h,f.id)){this.depMaps.push(f);
|
||||
if(this.events.error)d.on("error",u(this,function(a){this.emit("error",a)}));d.enable()}}else d?(this.map.url=i.nameToUrl(d),this.load()):(l=u(this,function(a){this.init([],function(){return a},null,{enabled:!0})}),l.error=u(this,function(a){this.inited=!0;this.error=a;a.requireModules=[b];B(h,function(a){0===a.map.id.indexOf(b+"_unnormalized")&&y(a.map.id)});w(a)}),l.fromText=u(this,function(f,c){var d=a.name,e=p(d),P=M;c&&(f=c);P&&(M=!1);s(e);t(j.config,b)&&(j.config[d]=j.config[b]);try{g.exec(f)}catch(h){return w(C("fromtexteval",
|
||||
"fromText eval for "+b+" failed: "+h,h,[b]))}P&&(M=!0);this.depMaps.push(e);i.completeLoad(d);n([d],l)}),f.load(a.name,n,l,j))}));i.enable(d,this);this.pluginMaps[d.id]=d},enable:function(){V[this.map.id]=this;this.enabling=this.enabled=!0;v(this.depMaps,u(this,function(a,b){var c,f;if("string"===typeof a){a=p(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=m(L,a.id)){this.depExports[b]=c(this);return}this.depCount+=1;q(a,"defined",u(this,function(a){this.defineDep(b,
|
||||
a);this.check()}));this.errback&&q(a,"error",u(this,this.errback))}c=a.id;f=h[c];!t(L,c)&&(f&&!f.enabled)&&i.enable(a,this)}));B(this.pluginMaps,u(this,function(a){var b=m(h,a.id);b&&!b.enabled&&i.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c=this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){v(this.events[a],function(a){a(b)});"error"===a&&delete this.events[a]}};i={config:j,contextName:b,registry:h,defined:r,urlFetched:S,defQueue:A,Module:Z,makeModuleMap:p,
|
||||
nextTick:g.nextTick,onError:w,configure:function(a){a.baseUrl&&"/"!==a.baseUrl.charAt(a.baseUrl.length-1)&&(a.baseUrl+="/");var b=j.shim,c={paths:!0,bundles:!0,config:!0,map:!0};B(a,function(a,b){c[b]?(j[b]||(j[b]={}),U(j[b],a,!0,!0)):j[b]=a});a.bundles&&B(a.bundles,function(a,b){v(a,function(a){a!==b&&(aa[a]=b)})});a.shim&&(B(a.shim,function(a,c){H(a)&&(a={deps:a});if((a.exports||a.init)&&!a.exportsFn)a.exportsFn=i.makeShimExports(a);b[c]=a}),j.shim=b);a.packages&&v(a.packages,function(a){var b,
|
||||
a="string"===typeof a?{name:a}:a;b=a.name;a.location&&(j.paths[b]=a.location);j.pkgs[b]=a.name+"/"+(a.main||"main").replace(ia,"").replace(Q,"")});B(h,function(a,b){!a.inited&&!a.map.unnormalized&&(a.map=p(b))});if(a.deps||a.callback)i.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b;a.init&&(b=a.init.apply(ba,arguments));return b||a.exports&&da(a.exports)}},makeRequire:function(a,e){function j(c,d,m){var n,q;e.enableBuildCallback&&(d&&G(d))&&(d.__requireJsBuild=
|
||||
!0);if("string"===typeof c){if(G(d))return w(C("requireargs","Invalid require call"),m);if(a&&t(L,c))return L[c](h[a.id]);if(g.get)return g.get(i,c,a,j);n=p(c,a,!1,!0);n=n.id;return!t(r,n)?w(C("notloaded",'Module name "'+n+'" has not been loaded yet for context: '+b+(a?"":". Use require([])"))):r[n]}J();i.nextTick(function(){J();q=s(p(null,a));q.skipMap=e.skipMap;q.init(c,d,m,{enabled:!0});D()});return j}e=e||{};U(j,{isBrowser:z,toUrl:function(b){var d,e=b.lastIndexOf("."),k=b.split("/")[0];if(-1!==
|
||||
e&&(!("."===k||".."===k)||1<e))d=b.substring(e,b.length),b=b.substring(0,e);return i.nameToUrl(c(b,a&&a.id,!0),d,!0)},defined:function(b){return t(r,p(b,a,!1,!0).id)},specified:function(b){b=p(b,a,!1,!0).id;return t(r,b)||t(h,b)}});a||(j.undef=function(b){x();var c=p(b,a,!0),e=m(h,b);d(b);delete r[b];delete S[c.url];delete $[b];T(A,function(a,c){a[0]===b&&A.splice(c,1)});e&&(e.events.defined&&($[b]=e.events),y(b))});return j},enable:function(a){m(h,a.id)&&s(a).enable()},completeLoad:function(a){var b,
|
||||
c,d=m(j.shim,a)||{},g=d.exports;for(x();A.length;){c=A.shift();if(null===c[0]){c[0]=a;if(b)break;b=!0}else c[0]===a&&(b=!0);E(c)}c=m(h,a);if(!b&&!t(r,a)&&c&&!c.inited){if(j.enforceDefine&&(!g||!da(g)))return e(a)?void 0:w(C("nodefine","No define call for "+a,null,[a]));E([a,d.deps||[],d.exportsFn])}D()},nameToUrl:function(a,b,c){var d,e,h;(d=m(j.pkgs,a))&&(a=d);if(d=m(aa,a))return i.nameToUrl(d,b,c);if(g.jsExtRegExp.test(a))d=a+(b||"");else{d=j.paths;a=a.split("/");for(e=a.length;0<e;e-=1)if(h=a.slice(0,
|
||||
e).join("/"),h=m(d,h)){H(h)&&(h=h[0]);a.splice(0,e,h);break}d=a.join("/");d+=b||(/^data\:|\?/.test(d)||c?"":".js");d=("/"===d.charAt(0)||d.match(/^[\w\+\.\-]+:/)?"":j.baseUrl)+d}return j.urlArgs?d+((-1===d.indexOf("?")?"?":"&")+j.urlArgs):d},load:function(a,b){g.load(i,a,b)},execCb:function(a,b,c,d){return b.apply(d,c)},onScriptLoad:function(a){if("load"===a.type||ja.test((a.currentTarget||a.srcElement).readyState))N=null,a=I(a),i.completeLoad(a.id)},onScriptError:function(a){var b=I(a);if(!e(b.id))return w(C("scripterror",
|
||||
"Script error for: "+b.id,a,[b.id]))}};i.require=i.makeRequire();return i}var g,x,y,D,I,E,N,J,s,O,ka=/(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,la=/[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,Q=/\.js$/,ia=/^\.\//;x=Object.prototype;var K=x.toString,fa=x.hasOwnProperty,ha=Array.prototype.splice,z=!!("undefined"!==typeof window&&"undefined"!==typeof navigator&&window.document),ea=!z&&"undefined"!==typeof importScripts,ja=z&&"PLAYSTATION 3"===navigator.platform?/^complete$/:/^(complete|loaded)$/,
|
||||
Y="undefined"!==typeof opera&&"[object Opera]"===opera.toString(),F={},q={},R=[],M=!1;if("undefined"===typeof define){if("undefined"!==typeof requirejs){if(G(requirejs))return;q=requirejs;requirejs=void 0}"undefined"!==typeof require&&!G(require)&&(q=require,require=void 0);g=requirejs=function(b,c,d,e){var n,p="_";!H(b)&&"string"!==typeof b&&(n=b,H(c)?(b=c,c=d,d=e):b=[]);n&&n.context&&(p=n.context);(e=m(F,p))||(e=F[p]=g.s.newContext(p));n&&e.configure(n);return e.require(b,c,d)};g.config=function(b){return g(b)};
|
||||
g.nextTick="undefined"!==typeof setTimeout?function(b){setTimeout(b,4)}:function(b){b()};require||(require=g);g.version="2.1.13";g.jsExtRegExp=/^\/|:|\?|\.js$/;g.isBrowser=z;x=g.s={contexts:F,newContext:ga};g({});v(["toUrl","undef","defined","specified"],function(b){g[b]=function(){var c=F._;return c.require[b].apply(c,arguments)}});if(z&&(y=x.head=document.getElementsByTagName("head")[0],D=document.getElementsByTagName("base")[0]))y=x.head=D.parentNode;g.onError=ca;g.createNode=function(b){var c=
|
||||
b.xhtml?document.createElementNS("http://www.w3.org/1999/xhtml","html:script"):document.createElement("script");c.type=b.scriptType||"text/javascript";c.charset="utf-8";c.async=!0;return c};g.load=function(b,c,d){var e=b&&b.config||{};if(z)return e=g.createNode(e,c,d),e.setAttribute("data-requirecontext",b.contextName),e.setAttribute("data-requiremodule",c),e.attachEvent&&!(e.attachEvent.toString&&0>e.attachEvent.toString().indexOf("[native code"))&&!Y?(M=!0,e.attachEvent("onreadystatechange",b.onScriptLoad)):
|
||||
(e.addEventListener("load",b.onScriptLoad,!1),e.addEventListener("error",b.onScriptError,!1)),e.src=d,J=e,D?y.insertBefore(e,D):y.appendChild(e),J=null,e;if(ea)try{importScripts(d),b.completeLoad(c)}catch(m){b.onError(C("importscripts","importScripts failed for "+c+" at "+d,m,[c]))}};z&&!q.skipDataMain&&T(document.getElementsByTagName("script"),function(b){y||(y=b.parentNode);if(I=b.getAttribute("data-main"))return s=I,q.baseUrl||(E=s.split("/"),s=E.pop(),O=E.length?E.join("/")+"/":"./",q.baseUrl=
|
||||
O),s=s.replace(Q,""),g.jsExtRegExp.test(s)&&(s=I),q.deps=q.deps?q.deps.concat(s):[s],!0});define=function(b,c,d){var e,g;"string"!==typeof b&&(d=c,c=b,b=null);H(c)||(d=c,c=null);!c&&G(d)&&(c=[],d.length&&(d.toString().replace(ka,"").replace(la,function(b,d){c.push(d)}),c=(1===d.length?["require"]:["require","exports","module"]).concat(c)));if(M){if(!(e=J))N&&"interactive"===N.readyState||T(document.getElementsByTagName("script"),function(b){if("interactive"===b.readyState)return N=b}),e=N;e&&(b||
|
||||
(b=e.getAttribute("data-requiremodule")),g=F[e.getAttribute("data-requirecontext")])}(g?g.defQueue:R).push([b,c,d])};define.amd={jQuery:!0};g.exec=function(b){return eval(b)};g(q)}})(this);
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
var fs = require('fs'),
|
||||
gulp = require('gulp'),
|
||||
gutil = require('gulp-util'),
|
||||
webpack = require('webpack'),
|
||||
uglify = require('uglify-js');
|
||||
|
||||
var ENTRY = './src/js/JSONEditor.js',
|
||||
HEADER = './src/js/header.js',
|
||||
FILE = 'jsoneditor.js',
|
||||
FILE_MIN = 'jsoneditor.min.js',
|
||||
FILE_MAP = 'jsoneditor.map',
|
||||
DIST = './',
|
||||
JSONEDITOR_JS = DIST + FILE,
|
||||
JSONEDITOR_MIN_JS = DIST + FILE_MIN,
|
||||
JSONEDITOR_MAP_JS = DIST + FILE_MAP;
|
||||
|
||||
// generate banner with today's date and correct version
|
||||
function createBanner() {
|
||||
var today = gutil.date(new Date(), 'yyyy-mm-dd'); // today, formatted as yyyy-mm-dd
|
||||
var version = require('./package.json').version; // math.js version
|
||||
|
||||
return String(fs.readFileSync(HEADER))
|
||||
.replace('@@date', today)
|
||||
.replace('@@version', version);
|
||||
}
|
||||
|
||||
var bannerPlugin = new webpack.BannerPlugin(createBanner(), {
|
||||
entryOnly: true,
|
||||
raw: true
|
||||
});
|
||||
|
||||
var webpackConfig = {
|
||||
entry: ENTRY,
|
||||
output: {
|
||||
library: 'JSONEditor',
|
||||
libraryTarget: 'umd',
|
||||
path: DIST,
|
||||
filename: FILE
|
||||
},
|
||||
plugins: [ bannerPlugin ],
|
||||
cache: true
|
||||
};
|
||||
|
||||
var uglifyConfig = {
|
||||
outSourceMap: FILE_MAP,
|
||||
output: {
|
||||
comments: /@license/
|
||||
}
|
||||
};
|
||||
|
||||
// create a single instance of the compiler to allow caching
|
||||
var compiler = webpack(webpackConfig);
|
||||
|
||||
gulp.task('bundle', function (cb) {
|
||||
// update the banner contents (has a date in it which should stay up to date)
|
||||
bannerPlugin.banner = createBanner();
|
||||
|
||||
compiler.run(function (err, stats) {
|
||||
if (err) {
|
||||
gutil.log(err);
|
||||
}
|
||||
|
||||
gutil.log('bundled ' + JSONEDITOR_JS);
|
||||
|
||||
// TODO: bundle css
|
||||
|
||||
// TODO: bundle and minify assets
|
||||
|
||||
cb();
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('minify', ['bundle'], function () {
|
||||
var result = uglify.minify([JSONEDITOR_JS], uglifyConfig);
|
||||
|
||||
fs.writeFileSync(JSONEDITOR_MIN_JS, result.code + '\n//# sourceMappingURL=' + FILE_MAP);
|
||||
fs.writeFileSync(JSONEDITOR_MAP_JS, result.map);
|
||||
|
||||
gutil.log('Minified ' + JSONEDITOR_MIN_JS);
|
||||
gutil.log('Mapped ' + JSONEDITOR_MAP_JS);
|
||||
|
||||
// TODO: minify css
|
||||
|
||||
});
|
||||
|
||||
// The default task (called when you run `gulp`)
|
||||
gulp.task('default', ['bundle', 'minify']);
|
12131
jsoneditor.js
12131
jsoneditor.js
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -17,7 +17,7 @@
|
|||
},
|
||||
"bugs": "https://github.com/josdejong/jsoneditor/issues",
|
||||
"scripts": {
|
||||
"build": "jake"
|
||||
"build": "gulp"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
|
@ -25,6 +25,10 @@
|
|||
"jake-utils": "latest",
|
||||
"archiver": "latest",
|
||||
"clean-css": "latest",
|
||||
"gulp": "latest",
|
||||
"gulp-util": "latest",
|
||||
"webpack": "latest",
|
||||
"uglify-js": "latest",
|
||||
"jsonlint": "latest",
|
||||
"ace": "git://github.com/ajaxorg/ace.git"
|
||||
}
|
||||
|
|
|
@ -1,211 +0,0 @@
|
|||
/**
|
||||
* @constructor AppendNode
|
||||
* @extends Node
|
||||
* @param {TreeEditor} editor
|
||||
* Create a new AppendNode. This is a special node which is created at the
|
||||
* end of the list with childs for an object or array
|
||||
*/
|
||||
function AppendNode (editor) {
|
||||
/** @type {TreeEditor} */
|
||||
this.editor = editor;
|
||||
this.dom = {};
|
||||
}
|
||||
|
||||
AppendNode.prototype = new Node();
|
||||
|
||||
/**
|
||||
* Return a table row with an append button.
|
||||
* @return {Element} dom TR element
|
||||
*/
|
||||
AppendNode.prototype.getDom = function () {
|
||||
// TODO: implement a new solution for the append node
|
||||
var dom = this.dom;
|
||||
|
||||
if (dom.tr) {
|
||||
return dom.tr;
|
||||
}
|
||||
|
||||
// a row for the append button
|
||||
var trAppend = document.createElement('tr');
|
||||
trAppend.node = this;
|
||||
dom.tr = trAppend;
|
||||
|
||||
// TODO: consistent naming
|
||||
|
||||
if (this.editor.mode.edit) {
|
||||
// a cell for the dragarea column
|
||||
dom.tdDrag = document.createElement('td');
|
||||
|
||||
// create context menu
|
||||
var tdMenu = document.createElement('td');
|
||||
dom.tdMenu = tdMenu;
|
||||
var menu = document.createElement('button');
|
||||
menu.className = 'contextmenu';
|
||||
menu.title = 'Click to open the actions menu (Ctrl+M)';
|
||||
dom.menu = menu;
|
||||
tdMenu.appendChild(dom.menu);
|
||||
}
|
||||
|
||||
// a cell for the contents (showing text 'empty')
|
||||
var tdAppend = document.createElement('td');
|
||||
var domText = document.createElement('div');
|
||||
domText.innerHTML = '(empty)';
|
||||
domText.className = 'readonly';
|
||||
tdAppend.appendChild(domText);
|
||||
dom.td = tdAppend;
|
||||
dom.text = domText;
|
||||
|
||||
this.updateDom();
|
||||
|
||||
return trAppend;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the HTML dom of the Node
|
||||
*/
|
||||
AppendNode.prototype.updateDom = function () {
|
||||
var dom = this.dom;
|
||||
var tdAppend = dom.td;
|
||||
if (tdAppend) {
|
||||
tdAppend.style.paddingLeft = (this.getLevel() * 24 + 26) + 'px';
|
||||
// TODO: not so nice hard coded offset
|
||||
}
|
||||
|
||||
var domText = dom.text;
|
||||
if (domText) {
|
||||
domText.innerHTML = '(empty ' + this.parent.type + ')';
|
||||
}
|
||||
|
||||
// attach or detach the contents of the append node:
|
||||
// hide when the parent has childs, show when the parent has no childs
|
||||
var trAppend = dom.tr;
|
||||
if (!this.isVisible()) {
|
||||
if (dom.tr.firstChild) {
|
||||
if (dom.tdDrag) {
|
||||
trAppend.removeChild(dom.tdDrag);
|
||||
}
|
||||
if (dom.tdMenu) {
|
||||
trAppend.removeChild(dom.tdMenu);
|
||||
}
|
||||
trAppend.removeChild(tdAppend);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!dom.tr.firstChild) {
|
||||
if (dom.tdDrag) {
|
||||
trAppend.appendChild(dom.tdDrag);
|
||||
}
|
||||
if (dom.tdMenu) {
|
||||
trAppend.appendChild(dom.tdMenu);
|
||||
}
|
||||
trAppend.appendChild(tdAppend);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check whether the AppendNode is currently visible.
|
||||
* the AppendNode is visible when its parent has no childs (i.e. is empty).
|
||||
* @return {boolean} isVisible
|
||||
*/
|
||||
AppendNode.prototype.isVisible = function () {
|
||||
return (this.parent.childs.length == 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Show a contextmenu for this node
|
||||
* @param {HTMLElement} anchor The element to attach the menu to.
|
||||
* @param {function} [onClose] Callback method called when the context menu
|
||||
* is being closed.
|
||||
*/
|
||||
AppendNode.prototype.showContextMenu = function (anchor, onClose) {
|
||||
var node = this;
|
||||
var titles = Node.TYPE_TITLES;
|
||||
var items = [
|
||||
// create append button
|
||||
{
|
||||
'text': 'Append',
|
||||
'title': 'Append a new field with type \'auto\' (Ctrl+Shift+Ins)',
|
||||
'submenuTitle': 'Select the type of the field to be appended',
|
||||
'className': 'insert',
|
||||
'click': function () {
|
||||
node._onAppend('', '', 'auto');
|
||||
},
|
||||
'submenu': [
|
||||
{
|
||||
'text': 'Auto',
|
||||
'className': 'type-auto',
|
||||
'title': titles.auto,
|
||||
'click': function () {
|
||||
node._onAppend('', '', 'auto');
|
||||
}
|
||||
},
|
||||
{
|
||||
'text': 'Array',
|
||||
'className': 'type-array',
|
||||
'title': titles.array,
|
||||
'click': function () {
|
||||
node._onAppend('', []);
|
||||
}
|
||||
},
|
||||
{
|
||||
'text': 'Object',
|
||||
'className': 'type-object',
|
||||
'title': titles.object,
|
||||
'click': function () {
|
||||
node._onAppend('', {});
|
||||
}
|
||||
},
|
||||
{
|
||||
'text': 'String',
|
||||
'className': 'type-string',
|
||||
'title': titles.string,
|
||||
'click': function () {
|
||||
node._onAppend('', '', 'string');
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
var menu = new ContextMenu(items, {close: onClose});
|
||||
menu.show(anchor);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle an event. The event is catched centrally by the editor
|
||||
* @param {Event} event
|
||||
*/
|
||||
AppendNode.prototype.onEvent = function (event) {
|
||||
var type = event.type;
|
||||
var target = event.target || event.srcElement;
|
||||
var dom = this.dom;
|
||||
|
||||
// highlight the append nodes parent
|
||||
var menu = dom.menu;
|
||||
if (target == menu) {
|
||||
if (type == 'mouseover') {
|
||||
this.editor.highlighter.highlight(this.parent);
|
||||
}
|
||||
else if (type == 'mouseout') {
|
||||
this.editor.highlighter.unhighlight();
|
||||
}
|
||||
}
|
||||
|
||||
// context menu events
|
||||
if (type == 'click' && target == dom.menu) {
|
||||
var highlighter = this.editor.highlighter;
|
||||
highlighter.highlight(this.parent);
|
||||
highlighter.lock();
|
||||
util.addClassName(dom.menu, 'selected');
|
||||
this.showContextMenu(dom.menu, function () {
|
||||
util.removeClassName(dom.menu, 'selected');
|
||||
highlighter.unlock();
|
||||
highlighter.unhighlight();
|
||||
});
|
||||
}
|
||||
|
||||
if (type == 'keydown') {
|
||||
this.onKeyDown(event);
|
||||
}
|
||||
};
|
|
@ -1,440 +1,444 @@
|
|||
/**
|
||||
* A context menu
|
||||
* @param {Object[]} items Array containing the menu structure
|
||||
* TODO: describe structure
|
||||
* @param {Object} [options] Object with options. Available options:
|
||||
* {function} close Callback called when the
|
||||
* context menu is being closed.
|
||||
* @constructor
|
||||
*/
|
||||
function ContextMenu (items, options) {
|
||||
this.dom = {};
|
||||
define(['./util'], function (util) {
|
||||
|
||||
var me = this;
|
||||
var dom = this.dom;
|
||||
this.anchor = undefined;
|
||||
this.items = items;
|
||||
this.eventListeners = {};
|
||||
this.selection = undefined; // holds the selection before the menu was opened
|
||||
this.visibleSubmenu = undefined;
|
||||
this.onClose = options ? options.close : undefined;
|
||||
/**
|
||||
* A context menu
|
||||
* @param {Object[]} items Array containing the menu structure
|
||||
* TODO: describe structure
|
||||
* @param {Object} [options] Object with options. Available options:
|
||||
* {function} close Callback called when the
|
||||
* context menu is being closed.
|
||||
* @constructor
|
||||
*/
|
||||
function ContextMenu (items, options) {
|
||||
this.dom = {};
|
||||
|
||||
// create a container element
|
||||
var menu = document.createElement('div');
|
||||
menu.className = 'jsoneditor-contextmenu';
|
||||
dom.menu = menu;
|
||||
var me = this;
|
||||
var dom = this.dom;
|
||||
this.anchor = undefined;
|
||||
this.items = items;
|
||||
this.eventListeners = {};
|
||||
this.selection = undefined; // holds the selection before the menu was opened
|
||||
this.visibleSubmenu = undefined;
|
||||
this.onClose = options ? options.close : undefined;
|
||||
|
||||
// create a list to hold the menu items
|
||||
var list = document.createElement('ul');
|
||||
list.className = 'menu';
|
||||
menu.appendChild(list);
|
||||
dom.list = list;
|
||||
dom.items = []; // list with all buttons
|
||||
// create a container element
|
||||
var menu = document.createElement('div');
|
||||
menu.className = 'jsoneditor-contextmenu';
|
||||
dom.menu = menu;
|
||||
|
||||
// create a (non-visible) button to set the focus to the menu
|
||||
var focusButton = document.createElement('button');
|
||||
dom.focusButton = focusButton;
|
||||
var li = document.createElement('li');
|
||||
li.style.overflow = 'hidden';
|
||||
li.style.height = '0';
|
||||
li.appendChild(focusButton);
|
||||
list.appendChild(li);
|
||||
// create a list to hold the menu items
|
||||
var list = document.createElement('ul');
|
||||
list.className = 'menu';
|
||||
menu.appendChild(list);
|
||||
dom.list = list;
|
||||
dom.items = []; // list with all buttons
|
||||
|
||||
function createMenuItems (list, domItems, items) {
|
||||
items.forEach(function (item) {
|
||||
if (item.type == 'separator') {
|
||||
// create a separator
|
||||
var separator = document.createElement('div');
|
||||
separator.className = 'separator';
|
||||
li = document.createElement('li');
|
||||
li.appendChild(separator);
|
||||
list.appendChild(li);
|
||||
}
|
||||
else {
|
||||
var domItem = {};
|
||||
// create a (non-visible) button to set the focus to the menu
|
||||
var focusButton = document.createElement('button');
|
||||
dom.focusButton = focusButton;
|
||||
var li = document.createElement('li');
|
||||
li.style.overflow = 'hidden';
|
||||
li.style.height = '0';
|
||||
li.appendChild(focusButton);
|
||||
list.appendChild(li);
|
||||
|
||||
// create a menu item
|
||||
var li = document.createElement('li');
|
||||
list.appendChild(li);
|
||||
|
||||
// create a button in the menu item
|
||||
var button = document.createElement('button');
|
||||
button.className = item.className;
|
||||
domItem.button = button;
|
||||
if (item.title) {
|
||||
button.title = item.title;
|
||||
}
|
||||
if (item.click) {
|
||||
button.onclick = function () {
|
||||
me.hide();
|
||||
item.click();
|
||||
};
|
||||
}
|
||||
li.appendChild(button);
|
||||
|
||||
// create the contents of the button
|
||||
if (item.submenu) {
|
||||
// add the icon to the button
|
||||
var divIcon = document.createElement('div');
|
||||
divIcon.className = 'icon';
|
||||
button.appendChild(divIcon);
|
||||
button.appendChild(document.createTextNode(item.text));
|
||||
|
||||
var buttonSubmenu;
|
||||
if (item.click) {
|
||||
// submenu and a button with a click handler
|
||||
button.className += ' default';
|
||||
|
||||
var buttonExpand = document.createElement('button');
|
||||
domItem.buttonExpand = buttonExpand;
|
||||
buttonExpand.className = 'expand';
|
||||
buttonExpand.innerHTML = '<div class="expand"></div>';
|
||||
li.appendChild(buttonExpand);
|
||||
if (item.submenuTitle) {
|
||||
buttonExpand.title = item.submenuTitle;
|
||||
}
|
||||
|
||||
buttonSubmenu = buttonExpand;
|
||||
}
|
||||
else {
|
||||
// submenu and a button without a click handler
|
||||
var divExpand = document.createElement('div');
|
||||
divExpand.className = 'expand';
|
||||
button.appendChild(divExpand);
|
||||
|
||||
buttonSubmenu = button;
|
||||
}
|
||||
|
||||
// attach a handler to expand/collapse the submenu
|
||||
buttonSubmenu.onclick = function () {
|
||||
me._onExpandItem(domItem);
|
||||
buttonSubmenu.focus();
|
||||
};
|
||||
|
||||
// create the submenu
|
||||
var domSubItems = [];
|
||||
domItem.subItems = domSubItems;
|
||||
var ul = document.createElement('ul');
|
||||
domItem.ul = ul;
|
||||
ul.className = 'menu';
|
||||
ul.style.height = '0';
|
||||
li.appendChild(ul);
|
||||
createMenuItems(ul, domSubItems, item.submenu);
|
||||
function createMenuItems (list, domItems, items) {
|
||||
items.forEach(function (item) {
|
||||
if (item.type == 'separator') {
|
||||
// create a separator
|
||||
var separator = document.createElement('div');
|
||||
separator.className = 'separator';
|
||||
li = document.createElement('li');
|
||||
li.appendChild(separator);
|
||||
list.appendChild(li);
|
||||
}
|
||||
else {
|
||||
// no submenu, just a button with clickhandler
|
||||
button.innerHTML = '<div class="icon"></div>' + item.text;
|
||||
}
|
||||
var domItem = {};
|
||||
|
||||
domItems.push(domItem);
|
||||
}
|
||||
// create a menu item
|
||||
var li = document.createElement('li');
|
||||
list.appendChild(li);
|
||||
|
||||
// create a button in the menu item
|
||||
var button = document.createElement('button');
|
||||
button.className = item.className;
|
||||
domItem.button = button;
|
||||
if (item.title) {
|
||||
button.title = item.title;
|
||||
}
|
||||
if (item.click) {
|
||||
button.onclick = function () {
|
||||
me.hide();
|
||||
item.click();
|
||||
};
|
||||
}
|
||||
li.appendChild(button);
|
||||
|
||||
// create the contents of the button
|
||||
if (item.submenu) {
|
||||
// add the icon to the button
|
||||
var divIcon = document.createElement('div');
|
||||
divIcon.className = 'icon';
|
||||
button.appendChild(divIcon);
|
||||
button.appendChild(document.createTextNode(item.text));
|
||||
|
||||
var buttonSubmenu;
|
||||
if (item.click) {
|
||||
// submenu and a button with a click handler
|
||||
button.className += ' default';
|
||||
|
||||
var buttonExpand = document.createElement('button');
|
||||
domItem.buttonExpand = buttonExpand;
|
||||
buttonExpand.className = 'expand';
|
||||
buttonExpand.innerHTML = '<div class="expand"></div>';
|
||||
li.appendChild(buttonExpand);
|
||||
if (item.submenuTitle) {
|
||||
buttonExpand.title = item.submenuTitle;
|
||||
}
|
||||
|
||||
buttonSubmenu = buttonExpand;
|
||||
}
|
||||
else {
|
||||
// submenu and a button without a click handler
|
||||
var divExpand = document.createElement('div');
|
||||
divExpand.className = 'expand';
|
||||
button.appendChild(divExpand);
|
||||
|
||||
buttonSubmenu = button;
|
||||
}
|
||||
|
||||
// attach a handler to expand/collapse the submenu
|
||||
buttonSubmenu.onclick = function () {
|
||||
me._onExpandItem(domItem);
|
||||
buttonSubmenu.focus();
|
||||
};
|
||||
|
||||
// create the submenu
|
||||
var domSubItems = [];
|
||||
domItem.subItems = domSubItems;
|
||||
var ul = document.createElement('ul');
|
||||
domItem.ul = ul;
|
||||
ul.className = 'menu';
|
||||
ul.style.height = '0';
|
||||
li.appendChild(ul);
|
||||
createMenuItems(ul, domSubItems, item.submenu);
|
||||
}
|
||||
else {
|
||||
// no submenu, just a button with clickhandler
|
||||
button.innerHTML = '<div class="icon"></div>' + item.text;
|
||||
}
|
||||
|
||||
domItems.push(domItem);
|
||||
}
|
||||
});
|
||||
}
|
||||
createMenuItems(list, this.dom.items, items);
|
||||
|
||||
// TODO: when the editor is small, show the submenu on the right instead of inline?
|
||||
|
||||
// calculate the max height of the menu with one submenu expanded
|
||||
this.maxHeight = 0; // height in pixels
|
||||
items.forEach(function (item) {
|
||||
var height = (items.length + (item.submenu ? item.submenu.length : 0)) * 24;
|
||||
me.maxHeight = Math.max(me.maxHeight, height);
|
||||
});
|
||||
}
|
||||
createMenuItems(list, this.dom.items, items);
|
||||
|
||||
// TODO: when the editor is small, show the submenu on the right instead of inline?
|
||||
/**
|
||||
* Get the currently visible buttons
|
||||
* @return {Array.<HTMLElement>} buttons
|
||||
* @private
|
||||
*/
|
||||
ContextMenu.prototype._getVisibleButtons = function () {
|
||||
var buttons = [];
|
||||
var me = this;
|
||||
this.dom.items.forEach(function (item) {
|
||||
buttons.push(item.button);
|
||||
if (item.buttonExpand) {
|
||||
buttons.push(item.buttonExpand);
|
||||
}
|
||||
if (item.subItems && item == me.expandedItem) {
|
||||
item.subItems.forEach(function (subItem) {
|
||||
buttons.push(subItem.button);
|
||||
if (subItem.buttonExpand) {
|
||||
buttons.push(subItem.buttonExpand);
|
||||
}
|
||||
// TODO: change to fully recursive method
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// calculate the max height of the menu with one submenu expanded
|
||||
this.maxHeight = 0; // height in pixels
|
||||
items.forEach(function (item) {
|
||||
var height = (items.length + (item.submenu ? item.submenu.length : 0)) * 24;
|
||||
me.maxHeight = Math.max(me.maxHeight, height);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently visible buttons
|
||||
* @return {Array.<HTMLElement>} buttons
|
||||
* @private
|
||||
*/
|
||||
ContextMenu.prototype._getVisibleButtons = function () {
|
||||
var buttons = [];
|
||||
var me = this;
|
||||
this.dom.items.forEach(function (item) {
|
||||
buttons.push(item.button);
|
||||
if (item.buttonExpand) {
|
||||
buttons.push(item.buttonExpand);
|
||||
}
|
||||
if (item.subItems && item == me.expandedItem) {
|
||||
item.subItems.forEach(function (subItem) {
|
||||
buttons.push(subItem.button);
|
||||
if (subItem.buttonExpand) {
|
||||
buttons.push(subItem.buttonExpand);
|
||||
}
|
||||
// TODO: change to fully recursive method
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return buttons;
|
||||
};
|
||||
return buttons;
|
||||
};
|
||||
|
||||
// currently displayed context menu, a singleton. We may only have one visible context menu
|
||||
ContextMenu.visibleMenu = undefined;
|
||||
|
||||
/**
|
||||
* Attach the menu to an anchor
|
||||
* @param {HTMLElement} anchor
|
||||
*/
|
||||
ContextMenu.prototype.show = function (anchor) {
|
||||
this.hide();
|
||||
|
||||
// calculate whether the menu fits below the anchor
|
||||
var windowHeight = window.innerHeight,
|
||||
windowScroll = (window.pageYOffset || document.scrollTop || 0),
|
||||
windowBottom = windowHeight + windowScroll,
|
||||
anchorHeight = anchor.offsetHeight,
|
||||
menuHeight = this.maxHeight;
|
||||
|
||||
// position the menu
|
||||
var left = util.getAbsoluteLeft(anchor);
|
||||
var top = util.getAbsoluteTop(anchor);
|
||||
if (top + anchorHeight + menuHeight < windowBottom) {
|
||||
// display the menu below the anchor
|
||||
this.dom.menu.style.left = left + 'px';
|
||||
this.dom.menu.style.top = (top + anchorHeight) + 'px';
|
||||
this.dom.menu.style.bottom = '';
|
||||
}
|
||||
else {
|
||||
// display the menu above the anchor
|
||||
this.dom.menu.style.left = left + 'px';
|
||||
this.dom.menu.style.top = '';
|
||||
this.dom.menu.style.bottom = (windowHeight - top) + 'px';
|
||||
}
|
||||
|
||||
// attach the menu to the document
|
||||
document.body.appendChild(this.dom.menu);
|
||||
|
||||
// create and attach event listeners
|
||||
var me = this;
|
||||
var list = this.dom.list;
|
||||
this.eventListeners.mousedown = util.addEventListener(
|
||||
document, 'mousedown', function (event) {
|
||||
// hide menu on click outside of the menu
|
||||
var target = event.target;
|
||||
if ((target != list) && !me._isChildOf(target, list)) {
|
||||
me.hide();
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
this.eventListeners.mousewheel = util.addEventListener(
|
||||
document, 'mousewheel', function (event) {
|
||||
// block scrolling when context menu is visible
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
});
|
||||
this.eventListeners.keydown = util.addEventListener(
|
||||
document, 'keydown', function (event) {
|
||||
me._onKeyDown(event);
|
||||
});
|
||||
|
||||
// move focus to the first button in the context menu
|
||||
this.selection = util.getSelection();
|
||||
this.anchor = anchor;
|
||||
setTimeout(function () {
|
||||
me.dom.focusButton.focus();
|
||||
}, 0);
|
||||
|
||||
if (ContextMenu.visibleMenu) {
|
||||
ContextMenu.visibleMenu.hide();
|
||||
}
|
||||
ContextMenu.visibleMenu = this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Hide the context menu if visible
|
||||
*/
|
||||
ContextMenu.prototype.hide = function () {
|
||||
// remove the menu from the DOM
|
||||
if (this.dom.menu.parentNode) {
|
||||
this.dom.menu.parentNode.removeChild(this.dom.menu);
|
||||
if (this.onClose) {
|
||||
this.onClose();
|
||||
}
|
||||
}
|
||||
|
||||
// remove all event listeners
|
||||
// all event listeners are supposed to be attached to document.
|
||||
for (var name in this.eventListeners) {
|
||||
if (this.eventListeners.hasOwnProperty(name)) {
|
||||
var fn = this.eventListeners[name];
|
||||
if (fn) {
|
||||
util.removeEventListener(document, name, fn);
|
||||
}
|
||||
delete this.eventListeners[name];
|
||||
}
|
||||
}
|
||||
|
||||
if (ContextMenu.visibleMenu == this) {
|
||||
ContextMenu.visibleMenu = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Expand a submenu
|
||||
* Any currently expanded submenu will be hided.
|
||||
* @param {Object} domItem
|
||||
* @private
|
||||
*/
|
||||
ContextMenu.prototype._onExpandItem = function (domItem) {
|
||||
var me = this;
|
||||
var alreadyVisible = (domItem == this.expandedItem);
|
||||
|
||||
// hide the currently visible submenu
|
||||
var expandedItem = this.expandedItem;
|
||||
if (expandedItem) {
|
||||
//var ul = expandedItem.ul;
|
||||
expandedItem.ul.style.height = '0';
|
||||
expandedItem.ul.style.padding = '';
|
||||
setTimeout(function () {
|
||||
if (me.expandedItem != expandedItem) {
|
||||
expandedItem.ul.style.display = '';
|
||||
util.removeClassName(expandedItem.ul.parentNode, 'selected');
|
||||
}
|
||||
}, 300); // timeout duration must match the css transition duration
|
||||
this.expandedItem = undefined;
|
||||
}
|
||||
|
||||
if (!alreadyVisible) {
|
||||
var ul = domItem.ul;
|
||||
ul.style.display = 'block';
|
||||
var height = ul.clientHeight; // force a reflow in Firefox
|
||||
setTimeout(function () {
|
||||
if (me.expandedItem == domItem) {
|
||||
ul.style.height = (ul.childNodes.length * 24) + 'px';
|
||||
ul.style.padding = '5px 10px';
|
||||
}
|
||||
}, 0);
|
||||
util.addClassName(ul.parentNode, 'selected');
|
||||
this.expandedItem = domItem;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle onkeydown event
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
ContextMenu.prototype._onKeyDown = function (event) {
|
||||
var target = event.target;
|
||||
var keynum = event.which;
|
||||
var handled = false;
|
||||
var buttons, targetIndex, prevButton, nextButton;
|
||||
|
||||
if (keynum == 27) { // ESC
|
||||
// hide the menu on ESC key
|
||||
|
||||
// restore previous selection and focus
|
||||
if (this.selection) {
|
||||
util.setSelection(this.selection);
|
||||
}
|
||||
if (this.anchor) {
|
||||
this.anchor.focus();
|
||||
}
|
||||
ContextMenu.visibleMenu = undefined;
|
||||
|
||||
/**
|
||||
* Attach the menu to an anchor
|
||||
* @param {HTMLElement} anchor
|
||||
*/
|
||||
ContextMenu.prototype.show = function (anchor) {
|
||||
this.hide();
|
||||
|
||||
handled = true;
|
||||
}
|
||||
else if (keynum == 9) { // Tab
|
||||
if (!event.shiftKey) { // Tab
|
||||
buttons = this._getVisibleButtons();
|
||||
targetIndex = buttons.indexOf(target);
|
||||
if (targetIndex == buttons.length - 1) {
|
||||
// move to first button
|
||||
buttons[0].focus();
|
||||
handled = true;
|
||||
// calculate whether the menu fits below the anchor
|
||||
var windowHeight = window.innerHeight,
|
||||
windowScroll = (window.pageYOffset || document.scrollTop || 0),
|
||||
windowBottom = windowHeight + windowScroll,
|
||||
anchorHeight = anchor.offsetHeight,
|
||||
menuHeight = this.maxHeight;
|
||||
|
||||
// position the menu
|
||||
var left = util.getAbsoluteLeft(anchor);
|
||||
var top = util.getAbsoluteTop(anchor);
|
||||
if (top + anchorHeight + menuHeight < windowBottom) {
|
||||
// display the menu below the anchor
|
||||
this.dom.menu.style.left = left + 'px';
|
||||
this.dom.menu.style.top = (top + anchorHeight) + 'px';
|
||||
this.dom.menu.style.bottom = '';
|
||||
}
|
||||
else {
|
||||
// display the menu above the anchor
|
||||
this.dom.menu.style.left = left + 'px';
|
||||
this.dom.menu.style.top = '';
|
||||
this.dom.menu.style.bottom = (windowHeight - top) + 'px';
|
||||
}
|
||||
|
||||
// attach the menu to the document
|
||||
document.body.appendChild(this.dom.menu);
|
||||
|
||||
// create and attach event listeners
|
||||
var me = this;
|
||||
var list = this.dom.list;
|
||||
this.eventListeners.mousedown = util.addEventListener(
|
||||
document, 'mousedown', function (event) {
|
||||
// hide menu on click outside of the menu
|
||||
var target = event.target;
|
||||
if ((target != list) && !me._isChildOf(target, list)) {
|
||||
me.hide();
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
this.eventListeners.mousewheel = util.addEventListener(
|
||||
document, 'mousewheel', function (event) {
|
||||
// block scrolling when context menu is visible
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
});
|
||||
this.eventListeners.keydown = util.addEventListener(
|
||||
document, 'keydown', function (event) {
|
||||
me._onKeyDown(event);
|
||||
});
|
||||
|
||||
// move focus to the first button in the context menu
|
||||
this.selection = util.getSelection();
|
||||
this.anchor = anchor;
|
||||
setTimeout(function () {
|
||||
me.dom.focusButton.focus();
|
||||
}, 0);
|
||||
|
||||
if (ContextMenu.visibleMenu) {
|
||||
ContextMenu.visibleMenu.hide();
|
||||
}
|
||||
ContextMenu.visibleMenu = this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Hide the context menu if visible
|
||||
*/
|
||||
ContextMenu.prototype.hide = function () {
|
||||
// remove the menu from the DOM
|
||||
if (this.dom.menu.parentNode) {
|
||||
this.dom.menu.parentNode.removeChild(this.dom.menu);
|
||||
if (this.onClose) {
|
||||
this.onClose();
|
||||
}
|
||||
}
|
||||
else { // Shift+Tab
|
||||
buttons = this._getVisibleButtons();
|
||||
targetIndex = buttons.indexOf(target);
|
||||
if (targetIndex == 0) {
|
||||
// move to last button
|
||||
buttons[buttons.length - 1].focus();
|
||||
handled = true;
|
||||
|
||||
// remove all event listeners
|
||||
// all event listeners are supposed to be attached to document.
|
||||
for (var name in this.eventListeners) {
|
||||
if (this.eventListeners.hasOwnProperty(name)) {
|
||||
var fn = this.eventListeners[name];
|
||||
if (fn) {
|
||||
util.removeEventListener(document, name, fn);
|
||||
}
|
||||
delete this.eventListeners[name];
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (keynum == 37) { // Arrow Left
|
||||
if (target.className == 'expand') {
|
||||
|
||||
if (ContextMenu.visibleMenu == this) {
|
||||
ContextMenu.visibleMenu = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Expand a submenu
|
||||
* Any currently expanded submenu will be hided.
|
||||
* @param {Object} domItem
|
||||
* @private
|
||||
*/
|
||||
ContextMenu.prototype._onExpandItem = function (domItem) {
|
||||
var me = this;
|
||||
var alreadyVisible = (domItem == this.expandedItem);
|
||||
|
||||
// hide the currently visible submenu
|
||||
var expandedItem = this.expandedItem;
|
||||
if (expandedItem) {
|
||||
//var ul = expandedItem.ul;
|
||||
expandedItem.ul.style.height = '0';
|
||||
expandedItem.ul.style.padding = '';
|
||||
setTimeout(function () {
|
||||
if (me.expandedItem != expandedItem) {
|
||||
expandedItem.ul.style.display = '';
|
||||
util.removeClassName(expandedItem.ul.parentNode, 'selected');
|
||||
}
|
||||
}, 300); // timeout duration must match the css transition duration
|
||||
this.expandedItem = undefined;
|
||||
}
|
||||
|
||||
if (!alreadyVisible) {
|
||||
var ul = domItem.ul;
|
||||
ul.style.display = 'block';
|
||||
var height = ul.clientHeight; // force a reflow in Firefox
|
||||
setTimeout(function () {
|
||||
if (me.expandedItem == domItem) {
|
||||
ul.style.height = (ul.childNodes.length * 24) + 'px';
|
||||
ul.style.padding = '5px 10px';
|
||||
}
|
||||
}, 0);
|
||||
util.addClassName(ul.parentNode, 'selected');
|
||||
this.expandedItem = domItem;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle onkeydown event
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
ContextMenu.prototype._onKeyDown = function (event) {
|
||||
var target = event.target;
|
||||
var keynum = event.which;
|
||||
var handled = false;
|
||||
var buttons, targetIndex, prevButton, nextButton;
|
||||
|
||||
if (keynum == 27) { // ESC
|
||||
// hide the menu on ESC key
|
||||
|
||||
// restore previous selection and focus
|
||||
if (this.selection) {
|
||||
util.setSelection(this.selection);
|
||||
}
|
||||
if (this.anchor) {
|
||||
this.anchor.focus();
|
||||
}
|
||||
|
||||
this.hide();
|
||||
|
||||
handled = true;
|
||||
}
|
||||
else if (keynum == 9) { // Tab
|
||||
if (!event.shiftKey) { // Tab
|
||||
buttons = this._getVisibleButtons();
|
||||
targetIndex = buttons.indexOf(target);
|
||||
if (targetIndex == buttons.length - 1) {
|
||||
// move to first button
|
||||
buttons[0].focus();
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
else { // Shift+Tab
|
||||
buttons = this._getVisibleButtons();
|
||||
targetIndex = buttons.indexOf(target);
|
||||
if (targetIndex == 0) {
|
||||
// move to last button
|
||||
buttons[buttons.length - 1].focus();
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (keynum == 37) { // Arrow Left
|
||||
if (target.className == 'expand') {
|
||||
buttons = this._getVisibleButtons();
|
||||
targetIndex = buttons.indexOf(target);
|
||||
prevButton = buttons[targetIndex - 1];
|
||||
if (prevButton) {
|
||||
prevButton.focus();
|
||||
}
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
else if (keynum == 38) { // Arrow Up
|
||||
buttons = this._getVisibleButtons();
|
||||
targetIndex = buttons.indexOf(target);
|
||||
prevButton = buttons[targetIndex - 1];
|
||||
if (prevButton && prevButton.className == 'expand') {
|
||||
// skip expand button
|
||||
prevButton = buttons[targetIndex - 2];
|
||||
}
|
||||
if (!prevButton) {
|
||||
// move to last button
|
||||
prevButton = buttons[buttons.length - 1];
|
||||
}
|
||||
if (prevButton) {
|
||||
prevButton.focus();
|
||||
}
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
else if (keynum == 38) { // Arrow Up
|
||||
buttons = this._getVisibleButtons();
|
||||
targetIndex = buttons.indexOf(target);
|
||||
prevButton = buttons[targetIndex - 1];
|
||||
if (prevButton && prevButton.className == 'expand') {
|
||||
// skip expand button
|
||||
prevButton = buttons[targetIndex - 2];
|
||||
}
|
||||
if (!prevButton) {
|
||||
// move to last button
|
||||
prevButton = buttons[buttons.length - 1];
|
||||
}
|
||||
if (prevButton) {
|
||||
prevButton.focus();
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
else if (keynum == 39) { // Arrow Right
|
||||
buttons = this._getVisibleButtons();
|
||||
targetIndex = buttons.indexOf(target);
|
||||
nextButton = buttons[targetIndex + 1];
|
||||
if (nextButton && nextButton.className == 'expand') {
|
||||
nextButton.focus();
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
else if (keynum == 40) { // Arrow Down
|
||||
buttons = this._getVisibleButtons();
|
||||
targetIndex = buttons.indexOf(target);
|
||||
nextButton = buttons[targetIndex + 1];
|
||||
if (nextButton && nextButton.className == 'expand') {
|
||||
// skip expand button
|
||||
nextButton = buttons[targetIndex + 2];
|
||||
}
|
||||
if (!nextButton) {
|
||||
// move to first button
|
||||
nextButton = buttons[0];
|
||||
}
|
||||
if (nextButton) {
|
||||
nextButton.focus();
|
||||
handled = true;
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
// TODO: arrow left and right
|
||||
|
||||
if (handled) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Test if an element is a child of a parent element.
|
||||
* @param {Element} child
|
||||
* @param {Element} parent
|
||||
* @return {boolean} isChild
|
||||
*/
|
||||
ContextMenu.prototype._isChildOf = function (child, parent) {
|
||||
var e = child.parentNode;
|
||||
while (e) {
|
||||
if (e == parent) {
|
||||
return true;
|
||||
else if (keynum == 39) { // Arrow Right
|
||||
buttons = this._getVisibleButtons();
|
||||
targetIndex = buttons.indexOf(target);
|
||||
nextButton = buttons[targetIndex + 1];
|
||||
if (nextButton && nextButton.className == 'expand') {
|
||||
nextButton.focus();
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
e = e.parentNode;
|
||||
}
|
||||
else if (keynum == 40) { // Arrow Down
|
||||
buttons = this._getVisibleButtons();
|
||||
targetIndex = buttons.indexOf(target);
|
||||
nextButton = buttons[targetIndex + 1];
|
||||
if (nextButton && nextButton.className == 'expand') {
|
||||
// skip expand button
|
||||
nextButton = buttons[targetIndex + 2];
|
||||
}
|
||||
if (!nextButton) {
|
||||
// move to first button
|
||||
nextButton = buttons[0];
|
||||
}
|
||||
if (nextButton) {
|
||||
nextButton.focus();
|
||||
handled = true;
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
// TODO: arrow left and right
|
||||
|
||||
return false;
|
||||
};
|
||||
if (handled) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Test if an element is a child of a parent element.
|
||||
* @param {Element} child
|
||||
* @param {Element} parent
|
||||
* @return {boolean} isChild
|
||||
*/
|
||||
ContextMenu.prototype._isChildOf = function (child, parent) {
|
||||
var e = child.parentNode;
|
||||
while (e) {
|
||||
if (e == parent) {
|
||||
return true;
|
||||
}
|
||||
e = e.parentNode;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
return ContextMenu;
|
||||
});
|
||||
|
|
|
@ -1,82 +1,87 @@
|
|||
/**
|
||||
* The highlighter can highlight/unhighlight a node, and
|
||||
* animate the visibility of a context menu.
|
||||
* @constructor Highlighter
|
||||
*/
|
||||
function Highlighter () {
|
||||
this.locked = false;
|
||||
}
|
||||
define(function () {
|
||||
|
||||
/**
|
||||
* Hightlight given node and its childs
|
||||
* @param {Node} node
|
||||
*/
|
||||
Highlighter.prototype.highlight = function (node) {
|
||||
if (this.locked) {
|
||||
return;
|
||||
/**
|
||||
* The highlighter can highlight/unhighlight a node, and
|
||||
* animate the visibility of a context menu.
|
||||
* @constructor Highlighter
|
||||
*/
|
||||
function Highlighter () {
|
||||
this.locked = false;
|
||||
}
|
||||
|
||||
if (this.node != node) {
|
||||
// unhighlight current node
|
||||
if (this.node) {
|
||||
this.node.setHighlight(false);
|
||||
/**
|
||||
* Hightlight given node and its childs
|
||||
* @param {Node} node
|
||||
*/
|
||||
Highlighter.prototype.highlight = function (node) {
|
||||
if (this.locked) {
|
||||
return;
|
||||
}
|
||||
|
||||
// highlight new node
|
||||
this.node = node;
|
||||
this.node.setHighlight(true);
|
||||
}
|
||||
if (this.node != node) {
|
||||
// unhighlight current node
|
||||
if (this.node) {
|
||||
this.node.setHighlight(false);
|
||||
}
|
||||
|
||||
// cancel any current timeout
|
||||
this._cancelUnhighlight();
|
||||
};
|
||||
// highlight new node
|
||||
this.node = node;
|
||||
this.node.setHighlight(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unhighlight currently highlighted node.
|
||||
* Will be done after a delay
|
||||
*/
|
||||
Highlighter.prototype.unhighlight = function () {
|
||||
if (this.locked) {
|
||||
return;
|
||||
}
|
||||
|
||||
var me = this;
|
||||
if (this.node) {
|
||||
// cancel any current timeout
|
||||
this._cancelUnhighlight();
|
||||
};
|
||||
|
||||
// do the unhighlighting after a small delay, to prevent re-highlighting
|
||||
// the same node when moving from the drag-icon to the contextmenu-icon
|
||||
// or vice versa.
|
||||
this.unhighlightTimer = setTimeout(function () {
|
||||
me.node.setHighlight(false);
|
||||
me.node = undefined;
|
||||
me.unhighlightTimer = undefined;
|
||||
}, 0);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Unhighlight currently highlighted node.
|
||||
* Will be done after a delay
|
||||
*/
|
||||
Highlighter.prototype.unhighlight = function () {
|
||||
if (this.locked) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel an unhighlight action (if before the timeout of the unhighlight action)
|
||||
* @private
|
||||
*/
|
||||
Highlighter.prototype._cancelUnhighlight = function () {
|
||||
if (this.unhighlightTimer) {
|
||||
clearTimeout(this.unhighlightTimer);
|
||||
this.unhighlightTimer = undefined;
|
||||
}
|
||||
};
|
||||
var me = this;
|
||||
if (this.node) {
|
||||
this._cancelUnhighlight();
|
||||
|
||||
/**
|
||||
* Lock highlighting or unhighlighting nodes.
|
||||
* methods highlight and unhighlight do not work while locked.
|
||||
*/
|
||||
Highlighter.prototype.lock = function () {
|
||||
this.locked = true;
|
||||
};
|
||||
// do the unhighlighting after a small delay, to prevent re-highlighting
|
||||
// the same node when moving from the drag-icon to the contextmenu-icon
|
||||
// or vice versa.
|
||||
this.unhighlightTimer = setTimeout(function () {
|
||||
me.node.setHighlight(false);
|
||||
me.node = undefined;
|
||||
me.unhighlightTimer = undefined;
|
||||
}, 0);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Unlock highlighting or unhighlighting nodes
|
||||
*/
|
||||
Highlighter.prototype.unlock = function () {
|
||||
this.locked = false;
|
||||
};
|
||||
/**
|
||||
* Cancel an unhighlight action (if before the timeout of the unhighlight action)
|
||||
* @private
|
||||
*/
|
||||
Highlighter.prototype._cancelUnhighlight = function () {
|
||||
if (this.unhighlightTimer) {
|
||||
clearTimeout(this.unhighlightTimer);
|
||||
this.unhighlightTimer = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Lock highlighting or unhighlighting nodes.
|
||||
* methods highlight and unhighlight do not work while locked.
|
||||
*/
|
||||
Highlighter.prototype.lock = function () {
|
||||
this.locked = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Unlock highlighting or unhighlighting nodes
|
||||
*/
|
||||
Highlighter.prototype.unlock = function () {
|
||||
this.locked = false;
|
||||
};
|
||||
|
||||
return Highlighter;
|
||||
});
|
|
@ -1,218 +1,223 @@
|
|||
/**
|
||||
* @constructor History
|
||||
* Store action history, enables undo and redo
|
||||
* @param {JSONEditor} editor
|
||||
*/
|
||||
function History (editor) {
|
||||
this.editor = editor;
|
||||
this.clear();
|
||||
define(['./util'], function (util) {
|
||||
|
||||
// map with all supported actions
|
||||
this.actions = {
|
||||
'editField': {
|
||||
'undo': function (params) {
|
||||
params.node.updateField(params.oldValue);
|
||||
/**
|
||||
* @constructor History
|
||||
* Store action history, enables undo and redo
|
||||
* @param {JSONEditor} editor
|
||||
*/
|
||||
function History (editor) {
|
||||
this.editor = editor;
|
||||
this.clear();
|
||||
|
||||
// map with all supported actions
|
||||
this.actions = {
|
||||
'editField': {
|
||||
'undo': function (params) {
|
||||
params.node.updateField(params.oldValue);
|
||||
},
|
||||
'redo': function (params) {
|
||||
params.node.updateField(params.newValue);
|
||||
}
|
||||
},
|
||||
'redo': function (params) {
|
||||
params.node.updateField(params.newValue);
|
||||
}
|
||||
},
|
||||
'editValue': {
|
||||
'undo': function (params) {
|
||||
params.node.updateValue(params.oldValue);
|
||||
'editValue': {
|
||||
'undo': function (params) {
|
||||
params.node.updateValue(params.oldValue);
|
||||
},
|
||||
'redo': function (params) {
|
||||
params.node.updateValue(params.newValue);
|
||||
}
|
||||
},
|
||||
'redo': function (params) {
|
||||
params.node.updateValue(params.newValue);
|
||||
}
|
||||
},
|
||||
'appendNode': {
|
||||
'undo': function (params) {
|
||||
params.parent.removeChild(params.node);
|
||||
'appendNode': {
|
||||
'undo': function (params) {
|
||||
params.parent.removeChild(params.node);
|
||||
},
|
||||
'redo': function (params) {
|
||||
params.parent.appendChild(params.node);
|
||||
}
|
||||
},
|
||||
'redo': function (params) {
|
||||
params.parent.appendChild(params.node);
|
||||
}
|
||||
},
|
||||
'insertBeforeNode': {
|
||||
'undo': function (params) {
|
||||
params.parent.removeChild(params.node);
|
||||
'insertBeforeNode': {
|
||||
'undo': function (params) {
|
||||
params.parent.removeChild(params.node);
|
||||
},
|
||||
'redo': function (params) {
|
||||
params.parent.insertBefore(params.node, params.beforeNode);
|
||||
}
|
||||
},
|
||||
'redo': function (params) {
|
||||
params.parent.insertBefore(params.node, params.beforeNode);
|
||||
}
|
||||
},
|
||||
'insertAfterNode': {
|
||||
'undo': function (params) {
|
||||
params.parent.removeChild(params.node);
|
||||
'insertAfterNode': {
|
||||
'undo': function (params) {
|
||||
params.parent.removeChild(params.node);
|
||||
},
|
||||
'redo': function (params) {
|
||||
params.parent.insertAfter(params.node, params.afterNode);
|
||||
}
|
||||
},
|
||||
'redo': function (params) {
|
||||
params.parent.insertAfter(params.node, params.afterNode);
|
||||
}
|
||||
},
|
||||
'removeNode': {
|
||||
'undo': function (params) {
|
||||
var parent = params.parent;
|
||||
var beforeNode = parent.childs[params.index] || parent.append;
|
||||
parent.insertBefore(params.node, beforeNode);
|
||||
'removeNode': {
|
||||
'undo': function (params) {
|
||||
var parent = params.parent;
|
||||
var beforeNode = parent.childs[params.index] || parent.append;
|
||||
parent.insertBefore(params.node, beforeNode);
|
||||
},
|
||||
'redo': function (params) {
|
||||
params.parent.removeChild(params.node);
|
||||
}
|
||||
},
|
||||
'redo': function (params) {
|
||||
params.parent.removeChild(params.node);
|
||||
}
|
||||
},
|
||||
'duplicateNode': {
|
||||
'undo': function (params) {
|
||||
params.parent.removeChild(params.clone);
|
||||
'duplicateNode': {
|
||||
'undo': function (params) {
|
||||
params.parent.removeChild(params.clone);
|
||||
},
|
||||
'redo': function (params) {
|
||||
params.parent.insertAfter(params.clone, params.node);
|
||||
}
|
||||
},
|
||||
'redo': function (params) {
|
||||
params.parent.insertAfter(params.clone, params.node);
|
||||
}
|
||||
},
|
||||
'changeType': {
|
||||
'undo': function (params) {
|
||||
params.node.changeType(params.oldType);
|
||||
'changeType': {
|
||||
'undo': function (params) {
|
||||
params.node.changeType(params.oldType);
|
||||
},
|
||||
'redo': function (params) {
|
||||
params.node.changeType(params.newType);
|
||||
}
|
||||
},
|
||||
'redo': function (params) {
|
||||
params.node.changeType(params.newType);
|
||||
}
|
||||
},
|
||||
'moveNode': {
|
||||
'undo': function (params) {
|
||||
params.startParent.moveTo(params.node, params.startIndex);
|
||||
'moveNode': {
|
||||
'undo': function (params) {
|
||||
params.startParent.moveTo(params.node, params.startIndex);
|
||||
},
|
||||
'redo': function (params) {
|
||||
params.endParent.moveTo(params.node, params.endIndex);
|
||||
}
|
||||
},
|
||||
'redo': function (params) {
|
||||
params.endParent.moveTo(params.node, params.endIndex);
|
||||
}
|
||||
},
|
||||
'sort': {
|
||||
'undo': function (params) {
|
||||
var node = params.node;
|
||||
node.hideChilds();
|
||||
node.sort = params.oldSort;
|
||||
node.childs = params.oldChilds;
|
||||
node.showChilds();
|
||||
},
|
||||
'redo': function (params) {
|
||||
var node = params.node;
|
||||
node.hideChilds();
|
||||
node.sort = params.newSort;
|
||||
node.childs = params.newChilds;
|
||||
node.showChilds();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: restore the original caret position and selection with each undo
|
||||
// TODO: implement history for actions "expand", "collapse", "scroll", "setDocument"
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The method onChange is executed when the History is changed, and can
|
||||
* be overloaded.
|
||||
*/
|
||||
History.prototype.onChange = function () {};
|
||||
|
||||
/**
|
||||
* Add a new action to the history
|
||||
* @param {String} action The executed action. Available actions: "editField",
|
||||
* "editValue", "changeType", "appendNode",
|
||||
* "removeNode", "duplicateNode", "moveNode"
|
||||
* @param {Object} params Object containing parameters describing the change.
|
||||
* The parameters in params depend on the action (for
|
||||
* example for "editValue" the Node, old value, and new
|
||||
* value are provided). params contains all information
|
||||
* needed to undo or redo the action.
|
||||
*/
|
||||
History.prototype.add = function (action, params) {
|
||||
this.index++;
|
||||
this.history[this.index] = {
|
||||
'action': action,
|
||||
'params': params,
|
||||
'timestamp': new Date()
|
||||
};
|
||||
|
||||
// remove redo actions which are invalid now
|
||||
if (this.index < this.history.length - 1) {
|
||||
this.history.splice(this.index + 1, this.history.length - this.index - 1);
|
||||
}
|
||||
|
||||
// fire onchange event
|
||||
this.onChange();
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear history
|
||||
*/
|
||||
History.prototype.clear = function () {
|
||||
this.history = [];
|
||||
this.index = -1;
|
||||
|
||||
// fire onchange event
|
||||
this.onChange();
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if there is an action available for undo
|
||||
* @return {Boolean} canUndo
|
||||
*/
|
||||
History.prototype.canUndo = function () {
|
||||
return (this.index >= 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if there is an action available for redo
|
||||
* @return {Boolean} canRedo
|
||||
*/
|
||||
History.prototype.canRedo = function () {
|
||||
return (this.index < this.history.length - 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Undo the last action
|
||||
*/
|
||||
History.prototype.undo = function () {
|
||||
if (this.canUndo()) {
|
||||
var obj = this.history[this.index];
|
||||
if (obj) {
|
||||
var action = this.actions[obj.action];
|
||||
if (action && action.undo) {
|
||||
action.undo(obj.params);
|
||||
if (obj.params.oldSelection) {
|
||||
this.editor.setSelection(obj.params.oldSelection);
|
||||
'sort': {
|
||||
'undo': function (params) {
|
||||
var node = params.node;
|
||||
node.hideChilds();
|
||||
node.sort = params.oldSort;
|
||||
node.childs = params.oldChilds;
|
||||
node.showChilds();
|
||||
},
|
||||
'redo': function (params) {
|
||||
var node = params.node;
|
||||
node.hideChilds();
|
||||
node.sort = params.newSort;
|
||||
node.childs = params.newChilds;
|
||||
node.showChilds();
|
||||
}
|
||||
}
|
||||
else {
|
||||
util.log('Error: unknown action "' + obj.action + '"');
|
||||
}
|
||||
}
|
||||
this.index--;
|
||||
|
||||
// fire onchange event
|
||||
this.onChange();
|
||||
// TODO: restore the original caret position and selection with each undo
|
||||
// TODO: implement history for actions "expand", "collapse", "scroll", "setDocument"
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Redo the last action
|
||||
*/
|
||||
History.prototype.redo = function () {
|
||||
if (this.canRedo()) {
|
||||
/**
|
||||
* The method onChange is executed when the History is changed, and can
|
||||
* be overloaded.
|
||||
*/
|
||||
History.prototype.onChange = function () {};
|
||||
|
||||
/**
|
||||
* Add a new action to the history
|
||||
* @param {String} action The executed action. Available actions: "editField",
|
||||
* "editValue", "changeType", "appendNode",
|
||||
* "removeNode", "duplicateNode", "moveNode"
|
||||
* @param {Object} params Object containing parameters describing the change.
|
||||
* The parameters in params depend on the action (for
|
||||
* example for "editValue" the Node, old value, and new
|
||||
* value are provided). params contains all information
|
||||
* needed to undo or redo the action.
|
||||
*/
|
||||
History.prototype.add = function (action, params) {
|
||||
this.index++;
|
||||
this.history[this.index] = {
|
||||
'action': action,
|
||||
'params': params,
|
||||
'timestamp': new Date()
|
||||
};
|
||||
|
||||
var obj = this.history[this.index];
|
||||
if (obj) {
|
||||
var action = this.actions[obj.action];
|
||||
if (action && action.redo) {
|
||||
action.redo(obj.params);
|
||||
if (obj.params.newSelection) {
|
||||
this.editor.setSelection(obj.params.newSelection);
|
||||
}
|
||||
}
|
||||
else {
|
||||
util.log('Error: unknown action "' + obj.action + '"');
|
||||
}
|
||||
// remove redo actions which are invalid now
|
||||
if (this.index < this.history.length - 1) {
|
||||
this.history.splice(this.index + 1, this.history.length - this.index - 1);
|
||||
}
|
||||
|
||||
// fire onchange event
|
||||
this.onChange();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear history
|
||||
*/
|
||||
History.prototype.clear = function () {
|
||||
this.history = [];
|
||||
this.index = -1;
|
||||
|
||||
// fire onchange event
|
||||
this.onChange();
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if there is an action available for undo
|
||||
* @return {Boolean} canUndo
|
||||
*/
|
||||
History.prototype.canUndo = function () {
|
||||
return (this.index >= 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if there is an action available for redo
|
||||
* @return {Boolean} canRedo
|
||||
*/
|
||||
History.prototype.canRedo = function () {
|
||||
return (this.index < this.history.length - 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Undo the last action
|
||||
*/
|
||||
History.prototype.undo = function () {
|
||||
if (this.canUndo()) {
|
||||
var obj = this.history[this.index];
|
||||
if (obj) {
|
||||
var action = this.actions[obj.action];
|
||||
if (action && action.undo) {
|
||||
action.undo(obj.params);
|
||||
if (obj.params.oldSelection) {
|
||||
this.editor.setSelection(obj.params.oldSelection);
|
||||
}
|
||||
}
|
||||
else {
|
||||
util.log('Error: unknown action "' + obj.action + '"');
|
||||
}
|
||||
}
|
||||
this.index--;
|
||||
|
||||
// fire onchange event
|
||||
this.onChange();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Redo the last action
|
||||
*/
|
||||
History.prototype.redo = function () {
|
||||
if (this.canRedo()) {
|
||||
this.index++;
|
||||
|
||||
var obj = this.history[this.index];
|
||||
if (obj) {
|
||||
var action = this.actions[obj.action];
|
||||
if (action && action.redo) {
|
||||
action.redo(obj.params);
|
||||
if (obj.params.newSelection) {
|
||||
this.editor.setSelection(obj.params.newSelection);
|
||||
}
|
||||
}
|
||||
else {
|
||||
util.log('Error: unknown action "' + obj.action + '"');
|
||||
}
|
||||
}
|
||||
|
||||
// fire onchange event
|
||||
this.onChange();
|
||||
}
|
||||
};
|
||||
|
||||
return History;
|
||||
});
|
||||
|
|
|
@ -1,49 +1,51 @@
|
|||
/**
|
||||
* @constructor JSONEditor
|
||||
* @param {Element} container Container element
|
||||
* @param {Object} [options] Object with options. available options:
|
||||
* {String} mode Editor mode. Available values:
|
||||
* 'tree' (default), 'view',
|
||||
* 'form', 'text', and 'code'.
|
||||
* {function} change Callback method, triggered
|
||||
* on change of contents
|
||||
* {Boolean} search Enable search box.
|
||||
* True by default
|
||||
* Only applicable for modes
|
||||
* 'tree', 'view', and 'form'
|
||||
* {Boolean} history Enable history (undo/redo).
|
||||
* True by default
|
||||
* Only applicable for modes
|
||||
* 'tree', 'view', and 'form'
|
||||
* {String} name Field name for the root node.
|
||||
* Only applicable for modes
|
||||
* 'tree', 'view', and 'form'
|
||||
* {Number} indentation Number of indentation
|
||||
* spaces. 4 by default.
|
||||
* Only applicable for
|
||||
* modes 'text' and 'code'
|
||||
* @param {Object | undefined} json JSON object
|
||||
*/
|
||||
function JSONEditor (container, options, json) {
|
||||
if (!(this instanceof JSONEditor)) {
|
||||
throw new Error('JSONEditor constructor called without "new".');
|
||||
define(['./TreeEditor', './TextEditor', './util'], function (TreeEditor, TextEditor, util) {
|
||||
|
||||
/**
|
||||
* @constructor JSONEditor
|
||||
* @param {Element} container Container element
|
||||
* @param {Object} [options] Object with options. available options:
|
||||
* {String} mode Editor mode. Available values:
|
||||
* 'tree' (default), 'view',
|
||||
* 'form', 'text', and 'code'.
|
||||
* {function} change Callback method, triggered
|
||||
* on change of contents
|
||||
* {Boolean} search Enable search box.
|
||||
* True by default
|
||||
* Only applicable for modes
|
||||
* 'tree', 'view', and 'form'
|
||||
* {Boolean} history Enable history (undo/redo).
|
||||
* True by default
|
||||
* Only applicable for modes
|
||||
* 'tree', 'view', and 'form'
|
||||
* {String} name Field name for the root node.
|
||||
* Only applicable for modes
|
||||
* 'tree', 'view', and 'form'
|
||||
* {Number} indentation Number of indentation
|
||||
* spaces. 4 by default.
|
||||
* Only applicable for
|
||||
* modes 'text' and 'code'
|
||||
* @param {Object | undefined} json JSON object
|
||||
*/
|
||||
function JSONEditor (container, options, json) {
|
||||
if (!(this instanceof JSONEditor)) {
|
||||
throw new Error('JSONEditor constructor called without "new".');
|
||||
}
|
||||
|
||||
// check for unsupported browser (IE8 and older)
|
||||
var ieVersion = util.getInternetExplorerVersion();
|
||||
if (ieVersion != -1 && ieVersion < 9) {
|
||||
throw new Error('Unsupported browser, IE9 or newer required. ' +
|
||||
'Please install the newest version of your browser.');
|
||||
}
|
||||
|
||||
if (arguments.length) {
|
||||
this._create(container, options, json);
|
||||
}
|
||||
}
|
||||
|
||||
// check for unsupported browser (IE8 and older)
|
||||
var ieVersion = util.getInternetExplorerVersion();
|
||||
if (ieVersion != -1 && ieVersion < 9) {
|
||||
throw new Error('Unsupported browser, IE9 or newer required. ' +
|
||||
'Please install the newest version of your browser.');
|
||||
}
|
||||
|
||||
if (arguments.length) {
|
||||
this._create(container, options, json);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration for all registered modes. Example:
|
||||
* {
|
||||
/**
|
||||
* Configuration for all registered modes. Example:
|
||||
* {
|
||||
* tree: {
|
||||
* editor: TreeEditor,
|
||||
* data: 'json'
|
||||
|
@ -53,161 +55,186 @@ function JSONEditor (container, options, json) {
|
|||
* data: 'text'
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @type { Object.<String, {editor: Object, data: String} > }
|
||||
*/
|
||||
JSONEditor.modes = {};
|
||||
*
|
||||
* @type { Object.<String, {editor: Object, data: String} > }
|
||||
*/
|
||||
JSONEditor.modes = {};
|
||||
|
||||
/**
|
||||
* Create the JSONEditor
|
||||
* @param {Element} container Container element
|
||||
* @param {Object} [options] See description in constructor
|
||||
* @param {Object | undefined} json JSON object
|
||||
* @private
|
||||
*/
|
||||
JSONEditor.prototype._create = function (container, options, json) {
|
||||
this.container = container;
|
||||
this.options = options || {};
|
||||
this.json = json || {};
|
||||
/**
|
||||
* Create the JSONEditor
|
||||
* @param {Element} container Container element
|
||||
* @param {Object} [options] See description in constructor
|
||||
* @param {Object | undefined} json JSON object
|
||||
* @private
|
||||
*/
|
||||
JSONEditor.prototype._create = function (container, options, json) {
|
||||
this.container = container;
|
||||
this.options = options || {};
|
||||
this.json = json || {};
|
||||
|
||||
var mode = this.options.mode || 'tree';
|
||||
this.setMode(mode);
|
||||
};
|
||||
var mode = this.options.mode || 'tree';
|
||||
this.setMode(mode);
|
||||
};
|
||||
|
||||
/**
|
||||
* Detach the editor from the DOM
|
||||
* @private
|
||||
*/
|
||||
JSONEditor.prototype._delete = function () {};
|
||||
/**
|
||||
* Detach the editor from the DOM
|
||||
* @private
|
||||
*/
|
||||
JSONEditor.prototype._delete = function () {};
|
||||
|
||||
/**
|
||||
* Set JSON object in editor
|
||||
* @param {Object | undefined} json JSON data
|
||||
*/
|
||||
JSONEditor.prototype.set = function (json) {
|
||||
this.json = json;
|
||||
};
|
||||
/**
|
||||
* Set JSON object in editor
|
||||
* @param {Object | undefined} json JSON data
|
||||
*/
|
||||
JSONEditor.prototype.set = function (json) {
|
||||
this.json = json;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get JSON from the editor
|
||||
* @returns {Object} json
|
||||
*/
|
||||
JSONEditor.prototype.get = function () {
|
||||
return this.json;
|
||||
};
|
||||
/**
|
||||
* Get JSON from the editor
|
||||
* @returns {Object} json
|
||||
*/
|
||||
JSONEditor.prototype.get = function () {
|
||||
return this.json;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set string containing JSON for the editor
|
||||
* @param {String | undefined} jsonText
|
||||
*/
|
||||
JSONEditor.prototype.setText = function (jsonText) {
|
||||
this.json = util.parse(jsonText);
|
||||
};
|
||||
/**
|
||||
* Set string containing JSON for the editor
|
||||
* @param {String | undefined} jsonText
|
||||
*/
|
||||
JSONEditor.prototype.setText = function (jsonText) {
|
||||
this.json = util.parse(jsonText);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get stringified JSON contents from the editor
|
||||
* @returns {String} jsonText
|
||||
*/
|
||||
JSONEditor.prototype.getText = function () {
|
||||
return JSON.stringify(this.json);
|
||||
};
|
||||
/**
|
||||
* Get stringified JSON contents from the editor
|
||||
* @returns {String} jsonText
|
||||
*/
|
||||
JSONEditor.prototype.getText = function () {
|
||||
return JSON.stringify(this.json);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set a field name for the root node.
|
||||
* @param {String | undefined} name
|
||||
*/
|
||||
JSONEditor.prototype.setName = function (name) {
|
||||
if (!this.options) {
|
||||
this.options = {};
|
||||
}
|
||||
this.options.name = name;
|
||||
};
|
||||
/**
|
||||
* Set a field name for the root node.
|
||||
* @param {String | undefined} name
|
||||
*/
|
||||
JSONEditor.prototype.setName = function (name) {
|
||||
if (!this.options) {
|
||||
this.options = {};
|
||||
}
|
||||
this.options.name = name;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the field name for the root node.
|
||||
* @return {String | undefined} name
|
||||
*/
|
||||
JSONEditor.prototype.getName = function () {
|
||||
return this.options && this.options.name;
|
||||
};
|
||||
/**
|
||||
* Get the field name for the root node.
|
||||
* @return {String | undefined} name
|
||||
*/
|
||||
JSONEditor.prototype.getName = function () {
|
||||
return this.options && this.options.name;
|
||||
};
|
||||
|
||||
/**
|
||||
* Change the mode of the editor.
|
||||
* JSONEditor will be extended with all methods needed for the chosen mode.
|
||||
* @param {String} mode Available modes: 'tree' (default), 'view', 'form',
|
||||
* 'text', and 'code'.
|
||||
*/
|
||||
JSONEditor.prototype.setMode = function (mode) {
|
||||
var container = this.container,
|
||||
options = util.extend({}, this.options),
|
||||
data,
|
||||
name;
|
||||
/**
|
||||
* Change the mode of the editor.
|
||||
* JSONEditor will be extended with all methods needed for the chosen mode.
|
||||
* @param {String} mode Available modes: 'tree' (default), 'view', 'form',
|
||||
* 'text', and 'code'.
|
||||
*/
|
||||
JSONEditor.prototype.setMode = function (mode) {
|
||||
var container = this.container,
|
||||
options = util.extend({}, this.options),
|
||||
data,
|
||||
name;
|
||||
|
||||
options.mode = mode;
|
||||
var config = JSONEditor.modes[mode];
|
||||
if (config) {
|
||||
try {
|
||||
if (config.data == 'text') {
|
||||
// text
|
||||
name = this.getName();
|
||||
data = this.getText();
|
||||
options.mode = mode;
|
||||
var config = JSONEditor.modes[mode];
|
||||
if (config) {
|
||||
try {
|
||||
if (config.data == 'text') {
|
||||
// text
|
||||
name = this.getName();
|
||||
data = this.getText();
|
||||
|
||||
this._delete();
|
||||
util.clear(this);
|
||||
util.extend(this, config.editor.prototype);
|
||||
this._create(container, options);
|
||||
this._delete();
|
||||
util.clear(this);
|
||||
util.extend(this, config.editor.prototype);
|
||||
this._create(container, options);
|
||||
|
||||
this.setName(name);
|
||||
this.setText(data);
|
||||
}
|
||||
else {
|
||||
// json
|
||||
name = this.getName();
|
||||
data = this.get();
|
||||
|
||||
this._delete();
|
||||
util.clear(this);
|
||||
util.extend(this, config.editor.prototype);
|
||||
this._create(container, options);
|
||||
|
||||
this.setName(name);
|
||||
this.set(data);
|
||||
}
|
||||
|
||||
if (typeof config.load === 'function') {
|
||||
try {
|
||||
config.load.call(this);
|
||||
this.setName(name);
|
||||
this.setText(data);
|
||||
}
|
||||
catch (err) {}
|
||||
else {
|
||||
// json
|
||||
name = this.getName();
|
||||
data = this.get();
|
||||
|
||||
this._delete();
|
||||
util.clear(this);
|
||||
util.extend(this, config.editor.prototype);
|
||||
this._create(container, options);
|
||||
|
||||
this.setName(name);
|
||||
this.set(data);
|
||||
}
|
||||
|
||||
if (typeof config.load === 'function') {
|
||||
try {
|
||||
config.load.call(this);
|
||||
}
|
||||
catch (err) {}
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
this._onError(err);
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
this._onError(err);
|
||||
else {
|
||||
throw new Error('Unknown mode "' + options.mode + '"');
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new Error('Unknown mode "' + options.mode + '"');
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Throw an error. If an error callback is configured in options.error, this
|
||||
* callback will be invoked. Else, a regular error is thrown.
|
||||
* @param {Error} err
|
||||
* @private
|
||||
*/
|
||||
JSONEditor.prototype._onError = function(err) {
|
||||
// TODO: onError is deprecated since version 2.2.0. cleanup some day
|
||||
if (typeof this.onError === 'function') {
|
||||
util.log('WARNING: JSONEditor.onError is deprecated. ' +
|
||||
'Use options.error instead.');
|
||||
this.onError(err);
|
||||
}
|
||||
/**
|
||||
* Throw an error. If an error callback is configured in options.error, this
|
||||
* callback will be invoked. Else, a regular error is thrown.
|
||||
* @param {Error} err
|
||||
* @private
|
||||
*/
|
||||
JSONEditor.prototype._onError = function(err) {
|
||||
// TODO: onError is deprecated since version 2.2.0. cleanup some day
|
||||
if (typeof this.onError === 'function') {
|
||||
util.log('WARNING: JSONEditor.onError is deprecated. ' +
|
||||
'Use options.error instead.');
|
||||
this.onError(err);
|
||||
}
|
||||
|
||||
if (this.options && typeof this.options.error === 'function') {
|
||||
this.options.error(err);
|
||||
}
|
||||
else {
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
if (this.options && typeof this.options.error === 'function') {
|
||||
this.options.error(err);
|
||||
}
|
||||
else {
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Register modes for the JSON Editor
|
||||
* TODO: describe the mode format
|
||||
* @param {Object} modes An object with the mode names as keys, and an object
|
||||
* defining the mode as value
|
||||
*/
|
||||
JSONEditor.registerModes = function (modes) {
|
||||
for (var mode in modes) {
|
||||
if (modes.hasOwnProperty(mode)) {
|
||||
if (mode in JSONEditor.modes) {
|
||||
throw new Error('Mode "' + mode + '" already registered');
|
||||
}
|
||||
|
||||
JSONEditor.modes[mode] = modes[mode];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// register TreeEditor and TextEditor
|
||||
JSONEditor.registerModes(TreeEditor.modes);
|
||||
JSONEditor.registerModes(TextEditor.modes);
|
||||
|
||||
return JSONEditor;
|
||||
});
|
5238
src/js/Node.js
5238
src/js/Node.js
File diff suppressed because it is too large
Load Diff
|
@ -1,286 +1,293 @@
|
|||
/**
|
||||
* @constructor SearchBox
|
||||
* Create a search box in given HTML container
|
||||
* @param {JSONEditor} editor The JSON Editor to attach to
|
||||
* @param {Element} container HTML container element of where to
|
||||
* create the search box
|
||||
*/
|
||||
function SearchBox (editor, container) {
|
||||
var searchBox = this;
|
||||
define(function () {
|
||||
|
||||
this.editor = editor;
|
||||
this.timeout = undefined;
|
||||
this.delay = 200; // ms
|
||||
this.lastText = undefined;
|
||||
/**
|
||||
* @constructor SearchBox
|
||||
* Create a search box in given HTML container
|
||||
* @param {JSONEditor} editor The JSON Editor to attach to
|
||||
* @param {Element} container HTML container element of where to
|
||||
* create the search box
|
||||
*/
|
||||
function SearchBox (editor, container) {
|
||||
var searchBox = this;
|
||||
|
||||
this.dom = {};
|
||||
this.dom.container = container;
|
||||
this.editor = editor;
|
||||
this.timeout = undefined;
|
||||
this.delay = 200; // ms
|
||||
this.lastText = undefined;
|
||||
|
||||
var table = document.createElement('table');
|
||||
this.dom.table = table;
|
||||
table.className = 'search';
|
||||
container.appendChild(table);
|
||||
var tbody = document.createElement('tbody');
|
||||
this.dom.tbody = tbody;
|
||||
table.appendChild(tbody);
|
||||
var tr = document.createElement('tr');
|
||||
tbody.appendChild(tr);
|
||||
this.dom = {};
|
||||
this.dom.container = container;
|
||||
|
||||
var td = document.createElement('td');
|
||||
tr.appendChild(td);
|
||||
var results = document.createElement('div');
|
||||
this.dom.results = results;
|
||||
results.className = 'results';
|
||||
td.appendChild(results);
|
||||
var table = document.createElement('table');
|
||||
this.dom.table = table;
|
||||
table.className = 'search';
|
||||
container.appendChild(table);
|
||||
var tbody = document.createElement('tbody');
|
||||
this.dom.tbody = tbody;
|
||||
table.appendChild(tbody);
|
||||
var tr = document.createElement('tr');
|
||||
tbody.appendChild(tr);
|
||||
|
||||
td = document.createElement('td');
|
||||
tr.appendChild(td);
|
||||
var divInput = document.createElement('div');
|
||||
this.dom.input = divInput;
|
||||
divInput.className = 'frame';
|
||||
divInput.title = 'Search fields and values';
|
||||
td.appendChild(divInput);
|
||||
var td = document.createElement('td');
|
||||
tr.appendChild(td);
|
||||
var results = document.createElement('div');
|
||||
this.dom.results = results;
|
||||
results.className = 'results';
|
||||
td.appendChild(results);
|
||||
|
||||
// table to contain the text input and search button
|
||||
var tableInput = document.createElement('table');
|
||||
divInput.appendChild(tableInput);
|
||||
var tbodySearch = document.createElement('tbody');
|
||||
tableInput.appendChild(tbodySearch);
|
||||
tr = document.createElement('tr');
|
||||
tbodySearch.appendChild(tr);
|
||||
td = document.createElement('td');
|
||||
tr.appendChild(td);
|
||||
var divInput = document.createElement('div');
|
||||
this.dom.input = divInput;
|
||||
divInput.className = 'frame';
|
||||
divInput.title = 'Search fields and values';
|
||||
td.appendChild(divInput);
|
||||
|
||||
var refreshSearch = document.createElement('button');
|
||||
refreshSearch.className = 'refresh';
|
||||
td = document.createElement('td');
|
||||
td.appendChild(refreshSearch);
|
||||
tr.appendChild(td);
|
||||
// table to contain the text input and search button
|
||||
var tableInput = document.createElement('table');
|
||||
divInput.appendChild(tableInput);
|
||||
var tbodySearch = document.createElement('tbody');
|
||||
tableInput.appendChild(tbodySearch);
|
||||
tr = document.createElement('tr');
|
||||
tbodySearch.appendChild(tr);
|
||||
|
||||
var search = document.createElement('input');
|
||||
this.dom.search = search;
|
||||
search.oninput = function (event) {
|
||||
searchBox._onDelayedSearch(event);
|
||||
};
|
||||
search.onchange = function (event) { // For IE 9
|
||||
searchBox._onSearch(event);
|
||||
};
|
||||
search.onkeydown = function (event) {
|
||||
searchBox._onKeyDown(event);
|
||||
};
|
||||
search.onkeyup = function (event) {
|
||||
searchBox._onKeyUp(event);
|
||||
};
|
||||
refreshSearch.onclick = function (event) {
|
||||
search.select();
|
||||
};
|
||||
var refreshSearch = document.createElement('button');
|
||||
refreshSearch.className = 'refresh';
|
||||
td = document.createElement('td');
|
||||
td.appendChild(refreshSearch);
|
||||
tr.appendChild(td);
|
||||
|
||||
// TODO: ESC in FF restores the last input, is a FF bug, https://bugzilla.mozilla.org/show_bug.cgi?id=598819
|
||||
td = document.createElement('td');
|
||||
td.appendChild(search);
|
||||
tr.appendChild(td);
|
||||
var search = document.createElement('input');
|
||||
this.dom.search = search;
|
||||
search.oninput = function (event) {
|
||||
searchBox._onDelayedSearch(event);
|
||||
};
|
||||
search.onchange = function (event) { // For IE 9
|
||||
searchBox._onSearch(event);
|
||||
};
|
||||
search.onkeydown = function (event) {
|
||||
searchBox._onKeyDown(event);
|
||||
};
|
||||
search.onkeyup = function (event) {
|
||||
searchBox._onKeyUp(event);
|
||||
};
|
||||
refreshSearch.onclick = function (event) {
|
||||
search.select();
|
||||
};
|
||||
|
||||
var searchNext = document.createElement('button');
|
||||
searchNext.title = 'Next result (Enter)';
|
||||
searchNext.className = 'next';
|
||||
searchNext.onclick = function () {
|
||||
searchBox.next();
|
||||
};
|
||||
td = document.createElement('td');
|
||||
td.appendChild(searchNext);
|
||||
tr.appendChild(td);
|
||||
// TODO: ESC in FF restores the last input, is a FF bug, https://bugzilla.mozilla.org/show_bug.cgi?id=598819
|
||||
td = document.createElement('td');
|
||||
td.appendChild(search);
|
||||
tr.appendChild(td);
|
||||
|
||||
var searchPrevious = document.createElement('button');
|
||||
searchPrevious.title = 'Previous result (Shift+Enter)';
|
||||
searchPrevious.className = 'previous';
|
||||
searchPrevious.onclick = function () {
|
||||
searchBox.previous();
|
||||
};
|
||||
td = document.createElement('td');
|
||||
td.appendChild(searchPrevious);
|
||||
tr.appendChild(td);
|
||||
}
|
||||
var searchNext = document.createElement('button');
|
||||
searchNext.title = 'Next result (Enter)';
|
||||
searchNext.className = 'next';
|
||||
searchNext.onclick = function () {
|
||||
searchBox.next();
|
||||
};
|
||||
td = document.createElement('td');
|
||||
td.appendChild(searchNext);
|
||||
tr.appendChild(td);
|
||||
|
||||
/**
|
||||
* Go to the next search result
|
||||
* @param {boolean} [focus] If true, focus will be set to the next result
|
||||
* focus is false by default.
|
||||
*/
|
||||
SearchBox.prototype.next = function(focus) {
|
||||
if (this.results != undefined) {
|
||||
var index = (this.resultIndex != undefined) ? this.resultIndex + 1 : 0;
|
||||
if (index > this.results.length - 1) {
|
||||
index = 0;
|
||||
}
|
||||
this._setActiveResult(index, focus);
|
||||
var searchPrevious = document.createElement('button');
|
||||
searchPrevious.title = 'Previous result (Shift+Enter)';
|
||||
searchPrevious.className = 'previous';
|
||||
searchPrevious.onclick = function () {
|
||||
searchBox.previous();
|
||||
};
|
||||
td = document.createElement('td');
|
||||
td.appendChild(searchPrevious);
|
||||
tr.appendChild(td);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Go to the prevous search result
|
||||
* @param {boolean} [focus] If true, focus will be set to the next result
|
||||
* focus is false by default.
|
||||
*/
|
||||
SearchBox.prototype.previous = function(focus) {
|
||||
if (this.results != undefined) {
|
||||
var max = this.results.length - 1;
|
||||
var index = (this.resultIndex != undefined) ? this.resultIndex - 1 : max;
|
||||
if (index < 0) {
|
||||
index = max;
|
||||
/**
|
||||
* Go to the next search result
|
||||
* @param {boolean} [focus] If true, focus will be set to the next result
|
||||
* focus is false by default.
|
||||
*/
|
||||
SearchBox.prototype.next = function(focus) {
|
||||
if (this.results != undefined) {
|
||||
var index = (this.resultIndex != undefined) ? this.resultIndex + 1 : 0;
|
||||
if (index > this.results.length - 1) {
|
||||
index = 0;
|
||||
}
|
||||
this._setActiveResult(index, focus);
|
||||
}
|
||||
this._setActiveResult(index, focus);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Set new value for the current active result
|
||||
* @param {Number} index
|
||||
* @param {boolean} [focus] If true, focus will be set to the next result.
|
||||
* focus is false by default.
|
||||
* @private
|
||||
*/
|
||||
SearchBox.prototype._setActiveResult = function(index, focus) {
|
||||
// de-activate current active result
|
||||
if (this.activeResult) {
|
||||
var prevNode = this.activeResult.node;
|
||||
var prevElem = this.activeResult.elem;
|
||||
if (prevElem == 'field') {
|
||||
delete prevNode.searchFieldActive;
|
||||
/**
|
||||
* Go to the prevous search result
|
||||
* @param {boolean} [focus] If true, focus will be set to the next result
|
||||
* focus is false by default.
|
||||
*/
|
||||
SearchBox.prototype.previous = function(focus) {
|
||||
if (this.results != undefined) {
|
||||
var max = this.results.length - 1;
|
||||
var index = (this.resultIndex != undefined) ? this.resultIndex - 1 : max;
|
||||
if (index < 0) {
|
||||
index = max;
|
||||
}
|
||||
this._setActiveResult(index, focus);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set new value for the current active result
|
||||
* @param {Number} index
|
||||
* @param {boolean} [focus] If true, focus will be set to the next result.
|
||||
* focus is false by default.
|
||||
* @private
|
||||
*/
|
||||
SearchBox.prototype._setActiveResult = function(index, focus) {
|
||||
// de-activate current active result
|
||||
if (this.activeResult) {
|
||||
var prevNode = this.activeResult.node;
|
||||
var prevElem = this.activeResult.elem;
|
||||
if (prevElem == 'field') {
|
||||
delete prevNode.searchFieldActive;
|
||||
}
|
||||
else {
|
||||
delete prevNode.searchValueActive;
|
||||
}
|
||||
prevNode.updateDom();
|
||||
}
|
||||
|
||||
if (!this.results || !this.results[index]) {
|
||||
// out of range, set to undefined
|
||||
this.resultIndex = undefined;
|
||||
this.activeResult = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
this.resultIndex = index;
|
||||
|
||||
// set new node active
|
||||
var node = this.results[this.resultIndex].node;
|
||||
var elem = this.results[this.resultIndex].elem;
|
||||
if (elem == 'field') {
|
||||
node.searchFieldActive = true;
|
||||
}
|
||||
else {
|
||||
delete prevNode.searchValueActive;
|
||||
node.searchValueActive = true;
|
||||
}
|
||||
prevNode.updateDom();
|
||||
}
|
||||
this.activeResult = this.results[this.resultIndex];
|
||||
node.updateDom();
|
||||
|
||||
if (!this.results || !this.results[index]) {
|
||||
// out of range, set to undefined
|
||||
this.resultIndex = undefined;
|
||||
this.activeResult = undefined;
|
||||
return;
|
||||
}
|
||||
// TODO: not so nice that the focus is only set after the animation is finished
|
||||
node.scrollTo(function () {
|
||||
if (focus) {
|
||||
node.focus(elem);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.resultIndex = index;
|
||||
|
||||
// set new node active
|
||||
var node = this.results[this.resultIndex].node;
|
||||
var elem = this.results[this.resultIndex].elem;
|
||||
if (elem == 'field') {
|
||||
node.searchFieldActive = true;
|
||||
}
|
||||
else {
|
||||
node.searchValueActive = true;
|
||||
}
|
||||
this.activeResult = this.results[this.resultIndex];
|
||||
node.updateDom();
|
||||
|
||||
// TODO: not so nice that the focus is only set after the animation is finished
|
||||
node.scrollTo(function () {
|
||||
if (focus) {
|
||||
node.focus(elem);
|
||||
/**
|
||||
* Cancel any running onDelayedSearch.
|
||||
* @private
|
||||
*/
|
||||
SearchBox.prototype._clearDelay = function() {
|
||||
if (this.timeout != undefined) {
|
||||
clearTimeout(this.timeout);
|
||||
delete this.timeout;
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Cancel any running onDelayedSearch.
|
||||
* @private
|
||||
*/
|
||||
SearchBox.prototype._clearDelay = function() {
|
||||
if (this.timeout != undefined) {
|
||||
clearTimeout(this.timeout);
|
||||
delete this.timeout;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Start a timer to execute a search after a short delay.
|
||||
* Used for reducing the number of searches while typing.
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
SearchBox.prototype._onDelayedSearch = function (event) {
|
||||
// execute the search after a short delay (reduces the number of
|
||||
// search actions while typing in the search text box)
|
||||
this._clearDelay();
|
||||
var searchBox = this;
|
||||
this.timeout = setTimeout(function (event) {
|
||||
searchBox._onSearch(event);
|
||||
},
|
||||
this.delay);
|
||||
};
|
||||
|
||||
/**
|
||||
* Start a timer to execute a search after a short delay.
|
||||
* Used for reducing the number of searches while typing.
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
SearchBox.prototype._onDelayedSearch = function (event) {
|
||||
// execute the search after a short delay (reduces the number of
|
||||
// search actions while typing in the search text box)
|
||||
this._clearDelay();
|
||||
var searchBox = this;
|
||||
this.timeout = setTimeout(function (event) {
|
||||
searchBox._onSearch(event);
|
||||
},
|
||||
this.delay);
|
||||
};
|
||||
/**
|
||||
* Handle onSearch event
|
||||
* @param {Event} event
|
||||
* @param {boolean} [forceSearch] If true, search will be executed again even
|
||||
* when the search text is not changed.
|
||||
* Default is false.
|
||||
* @private
|
||||
*/
|
||||
SearchBox.prototype._onSearch = function (event, forceSearch) {
|
||||
this._clearDelay();
|
||||
|
||||
/**
|
||||
* Handle onSearch event
|
||||
* @param {Event} event
|
||||
* @param {boolean} [forceSearch] If true, search will be executed again even
|
||||
* when the search text is not changed.
|
||||
* Default is false.
|
||||
* @private
|
||||
*/
|
||||
SearchBox.prototype._onSearch = function (event, forceSearch) {
|
||||
this._clearDelay();
|
||||
var value = this.dom.search.value;
|
||||
var text = (value.length > 0) ? value : undefined;
|
||||
if (text != this.lastText || forceSearch) {
|
||||
// only search again when changed
|
||||
this.lastText = text;
|
||||
this.results = this.editor.search(text);
|
||||
this._setActiveResult(undefined);
|
||||
|
||||
var value = this.dom.search.value;
|
||||
var text = (value.length > 0) ? value : undefined;
|
||||
if (text != this.lastText || forceSearch) {
|
||||
// only search again when changed
|
||||
this.lastText = text;
|
||||
this.results = this.editor.search(text);
|
||||
this._setActiveResult(undefined);
|
||||
|
||||
// display search results
|
||||
if (text != undefined) {
|
||||
var resultCount = this.results.length;
|
||||
switch (resultCount) {
|
||||
case 0: this.dom.results.innerHTML = 'no results'; break;
|
||||
case 1: this.dom.results.innerHTML = '1 result'; break;
|
||||
default: this.dom.results.innerHTML = resultCount + ' results'; break;
|
||||
// display search results
|
||||
if (text != undefined) {
|
||||
var resultCount = this.results.length;
|
||||
switch (resultCount) {
|
||||
case 0: this.dom.results.innerHTML = 'no results'; break;
|
||||
case 1: this.dom.results.innerHTML = '1 result'; break;
|
||||
default: this.dom.results.innerHTML = resultCount + ' results'; break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.dom.results.innerHTML = '';
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.dom.results.innerHTML = '';
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle onKeyDown event in the input box
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
SearchBox.prototype._onKeyDown = function (event) {
|
||||
var keynum = event.which;
|
||||
if (keynum == 27) { // ESC
|
||||
this.dom.search.value = ''; // clear search
|
||||
this._onSearch(event);
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
else if (keynum == 13) { // Enter
|
||||
if (event.ctrlKey) {
|
||||
// force to search again
|
||||
this._onSearch(event, true);
|
||||
/**
|
||||
* Handle onKeyDown event in the input box
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
SearchBox.prototype._onKeyDown = function (event) {
|
||||
var keynum = event.which;
|
||||
if (keynum == 27) { // ESC
|
||||
this.dom.search.value = ''; // clear search
|
||||
this._onSearch(event);
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
else if (event.shiftKey) {
|
||||
// move to the previous search result
|
||||
this.previous();
|
||||
else if (keynum == 13) { // Enter
|
||||
if (event.ctrlKey) {
|
||||
// force to search again
|
||||
this._onSearch(event, true);
|
||||
}
|
||||
else if (event.shiftKey) {
|
||||
// move to the previous search result
|
||||
this.previous();
|
||||
}
|
||||
else {
|
||||
// move to the next search result
|
||||
this.next();
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
else {
|
||||
// move to the next search result
|
||||
this.next();
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle onKeyUp event in the input box
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
SearchBox.prototype._onKeyUp = function (event) {
|
||||
var keynum = event.keyCode;
|
||||
if (keynum != 27 && keynum != 13) { // !show and !Enter
|
||||
this._onDelayedSearch(event); // For IE 9
|
||||
}
|
||||
};
|
||||
|
||||
return SearchBox;
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Handle onKeyUp event in the input box
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
SearchBox.prototype._onKeyUp = function (event) {
|
||||
var keynum = event.keyCode;
|
||||
if (keynum != 27 && keynum != 13) { // !show and !Enter
|
||||
this._onDelayedSearch(event); // For IE 9
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,305 +1,312 @@
|
|||
/**
|
||||
* Create a TextEditor and attach it to given container
|
||||
* @constructor TextEditor
|
||||
* @param {Element} container
|
||||
* @param {Object} [options] Object with options. available options:
|
||||
* {String} mode Available values:
|
||||
* "text" (default)
|
||||
* or "code".
|
||||
* {Number} indentation Number of indentation
|
||||
* spaces. 2 by default.
|
||||
* {function} change Callback method
|
||||
* triggered on change
|
||||
* @param {JSON | String} [json] initial contents of the formatter
|
||||
*/
|
||||
function TextEditor(container, options, json) {
|
||||
if (!(this instanceof TextEditor)) {
|
||||
throw new Error('TextEditor constructor called without "new".');
|
||||
define(['./modebox', './util'], function (modebox, util) {
|
||||
|
||||
/**
|
||||
* Create a TextEditor and attach it to given container
|
||||
* @constructor TextEditor
|
||||
* @param {Element} container
|
||||
* @param {Object} [options] Object with options. available options:
|
||||
* {String} mode Available values:
|
||||
* "text" (default)
|
||||
* or "code".
|
||||
* {Number} indentation Number of indentation
|
||||
* spaces. 2 by default.
|
||||
* {function} change Callback method
|
||||
* triggered on change
|
||||
* @param {JSON | String} [json] initial contents of the formatter
|
||||
*/
|
||||
function TextEditor(container, options, json) {
|
||||
if (!(this instanceof TextEditor)) {
|
||||
throw new Error('TextEditor constructor called without "new".');
|
||||
}
|
||||
|
||||
this._create(container, options, json);
|
||||
}
|
||||
|
||||
this._create(container, options, json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a TextEditor and attach it to given container
|
||||
* @constructor TextEditor
|
||||
* @param {Element} container
|
||||
* @param {Object} [options] See description in constructor
|
||||
* @param {JSON | String} [json] initial contents of the formatter
|
||||
* @private
|
||||
*/
|
||||
TextEditor.prototype._create = function (container, options, json) {
|
||||
// read options
|
||||
options = options || {};
|
||||
this.options = options;
|
||||
if (options.indentation) {
|
||||
this.indentation = Number(options.indentation);
|
||||
}
|
||||
else {
|
||||
this.indentation = 2; // number of spaces
|
||||
}
|
||||
this.mode = (options.mode == 'code') ? 'code' : 'text';
|
||||
if (this.mode == 'code') {
|
||||
// verify whether Ace editor is available and supported
|
||||
if (typeof ace === 'undefined') {
|
||||
this.mode = 'text';
|
||||
util.log('WARNING: Cannot load code editor, Ace library not loaded. ' +
|
||||
'Falling back to plain text editor');
|
||||
/**
|
||||
* Create a TextEditor and attach it to given container
|
||||
* @constructor TextEditor
|
||||
* @param {Element} container
|
||||
* @param {Object} [options] See description in constructor
|
||||
* @param {JSON | String} [json] initial contents of the formatter
|
||||
* @private
|
||||
*/
|
||||
TextEditor.prototype._create = function (container, options, json) {
|
||||
// read options
|
||||
options = options || {};
|
||||
this.options = options;
|
||||
if (options.indentation) {
|
||||
this.indentation = Number(options.indentation);
|
||||
}
|
||||
}
|
||||
|
||||
var me = this;
|
||||
this.container = container;
|
||||
this.dom = {};
|
||||
this.editor = undefined; // ace code editor
|
||||
this.textarea = undefined; // plain text editor (fallback when Ace is not available)
|
||||
|
||||
this.width = container.clientWidth;
|
||||
this.height = container.clientHeight;
|
||||
|
||||
this.frame = document.createElement('div');
|
||||
this.frame.className = 'jsoneditor';
|
||||
this.frame.onclick = function (event) {
|
||||
// prevent default submit action when TextEditor is located inside a form
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
// create menu
|
||||
this.menu = document.createElement('div');
|
||||
this.menu.className = 'menu';
|
||||
this.frame.appendChild(this.menu);
|
||||
|
||||
// create format button
|
||||
var buttonFormat = document.createElement('button');
|
||||
buttonFormat.className = 'format';
|
||||
buttonFormat.title = 'Format JSON data, with proper indentation and line feeds';
|
||||
this.menu.appendChild(buttonFormat);
|
||||
buttonFormat.onclick = function () {
|
||||
try {
|
||||
me.format();
|
||||
else {
|
||||
this.indentation = 2; // number of spaces
|
||||
}
|
||||
catch (err) {
|
||||
me._onError(err);
|
||||
this.mode = (options.mode == 'code') ? 'code' : 'text';
|
||||
if (this.mode == 'code') {
|
||||
// verify whether Ace editor is available and supported
|
||||
if (typeof ace === 'undefined') {
|
||||
this.mode = 'text';
|
||||
util.log('WARNING: Cannot load code editor, Ace library not loaded. ' +
|
||||
'Falling back to plain text editor');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// create compact button
|
||||
var buttonCompact = document.createElement('button');
|
||||
buttonCompact.className = 'compact';
|
||||
buttonCompact.title = 'Compact JSON data, remove all whitespaces';
|
||||
this.menu.appendChild(buttonCompact);
|
||||
buttonCompact.onclick = function () {
|
||||
try {
|
||||
me.compact();
|
||||
}
|
||||
catch (err) {
|
||||
me._onError(err);
|
||||
}
|
||||
};
|
||||
var me = this;
|
||||
this.container = container;
|
||||
this.dom = {};
|
||||
this.editor = undefined; // ace code editor
|
||||
this.textarea = undefined; // plain text editor (fallback when Ace is not available)
|
||||
|
||||
// create mode box
|
||||
if (this.options && this.options.modes && this.options.modes.length) {
|
||||
var modeBox = createModeBox(this, this.options.modes, this.options.mode);
|
||||
this.menu.appendChild(modeBox);
|
||||
this.dom.modeBox = modeBox;
|
||||
}
|
||||
this.width = container.clientWidth;
|
||||
this.height = container.clientHeight;
|
||||
|
||||
this.content = document.createElement('div');
|
||||
this.content.className = 'outer';
|
||||
this.frame.appendChild(this.content);
|
||||
|
||||
this.container.appendChild(this.frame);
|
||||
|
||||
if (this.mode == 'code') {
|
||||
this.editorDom = document.createElement('div');
|
||||
this.editorDom.style.height = '100%'; // TODO: move to css
|
||||
this.editorDom.style.width = '100%'; // TODO: move to css
|
||||
this.content.appendChild(this.editorDom);
|
||||
|
||||
var editor = ace.edit(this.editorDom);
|
||||
editor.setTheme('ace/theme/jsoneditor');
|
||||
editor.setShowPrintMargin(false);
|
||||
editor.setFontSize(13);
|
||||
editor.getSession().setMode('ace/mode/json');
|
||||
editor.getSession().setTabSize(2);
|
||||
editor.getSession().setUseSoftTabs(true);
|
||||
editor.getSession().setUseWrapMode(true);
|
||||
this.editor = editor;
|
||||
|
||||
var poweredBy = document.createElement('a');
|
||||
poweredBy.appendChild(document.createTextNode('powered by ace'));
|
||||
poweredBy.href = 'http://ace.ajax.org';
|
||||
poweredBy.target = '_blank';
|
||||
poweredBy.className = 'poweredBy';
|
||||
poweredBy.onclick = function () {
|
||||
// TODO: this anchor falls below the margin of the content,
|
||||
// therefore the normal a.href does not work. We use a click event
|
||||
// for now, but this should be fixed.
|
||||
window.open(poweredBy.href, poweredBy.target);
|
||||
this.frame = document.createElement('div');
|
||||
this.frame.className = 'jsoneditor';
|
||||
this.frame.onclick = function (event) {
|
||||
// prevent default submit action when TextEditor is located inside a form
|
||||
event.preventDefault();
|
||||
};
|
||||
this.menu.appendChild(poweredBy);
|
||||
|
||||
if (options.change) {
|
||||
// register onchange event
|
||||
editor.on('change', function () {
|
||||
options.change();
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
// load a plain text textarea
|
||||
var textarea = document.createElement('textarea');
|
||||
textarea.className = 'text';
|
||||
textarea.spellcheck = false;
|
||||
this.content.appendChild(textarea);
|
||||
this.textarea = textarea;
|
||||
// create menu
|
||||
this.menu = document.createElement('div');
|
||||
this.menu.className = 'menu';
|
||||
this.frame.appendChild(this.menu);
|
||||
|
||||
if (options.change) {
|
||||
// register onchange event
|
||||
if (this.textarea.oninput === null) {
|
||||
this.textarea.oninput = function () {
|
||||
options.change();
|
||||
}
|
||||
// create format button
|
||||
var buttonFormat = document.createElement('button');
|
||||
buttonFormat.className = 'format';
|
||||
buttonFormat.title = 'Format JSON data, with proper indentation and line feeds';
|
||||
this.menu.appendChild(buttonFormat);
|
||||
buttonFormat.onclick = function () {
|
||||
try {
|
||||
me.format();
|
||||
}
|
||||
else {
|
||||
// oninput is undefined. For IE8-
|
||||
this.textarea.onchange = function () {
|
||||
catch (err) {
|
||||
me._onError(err);
|
||||
}
|
||||
};
|
||||
|
||||
// create compact button
|
||||
var buttonCompact = document.createElement('button');
|
||||
buttonCompact.className = 'compact';
|
||||
buttonCompact.title = 'Compact JSON data, remove all whitespaces';
|
||||
this.menu.appendChild(buttonCompact);
|
||||
buttonCompact.onclick = function () {
|
||||
try {
|
||||
me.compact();
|
||||
}
|
||||
catch (err) {
|
||||
me._onError(err);
|
||||
}
|
||||
};
|
||||
|
||||
// create mode box
|
||||
if (this.options && this.options.modes && this.options.modes.length) {
|
||||
var modeBox = modebox.create(this, this.options.modes, this.options.mode);
|
||||
this.menu.appendChild(modeBox);
|
||||
this.dom.modeBox = modeBox;
|
||||
}
|
||||
|
||||
this.content = document.createElement('div');
|
||||
this.content.className = 'outer';
|
||||
this.frame.appendChild(this.content);
|
||||
|
||||
this.container.appendChild(this.frame);
|
||||
|
||||
if (this.mode == 'code') {
|
||||
this.editorDom = document.createElement('div');
|
||||
this.editorDom.style.height = '100%'; // TODO: move to css
|
||||
this.editorDom.style.width = '100%'; // TODO: move to css
|
||||
this.content.appendChild(this.editorDom);
|
||||
|
||||
var editor = ace.edit(this.editorDom);
|
||||
editor.setTheme('ace/theme/jsoneditor');
|
||||
editor.setShowPrintMargin(false);
|
||||
editor.setFontSize(13);
|
||||
editor.getSession().setMode('ace/mode/json');
|
||||
editor.getSession().setTabSize(2);
|
||||
editor.getSession().setUseSoftTabs(true);
|
||||
editor.getSession().setUseWrapMode(true);
|
||||
this.editor = editor;
|
||||
|
||||
var poweredBy = document.createElement('a');
|
||||
poweredBy.appendChild(document.createTextNode('powered by ace'));
|
||||
poweredBy.href = 'http://ace.ajax.org';
|
||||
poweredBy.target = '_blank';
|
||||
poweredBy.className = 'poweredBy';
|
||||
poweredBy.onclick = function () {
|
||||
// TODO: this anchor falls below the margin of the content,
|
||||
// therefore the normal a.href does not work. We use a click event
|
||||
// for now, but this should be fixed.
|
||||
window.open(poweredBy.href, poweredBy.target);
|
||||
};
|
||||
this.menu.appendChild(poweredBy);
|
||||
|
||||
if (options.change) {
|
||||
// register onchange event
|
||||
editor.on('change', function () {
|
||||
options.change();
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
// load a plain text textarea
|
||||
var textarea = document.createElement('textarea');
|
||||
textarea.className = 'text';
|
||||
textarea.spellcheck = false;
|
||||
this.content.appendChild(textarea);
|
||||
this.textarea = textarea;
|
||||
|
||||
if (options.change) {
|
||||
// register onchange event
|
||||
if (this.textarea.oninput === null) {
|
||||
this.textarea.oninput = function () {
|
||||
options.change();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// oninput is undefined. For IE8-
|
||||
this.textarea.onchange = function () {
|
||||
options.change();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// load initial json object or string
|
||||
if (typeof(json) == 'string') {
|
||||
this.setText(json);
|
||||
}
|
||||
else {
|
||||
this.set(json);
|
||||
}
|
||||
};
|
||||
// load initial json object or string
|
||||
if (typeof(json) == 'string') {
|
||||
this.setText(json);
|
||||
}
|
||||
else {
|
||||
this.set(json);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Detach the editor from the DOM
|
||||
* @private
|
||||
*/
|
||||
TextEditor.prototype._delete = function () {
|
||||
if (this.frame && this.container && this.frame.parentNode == this.container) {
|
||||
this.container.removeChild(this.frame);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Detach the editor from the DOM
|
||||
* @private
|
||||
*/
|
||||
TextEditor.prototype._delete = function () {
|
||||
if (this.frame && this.container && this.frame.parentNode == this.container) {
|
||||
this.container.removeChild(this.frame);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Throw an error. If an error callback is configured in options.error, this
|
||||
* callback will be invoked. Else, a regular error is thrown.
|
||||
* @param {Error} err
|
||||
* @private
|
||||
*/
|
||||
TextEditor.prototype._onError = function(err) {
|
||||
// TODO: onError is deprecated since version 2.2.0. cleanup some day
|
||||
if (typeof this.onError === 'function') {
|
||||
util.log('WARNING: JSONEditor.onError is deprecated. ' +
|
||||
'Use options.error instead.');
|
||||
this.onError(err);
|
||||
}
|
||||
/**
|
||||
* Throw an error. If an error callback is configured in options.error, this
|
||||
* callback will be invoked. Else, a regular error is thrown.
|
||||
* @param {Error} err
|
||||
* @private
|
||||
*/
|
||||
TextEditor.prototype._onError = function(err) {
|
||||
// TODO: onError is deprecated since version 2.2.0. cleanup some day
|
||||
if (typeof this.onError === 'function') {
|
||||
util.log('WARNING: JSONEditor.onError is deprecated. ' +
|
||||
'Use options.error instead.');
|
||||
this.onError(err);
|
||||
}
|
||||
|
||||
if (this.options && typeof this.options.error === 'function') {
|
||||
this.options.error(err);
|
||||
}
|
||||
else {
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
if (this.options && typeof this.options.error === 'function') {
|
||||
this.options.error(err);
|
||||
}
|
||||
else {
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Compact the code in the formatter
|
||||
*/
|
||||
TextEditor.prototype.compact = function () {
|
||||
var json = util.parse(this.getText());
|
||||
this.setText(JSON.stringify(json));
|
||||
};
|
||||
/**
|
||||
* Compact the code in the formatter
|
||||
*/
|
||||
TextEditor.prototype.compact = function () {
|
||||
var json = util.parse(this.getText());
|
||||
this.setText(JSON.stringify(json));
|
||||
};
|
||||
|
||||
/**
|
||||
* Format the code in the formatter
|
||||
*/
|
||||
TextEditor.prototype.format = function () {
|
||||
var json = util.parse(this.getText());
|
||||
this.setText(JSON.stringify(json, null, this.indentation));
|
||||
};
|
||||
/**
|
||||
* Format the code in the formatter
|
||||
*/
|
||||
TextEditor.prototype.format = function () {
|
||||
var json = util.parse(this.getText());
|
||||
this.setText(JSON.stringify(json, null, this.indentation));
|
||||
};
|
||||
|
||||
/**
|
||||
* Set focus to the formatter
|
||||
*/
|
||||
TextEditor.prototype.focus = function () {
|
||||
if (this.textarea) {
|
||||
this.textarea.focus();
|
||||
}
|
||||
if (this.editor) {
|
||||
this.editor.focus();
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Set focus to the formatter
|
||||
*/
|
||||
TextEditor.prototype.focus = function () {
|
||||
if (this.textarea) {
|
||||
this.textarea.focus();
|
||||
}
|
||||
if (this.editor) {
|
||||
this.editor.focus();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Resize the formatter
|
||||
*/
|
||||
TextEditor.prototype.resize = function () {
|
||||
if (this.editor) {
|
||||
var force = false;
|
||||
this.editor.resize(force);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Resize the formatter
|
||||
*/
|
||||
TextEditor.prototype.resize = function () {
|
||||
if (this.editor) {
|
||||
var force = false;
|
||||
this.editor.resize(force);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set json data in the formatter
|
||||
* @param {Object} json
|
||||
*/
|
||||
TextEditor.prototype.set = function(json) {
|
||||
this.setText(JSON.stringify(json, null, this.indentation));
|
||||
};
|
||||
/**
|
||||
* Set json data in the formatter
|
||||
* @param {Object} json
|
||||
*/
|
||||
TextEditor.prototype.set = function(json) {
|
||||
this.setText(JSON.stringify(json, null, this.indentation));
|
||||
};
|
||||
|
||||
/**
|
||||
* Get json data from the formatter
|
||||
* @return {Object} json
|
||||
*/
|
||||
TextEditor.prototype.get = function() {
|
||||
return util.parse(this.getText());
|
||||
};
|
||||
/**
|
||||
* Get json data from the formatter
|
||||
* @return {Object} json
|
||||
*/
|
||||
TextEditor.prototype.get = function() {
|
||||
return util.parse(this.getText());
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the text contents of the TextEditor
|
||||
* @return {String} jsonText
|
||||
*/
|
||||
TextEditor.prototype.getText = function() {
|
||||
if (this.textarea) {
|
||||
return this.textarea.value;
|
||||
}
|
||||
if (this.editor) {
|
||||
return this.editor.getValue();
|
||||
}
|
||||
return '';
|
||||
};
|
||||
/**
|
||||
* Get the text contents of the TextEditor
|
||||
* @return {String} jsonText
|
||||
*/
|
||||
TextEditor.prototype.getText = function() {
|
||||
if (this.textarea) {
|
||||
return this.textarea.value;
|
||||
}
|
||||
if (this.editor) {
|
||||
return this.editor.getValue();
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the text contents of the TextEditor
|
||||
* @param {String} jsonText
|
||||
*/
|
||||
TextEditor.prototype.setText = function(jsonText) {
|
||||
if (this.textarea) {
|
||||
this.textarea.value = jsonText;
|
||||
}
|
||||
if (this.editor) {
|
||||
this.editor.setValue(jsonText, -1);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Set the text contents of the TextEditor
|
||||
* @param {String} jsonText
|
||||
*/
|
||||
TextEditor.prototype.setText = function(jsonText) {
|
||||
if (this.textarea) {
|
||||
this.textarea.value = jsonText;
|
||||
}
|
||||
if (this.editor) {
|
||||
this.editor.setValue(jsonText, -1);
|
||||
}
|
||||
};
|
||||
|
||||
// register modes at the JSONEditor
|
||||
JSONEditor.modes.text = {
|
||||
editor: TextEditor,
|
||||
data: 'text',
|
||||
load: TextEditor.prototype.format
|
||||
};
|
||||
JSONEditor.modes.code = {
|
||||
editor: TextEditor,
|
||||
data: 'text',
|
||||
load: TextEditor.prototype.format
|
||||
};
|
||||
// define modes
|
||||
TextEditor.modes = {
|
||||
text: {
|
||||
editor: TextEditor,
|
||||
data: 'text',
|
||||
load: TextEditor.prototype.format
|
||||
},
|
||||
code: {
|
||||
editor: TextEditor,
|
||||
data: 'text',
|
||||
load: TextEditor.prototype.format
|
||||
}
|
||||
};
|
||||
|
||||
return TextEditor;
|
||||
});
|
||||
|
|
1387
src/js/TreeEditor.js
1387
src/js/TreeEditor.js
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,225 @@
|
|||
define(['./util'], function (util) {
|
||||
|
||||
/**
|
||||
* A factory function to create an AppendNode, which depends on a Node
|
||||
* @param {Node} Node
|
||||
*/
|
||||
function appendNodeFactory(Node) {
|
||||
/**
|
||||
* @constructor AppendNode
|
||||
* @extends Node
|
||||
* @param {TreeEditor} editor
|
||||
* Create a new AppendNode. This is a special node which is created at the
|
||||
* end of the list with childs for an object or array
|
||||
*/
|
||||
function AppendNode (editor) {
|
||||
/** @type {TreeEditor} */
|
||||
this.editor = editor;
|
||||
this.dom = {};
|
||||
}
|
||||
|
||||
AppendNode.prototype = new Node();
|
||||
|
||||
/**
|
||||
* Return a table row with an append button.
|
||||
* @return {Element} dom TR element
|
||||
*/
|
||||
AppendNode.prototype.getDom = function () {
|
||||
// TODO: implement a new solution for the append node
|
||||
var dom = this.dom;
|
||||
|
||||
if (dom.tr) {
|
||||
return dom.tr;
|
||||
}
|
||||
|
||||
// a row for the append button
|
||||
var trAppend = document.createElement('tr');
|
||||
trAppend.node = this;
|
||||
dom.tr = trAppend;
|
||||
|
||||
// TODO: consistent naming
|
||||
|
||||
if (this.editor.mode.edit) {
|
||||
// a cell for the dragarea column
|
||||
dom.tdDrag = document.createElement('td');
|
||||
|
||||
// create context menu
|
||||
var tdMenu = document.createElement('td');
|
||||
dom.tdMenu = tdMenu;
|
||||
var menu = document.createElement('button');
|
||||
menu.className = 'contextmenu';
|
||||
menu.title = 'Click to open the actions menu (Ctrl+M)';
|
||||
dom.menu = menu;
|
||||
tdMenu.appendChild(dom.menu);
|
||||
}
|
||||
|
||||
// a cell for the contents (showing text 'empty')
|
||||
var tdAppend = document.createElement('td');
|
||||
var domText = document.createElement('div');
|
||||
domText.innerHTML = '(empty)';
|
||||
domText.className = 'readonly';
|
||||
tdAppend.appendChild(domText);
|
||||
dom.td = tdAppend;
|
||||
dom.text = domText;
|
||||
|
||||
this.updateDom();
|
||||
|
||||
return trAppend;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the HTML dom of the Node
|
||||
*/
|
||||
AppendNode.prototype.updateDom = function () {
|
||||
var dom = this.dom;
|
||||
var tdAppend = dom.td;
|
||||
if (tdAppend) {
|
||||
tdAppend.style.paddingLeft = (this.getLevel() * 24 + 26) + 'px';
|
||||
// TODO: not so nice hard coded offset
|
||||
}
|
||||
|
||||
var domText = dom.text;
|
||||
if (domText) {
|
||||
domText.innerHTML = '(empty ' + this.parent.type + ')';
|
||||
}
|
||||
|
||||
// attach or detach the contents of the append node:
|
||||
// hide when the parent has childs, show when the parent has no childs
|
||||
var trAppend = dom.tr;
|
||||
if (!this.isVisible()) {
|
||||
if (dom.tr.firstChild) {
|
||||
if (dom.tdDrag) {
|
||||
trAppend.removeChild(dom.tdDrag);
|
||||
}
|
||||
if (dom.tdMenu) {
|
||||
trAppend.removeChild(dom.tdMenu);
|
||||
}
|
||||
trAppend.removeChild(tdAppend);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!dom.tr.firstChild) {
|
||||
if (dom.tdDrag) {
|
||||
trAppend.appendChild(dom.tdDrag);
|
||||
}
|
||||
if (dom.tdMenu) {
|
||||
trAppend.appendChild(dom.tdMenu);
|
||||
}
|
||||
trAppend.appendChild(tdAppend);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check whether the AppendNode is currently visible.
|
||||
* the AppendNode is visible when its parent has no childs (i.e. is empty).
|
||||
* @return {boolean} isVisible
|
||||
*/
|
||||
AppendNode.prototype.isVisible = function () {
|
||||
return (this.parent.childs.length == 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Show a contextmenu for this node
|
||||
* @param {HTMLElement} anchor The element to attach the menu to.
|
||||
* @param {function} [onClose] Callback method called when the context menu
|
||||
* is being closed.
|
||||
*/
|
||||
AppendNode.prototype.showContextMenu = function (anchor, onClose) {
|
||||
var node = this;
|
||||
var titles = Node.TYPE_TITLES;
|
||||
var items = [
|
||||
// create append button
|
||||
{
|
||||
'text': 'Append',
|
||||
'title': 'Append a new field with type \'auto\' (Ctrl+Shift+Ins)',
|
||||
'submenuTitle': 'Select the type of the field to be appended',
|
||||
'className': 'insert',
|
||||
'click': function () {
|
||||
node._onAppend('', '', 'auto');
|
||||
},
|
||||
'submenu': [
|
||||
{
|
||||
'text': 'Auto',
|
||||
'className': 'type-auto',
|
||||
'title': titles.auto,
|
||||
'click': function () {
|
||||
node._onAppend('', '', 'auto');
|
||||
}
|
||||
},
|
||||
{
|
||||
'text': 'Array',
|
||||
'className': 'type-array',
|
||||
'title': titles.array,
|
||||
'click': function () {
|
||||
node._onAppend('', []);
|
||||
}
|
||||
},
|
||||
{
|
||||
'text': 'Object',
|
||||
'className': 'type-object',
|
||||
'title': titles.object,
|
||||
'click': function () {
|
||||
node._onAppend('', {});
|
||||
}
|
||||
},
|
||||
{
|
||||
'text': 'String',
|
||||
'className': 'type-string',
|
||||
'title': titles.string,
|
||||
'click': function () {
|
||||
node._onAppend('', '', 'string');
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
var menu = new ContextMenu(items, {close: onClose});
|
||||
menu.show(anchor);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle an event. The event is catched centrally by the editor
|
||||
* @param {Event} event
|
||||
*/
|
||||
AppendNode.prototype.onEvent = function (event) {
|
||||
var type = event.type;
|
||||
var target = event.target || event.srcElement;
|
||||
var dom = this.dom;
|
||||
|
||||
// highlight the append nodes parent
|
||||
var menu = dom.menu;
|
||||
if (target == menu) {
|
||||
if (type == 'mouseover') {
|
||||
this.editor.highlighter.highlight(this.parent);
|
||||
}
|
||||
else if (type == 'mouseout') {
|
||||
this.editor.highlighter.unhighlight();
|
||||
}
|
||||
}
|
||||
|
||||
// context menu events
|
||||
if (type == 'click' && target == dom.menu) {
|
||||
var highlighter = this.editor.highlighter;
|
||||
highlighter.highlight(this.parent);
|
||||
highlighter.lock();
|
||||
util.addClassName(dom.menu, 'selected');
|
||||
this.showContextMenu(dom.menu, function () {
|
||||
util.removeClassName(dom.menu, 'selected');
|
||||
highlighter.unlock();
|
||||
highlighter.unhighlight();
|
||||
});
|
||||
}
|
||||
|
||||
if (type == 'keydown') {
|
||||
this.onKeyDown(event);
|
||||
}
|
||||
};
|
||||
|
||||
return AppendNode;
|
||||
}
|
||||
|
||||
// return the factory function
|
||||
return appendNodeFactory;
|
||||
});
|
|
@ -20,7 +20,7 @@
|
|||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*
|
||||
* Copyright (c) 2011-2013 Jos de Jong, http://jsoneditoronline.org
|
||||
* Copyright (c) 2011-2014 Jos de Jong, http://jsoneditoronline.org
|
||||
*
|
||||
* @author Jos de Jong, <wjosdejong@gmail.com>
|
||||
* @version @@version
|
||||
|
|
|
@ -1,94 +1,101 @@
|
|||
/**
|
||||
* create a mode box to be used in the editor menu's
|
||||
* @param {JSONEditor} editor
|
||||
* @param {String[]} modes Available modes: 'code', 'form', 'text', 'tree', 'view'
|
||||
* @param {String} current Available modes: 'code', 'form', 'text', 'tree', 'view'
|
||||
* @returns {HTMLElement} box
|
||||
*/
|
||||
function createModeBox(editor, modes, current) {
|
||||
define(['./ContextMenu'], function (ContextMenu) {
|
||||
|
||||
/**
|
||||
* Switch the mode of the editor
|
||||
* @param {String} mode
|
||||
* Create a mode box to be used in the editor menu's
|
||||
* @param {JSONEditor} editor
|
||||
* @param {String[]} modes Available modes: 'code', 'form', 'text', 'tree', 'view'
|
||||
* @param {String} current Available modes: 'code', 'form', 'text', 'tree', 'view'
|
||||
* @returns {HTMLElement} box
|
||||
*/
|
||||
function switchMode(mode) {
|
||||
// switch mode
|
||||
editor.setMode(mode);
|
||||
function createModeBox(editor, modes, current) {
|
||||
/**
|
||||
* Switch the mode of the editor
|
||||
* @param {String} mode
|
||||
*/
|
||||
function switchMode(mode) {
|
||||
// switch mode
|
||||
editor.setMode(mode);
|
||||
|
||||
// restore focus on mode box
|
||||
var modeBox = editor.dom && editor.dom.modeBox;
|
||||
if (modeBox) {
|
||||
modeBox.focus();
|
||||
}
|
||||
}
|
||||
|
||||
// available modes
|
||||
var availableModes = {
|
||||
code: {
|
||||
'text': 'Code',
|
||||
'title': 'Switch to code highlighter',
|
||||
'click': function () {
|
||||
switchMode('code')
|
||||
}
|
||||
},
|
||||
form: {
|
||||
'text': 'Form',
|
||||
'title': 'Switch to form editor',
|
||||
'click': function () {
|
||||
switchMode('form');
|
||||
}
|
||||
},
|
||||
text: {
|
||||
'text': 'Text',
|
||||
'title': 'Switch to plain text editor',
|
||||
'click': function () {
|
||||
switchMode('text');
|
||||
}
|
||||
},
|
||||
tree: {
|
||||
'text': 'Tree',
|
||||
'title': 'Switch to tree editor',
|
||||
'click': function () {
|
||||
switchMode('tree');
|
||||
}
|
||||
},
|
||||
view: {
|
||||
'text': 'View',
|
||||
'title': 'Switch to tree view',
|
||||
'click': function () {
|
||||
switchMode('view');
|
||||
// restore focus on mode box
|
||||
var modeBox = editor.dom && editor.dom.modeBox;
|
||||
if (modeBox) {
|
||||
modeBox.focus();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// list the selected modes
|
||||
var items = [];
|
||||
for (var i = 0; i < modes.length; i++) {
|
||||
var mode = modes[i];
|
||||
var item = availableModes[mode];
|
||||
if (!item) {
|
||||
throw new Error('Unknown mode "' + mode + '"');
|
||||
// available modes
|
||||
var availableModes = {
|
||||
code: {
|
||||
'text': 'Code',
|
||||
'title': 'Switch to code highlighter',
|
||||
'click': function () {
|
||||
switchMode('code')
|
||||
}
|
||||
},
|
||||
form: {
|
||||
'text': 'Form',
|
||||
'title': 'Switch to form editor',
|
||||
'click': function () {
|
||||
switchMode('form');
|
||||
}
|
||||
},
|
||||
text: {
|
||||
'text': 'Text',
|
||||
'title': 'Switch to plain text editor',
|
||||
'click': function () {
|
||||
switchMode('text');
|
||||
}
|
||||
},
|
||||
tree: {
|
||||
'text': 'Tree',
|
||||
'title': 'Switch to tree editor',
|
||||
'click': function () {
|
||||
switchMode('tree');
|
||||
}
|
||||
},
|
||||
view: {
|
||||
'text': 'View',
|
||||
'title': 'Switch to tree view',
|
||||
'click': function () {
|
||||
switchMode('view');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// list the selected modes
|
||||
var items = [];
|
||||
for (var i = 0; i < modes.length; i++) {
|
||||
var mode = modes[i];
|
||||
var item = availableModes[mode];
|
||||
if (!item) {
|
||||
throw new Error('Unknown mode "' + mode + '"');
|
||||
}
|
||||
|
||||
item.className = 'type-modes' + ((current == mode) ? ' selected' : '');
|
||||
items.push(item);
|
||||
}
|
||||
|
||||
item.className = 'type-modes' + ((current == mode) ? ' selected' : '');
|
||||
items.push(item);
|
||||
// retrieve the title of current mode
|
||||
var currentMode = availableModes[current];
|
||||
if (!currentMode) {
|
||||
throw new Error('Unknown mode "' + current + '"');
|
||||
}
|
||||
var currentTitle = currentMode.text;
|
||||
|
||||
// create the html element
|
||||
var box = document.createElement('button');
|
||||
box.className = 'modes separator';
|
||||
box.innerHTML = currentTitle + ' ▾';
|
||||
box.title = 'Switch editor mode';
|
||||
box.onclick = function () {
|
||||
var menu = new ContextMenu(items);
|
||||
menu.show(box);
|
||||
};
|
||||
|
||||
return box;
|
||||
}
|
||||
|
||||
// retrieve the title of current mode
|
||||
var currentMode = availableModes[current];
|
||||
if (!currentMode) {
|
||||
throw new Error('Unknown mode "' + current + '"');
|
||||
return {
|
||||
create: createModeBox
|
||||
}
|
||||
var currentTitle = currentMode.text;
|
||||
|
||||
// create the html element
|
||||
var box = document.createElement('button');
|
||||
box.className = 'modes separator';
|
||||
box.innerHTML = currentTitle + ' ▾';
|
||||
box.title = 'Switch editor mode';
|
||||
box.onclick = function () {
|
||||
var menu = new ContextMenu(items);
|
||||
menu.show(box);
|
||||
};
|
||||
|
||||
return box;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -2,10 +2,6 @@
|
|||
// module exports
|
||||
var jsoneditor = {
|
||||
'JSONEditor': JSONEditor,
|
||||
'JSONFormatter': function () {
|
||||
throw new Error('JSONFormatter is deprecated. ' +
|
||||
'Use JSONEditor with mode "text" or "code" instead');
|
||||
},
|
||||
'util': util
|
||||
};
|
||||
|
||||
|
|
871
src/js/util.js
871
src/js/util.js
|
@ -1,474 +1,479 @@
|
|||
// create namespace
|
||||
util = {};
|
||||
define(function () {
|
||||
|
||||
/**
|
||||
* Parse JSON using the parser built-in in the browser.
|
||||
* On exception, the jsonString is validated and a detailed error is thrown.
|
||||
* @param {String} jsonString
|
||||
*/
|
||||
util.parse = function parse(jsonString) {
|
||||
try {
|
||||
return JSON.parse(jsonString);
|
||||
}
|
||||
catch (err) {
|
||||
// try to throw a more detailed error message using validate
|
||||
util.validate(jsonString);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
// create namespace
|
||||
var util = {};
|
||||
|
||||
/**
|
||||
* Validate a string containing a JSON object
|
||||
* This method uses JSONLint to validate the String. If JSONLint is not
|
||||
* available, the built-in JSON parser of the browser is used.
|
||||
* @param {String} jsonString String with an (invalid) JSON object
|
||||
* @throws Error
|
||||
*/
|
||||
util.validate = function validate(jsonString) {
|
||||
if (typeof(jsonlint) != 'undefined') {
|
||||
jsonlint.parse(jsonString);
|
||||
}
|
||||
else {
|
||||
JSON.parse(jsonString);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Extend object a with the properties of object b
|
||||
* @param {Object} a
|
||||
* @param {Object} b
|
||||
* @return {Object} a
|
||||
*/
|
||||
util.extend = function extend(a, b) {
|
||||
for (var prop in b) {
|
||||
if (b.hasOwnProperty(prop)) {
|
||||
a[prop] = b[prop];
|
||||
/**
|
||||
* Parse JSON using the parser built-in in the browser.
|
||||
* On exception, the jsonString is validated and a detailed error is thrown.
|
||||
* @param {String} jsonString
|
||||
*/
|
||||
util.parse = function parse(jsonString) {
|
||||
try {
|
||||
return JSON.parse(jsonString);
|
||||
}
|
||||
}
|
||||
return a;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove all properties from object a
|
||||
* @param {Object} a
|
||||
* @return {Object} a
|
||||
*/
|
||||
util.clear = function clear (a) {
|
||||
for (var prop in a) {
|
||||
if (a.hasOwnProperty(prop)) {
|
||||
delete a[prop];
|
||||
catch (err) {
|
||||
// try to throw a more detailed error message using validate
|
||||
util.validate(jsonString);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
return a;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Output text to the console, if console is available
|
||||
* @param {...*} args
|
||||
*/
|
||||
util.log = function log (args) {
|
||||
if (typeof console !== 'undefined' && typeof console.log === 'function') {
|
||||
console.log.apply(console, arguments);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the type of an object
|
||||
* @param {*} object
|
||||
* @return {String} type
|
||||
*/
|
||||
util.type = function type (object) {
|
||||
if (object === null) {
|
||||
return 'null';
|
||||
}
|
||||
if (object === undefined) {
|
||||
return 'undefined';
|
||||
}
|
||||
if ((object instanceof Number) || (typeof object === 'number')) {
|
||||
return 'number';
|
||||
}
|
||||
if ((object instanceof String) || (typeof object === 'string')) {
|
||||
return 'string';
|
||||
}
|
||||
if ((object instanceof Boolean) || (typeof object === 'boolean')) {
|
||||
return 'boolean';
|
||||
}
|
||||
if ((object instanceof RegExp) || (typeof object === 'regexp')) {
|
||||
return 'regexp';
|
||||
}
|
||||
if (Array.isArray(object)) {
|
||||
return 'array';
|
||||
}
|
||||
|
||||
return 'object';
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether a text contains a url (matches when a string starts
|
||||
* with 'http://*' or 'https://*' and has no whitespace characters)
|
||||
* @param {String} text
|
||||
*/
|
||||
var isUrlRegex = /^https?:\/\/\S+$/;
|
||||
util.isUrl = function isUrl (text) {
|
||||
return (typeof text == 'string' || text instanceof String) &&
|
||||
isUrlRegex.test(text);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the absolute left value of a DOM element
|
||||
* @param {Element} elem A dom element, for example a div
|
||||
* @return {Number} left The absolute left position of this element
|
||||
* in the browser page.
|
||||
*/
|
||||
util.getAbsoluteLeft = function getAbsoluteLeft(elem) {
|
||||
var rect = elem.getBoundingClientRect();
|
||||
return rect.left + window.pageXOffset || document.scrollLeft || 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the absolute top value of a DOM element
|
||||
* @param {Element} elem A dom element, for example a div
|
||||
* @return {Number} top The absolute top position of this element
|
||||
* in the browser page.
|
||||
*/
|
||||
util.getAbsoluteTop = function getAbsoluteTop(elem) {
|
||||
var rect = elem.getBoundingClientRect();
|
||||
return rect.top + window.pageYOffset || document.scrollTop || 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* add a className to the given elements style
|
||||
* @param {Element} elem
|
||||
* @param {String} className
|
||||
*/
|
||||
util.addClassName = function addClassName(elem, className) {
|
||||
var classes = elem.className.split(' ');
|
||||
if (classes.indexOf(className) == -1) {
|
||||
classes.push(className); // add the class to the array
|
||||
elem.className = classes.join(' ');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* add a className to the given elements style
|
||||
* @param {Element} elem
|
||||
* @param {String} className
|
||||
*/
|
||||
util.removeClassName = function removeClassName(elem, className) {
|
||||
var classes = elem.className.split(' ');
|
||||
var index = classes.indexOf(className);
|
||||
if (index != -1) {
|
||||
classes.splice(index, 1); // remove the class from the array
|
||||
elem.className = classes.join(' ');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Strip the formatting from the contents of a div
|
||||
* the formatting from the div itself is not stripped, only from its childs.
|
||||
* @param {Element} divElement
|
||||
*/
|
||||
util.stripFormatting = function stripFormatting(divElement) {
|
||||
var childs = divElement.childNodes;
|
||||
for (var i = 0, iMax = childs.length; i < iMax; i++) {
|
||||
var child = childs[i];
|
||||
|
||||
// remove the style
|
||||
if (child.style) {
|
||||
// TODO: test if child.attributes does contain style
|
||||
child.removeAttribute('style');
|
||||
/**
|
||||
* Validate a string containing a JSON object
|
||||
* This method uses JSONLint to validate the String. If JSONLint is not
|
||||
* available, the built-in JSON parser of the browser is used.
|
||||
* @param {String} jsonString String with an (invalid) JSON object
|
||||
* @throws Error
|
||||
*/
|
||||
util.validate = function validate(jsonString) {
|
||||
if (typeof(jsonlint) != 'undefined') {
|
||||
jsonlint.parse(jsonString);
|
||||
}
|
||||
else {
|
||||
JSON.parse(jsonString);
|
||||
}
|
||||
};
|
||||
|
||||
// remove all attributes
|
||||
var attributes = child.attributes;
|
||||
if (attributes) {
|
||||
for (var j = attributes.length - 1; j >= 0; j--) {
|
||||
var attribute = attributes[j];
|
||||
if (attribute.specified == true) {
|
||||
child.removeAttribute(attribute.name);
|
||||
}
|
||||
/**
|
||||
* Extend object a with the properties of object b
|
||||
* @param {Object} a
|
||||
* @param {Object} b
|
||||
* @return {Object} a
|
||||
*/
|
||||
util.extend = function extend(a, b) {
|
||||
for (var prop in b) {
|
||||
if (b.hasOwnProperty(prop)) {
|
||||
a[prop] = b[prop];
|
||||
}
|
||||
}
|
||||
return a;
|
||||
};
|
||||
|
||||
// recursively strip childs
|
||||
util.stripFormatting(child);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set focus to the end of an editable div
|
||||
* code from Nico Burns
|
||||
* http://stackoverflow.com/users/140293/nico-burns
|
||||
* http://stackoverflow.com/questions/1125292/how-to-move-cursor-to-end-of-contenteditable-entity
|
||||
* @param {Element} contentEditableElement A content editable div
|
||||
*/
|
||||
util.setEndOfContentEditable = function setEndOfContentEditable(contentEditableElement) {
|
||||
var range, selection;
|
||||
if(document.createRange) {
|
||||
range = document.createRange();//Create a range (a range is a like the selection but invisible)
|
||||
range.selectNodeContents(contentEditableElement);//Select the entire contents of the element with the range
|
||||
range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start
|
||||
selection = window.getSelection();//get the selection object (allows you to change selection)
|
||||
selection.removeAllRanges();//remove any selections already made
|
||||
selection.addRange(range);//make the range you have just created the visible selection
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Select all text of a content editable div.
|
||||
* http://stackoverflow.com/a/3806004/1262753
|
||||
* @param {Element} contentEditableElement A content editable div
|
||||
*/
|
||||
util.selectContentEditable = function selectContentEditable(contentEditableElement) {
|
||||
if (!contentEditableElement || contentEditableElement.nodeName != 'DIV') {
|
||||
return;
|
||||
}
|
||||
|
||||
var sel, range;
|
||||
if (window.getSelection && document.createRange) {
|
||||
range = document.createRange();
|
||||
range.selectNodeContents(contentEditableElement);
|
||||
sel = window.getSelection();
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(range);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get text selection
|
||||
* http://stackoverflow.com/questions/4687808/contenteditable-selected-text-save-and-restore
|
||||
* @return {Range | TextRange | null} range
|
||||
*/
|
||||
util.getSelection = function getSelection() {
|
||||
if (window.getSelection) {
|
||||
var sel = window.getSelection();
|
||||
if (sel.getRangeAt && sel.rangeCount) {
|
||||
return sel.getRangeAt(0);
|
||||
/**
|
||||
* Remove all properties from object a
|
||||
* @param {Object} a
|
||||
* @return {Object} a
|
||||
*/
|
||||
util.clear = function clear (a) {
|
||||
for (var prop in a) {
|
||||
if (a.hasOwnProperty(prop)) {
|
||||
delete a[prop];
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
return a;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set text selection
|
||||
* http://stackoverflow.com/questions/4687808/contenteditable-selected-text-save-and-restore
|
||||
* @param {Range | TextRange | null} range
|
||||
*/
|
||||
util.setSelection = function setSelection(range) {
|
||||
if (range) {
|
||||
if (window.getSelection) {
|
||||
var sel = window.getSelection();
|
||||
/**
|
||||
* Output text to the console, if console is available
|
||||
* @param {...*} args
|
||||
*/
|
||||
util.log = function log (args) {
|
||||
if (typeof console !== 'undefined' && typeof console.log === 'function') {
|
||||
console.log.apply(console, arguments);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the type of an object
|
||||
* @param {*} object
|
||||
* @return {String} type
|
||||
*/
|
||||
util.type = function type (object) {
|
||||
if (object === null) {
|
||||
return 'null';
|
||||
}
|
||||
if (object === undefined) {
|
||||
return 'undefined';
|
||||
}
|
||||
if ((object instanceof Number) || (typeof object === 'number')) {
|
||||
return 'number';
|
||||
}
|
||||
if ((object instanceof String) || (typeof object === 'string')) {
|
||||
return 'string';
|
||||
}
|
||||
if ((object instanceof Boolean) || (typeof object === 'boolean')) {
|
||||
return 'boolean';
|
||||
}
|
||||
if ((object instanceof RegExp) || (typeof object === 'regexp')) {
|
||||
return 'regexp';
|
||||
}
|
||||
if (Array.isArray(object)) {
|
||||
return 'array';
|
||||
}
|
||||
|
||||
return 'object';
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether a text contains a url (matches when a string starts
|
||||
* with 'http://*' or 'https://*' and has no whitespace characters)
|
||||
* @param {String} text
|
||||
*/
|
||||
var isUrlRegex = /^https?:\/\/\S+$/;
|
||||
util.isUrl = function isUrl (text) {
|
||||
return (typeof text == 'string' || text instanceof String) &&
|
||||
isUrlRegex.test(text);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the absolute left value of a DOM element
|
||||
* @param {Element} elem A dom element, for example a div
|
||||
* @return {Number} left The absolute left position of this element
|
||||
* in the browser page.
|
||||
*/
|
||||
util.getAbsoluteLeft = function getAbsoluteLeft(elem) {
|
||||
var rect = elem.getBoundingClientRect();
|
||||
return rect.left + window.pageXOffset || document.scrollLeft || 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the absolute top value of a DOM element
|
||||
* @param {Element} elem A dom element, for example a div
|
||||
* @return {Number} top The absolute top position of this element
|
||||
* in the browser page.
|
||||
*/
|
||||
util.getAbsoluteTop = function getAbsoluteTop(elem) {
|
||||
var rect = elem.getBoundingClientRect();
|
||||
return rect.top + window.pageYOffset || document.scrollTop || 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* add a className to the given elements style
|
||||
* @param {Element} elem
|
||||
* @param {String} className
|
||||
*/
|
||||
util.addClassName = function addClassName(elem, className) {
|
||||
var classes = elem.className.split(' ');
|
||||
if (classes.indexOf(className) == -1) {
|
||||
classes.push(className); // add the class to the array
|
||||
elem.className = classes.join(' ');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* add a className to the given elements style
|
||||
* @param {Element} elem
|
||||
* @param {String} className
|
||||
*/
|
||||
util.removeClassName = function removeClassName(elem, className) {
|
||||
var classes = elem.className.split(' ');
|
||||
var index = classes.indexOf(className);
|
||||
if (index != -1) {
|
||||
classes.splice(index, 1); // remove the class from the array
|
||||
elem.className = classes.join(' ');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Strip the formatting from the contents of a div
|
||||
* the formatting from the div itself is not stripped, only from its childs.
|
||||
* @param {Element} divElement
|
||||
*/
|
||||
util.stripFormatting = function stripFormatting(divElement) {
|
||||
var childs = divElement.childNodes;
|
||||
for (var i = 0, iMax = childs.length; i < iMax; i++) {
|
||||
var child = childs[i];
|
||||
|
||||
// remove the style
|
||||
if (child.style) {
|
||||
// TODO: test if child.attributes does contain style
|
||||
child.removeAttribute('style');
|
||||
}
|
||||
|
||||
// remove all attributes
|
||||
var attributes = child.attributes;
|
||||
if (attributes) {
|
||||
for (var j = attributes.length - 1; j >= 0; j--) {
|
||||
var attribute = attributes[j];
|
||||
if (attribute.specified == true) {
|
||||
child.removeAttribute(attribute.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// recursively strip childs
|
||||
util.stripFormatting(child);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set focus to the end of an editable div
|
||||
* code from Nico Burns
|
||||
* http://stackoverflow.com/users/140293/nico-burns
|
||||
* http://stackoverflow.com/questions/1125292/how-to-move-cursor-to-end-of-contenteditable-entity
|
||||
* @param {Element} contentEditableElement A content editable div
|
||||
*/
|
||||
util.setEndOfContentEditable = function setEndOfContentEditable(contentEditableElement) {
|
||||
var range, selection;
|
||||
if(document.createRange) {
|
||||
range = document.createRange();//Create a range (a range is a like the selection but invisible)
|
||||
range.selectNodeContents(contentEditableElement);//Select the entire contents of the element with the range
|
||||
range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start
|
||||
selection = window.getSelection();//get the selection object (allows you to change selection)
|
||||
selection.removeAllRanges();//remove any selections already made
|
||||
selection.addRange(range);//make the range you have just created the visible selection
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Select all text of a content editable div.
|
||||
* http://stackoverflow.com/a/3806004/1262753
|
||||
* @param {Element} contentEditableElement A content editable div
|
||||
*/
|
||||
util.selectContentEditable = function selectContentEditable(contentEditableElement) {
|
||||
if (!contentEditableElement || contentEditableElement.nodeName != 'DIV') {
|
||||
return;
|
||||
}
|
||||
|
||||
var sel, range;
|
||||
if (window.getSelection && document.createRange) {
|
||||
range = document.createRange();
|
||||
range.selectNodeContents(contentEditableElement);
|
||||
sel = window.getSelection();
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(range);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get selected text range
|
||||
* @return {Object} params object containing parameters:
|
||||
* {Number} startOffset
|
||||
* {Number} endOffset
|
||||
* {Element} container HTML element holding the
|
||||
* selected text element
|
||||
* Returns null if no text selection is found
|
||||
*/
|
||||
util.getSelectionOffset = function getSelectionOffset() {
|
||||
var range = util.getSelection();
|
||||
|
||||
if (range && 'startOffset' in range && 'endOffset' in range &&
|
||||
range.startContainer && (range.startContainer == range.endContainer)) {
|
||||
return {
|
||||
startOffset: range.startOffset,
|
||||
endOffset: range.endOffset,
|
||||
container: range.startContainer.parentNode
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set selected text range in given element
|
||||
* @param {Object} params An object containing:
|
||||
* {Element} container
|
||||
* {Number} startOffset
|
||||
* {Number} endOffset
|
||||
*/
|
||||
util.setSelectionOffset = function setSelectionOffset(params) {
|
||||
if (document.createRange && window.getSelection) {
|
||||
var selection = window.getSelection();
|
||||
if(selection) {
|
||||
var range = document.createRange();
|
||||
// TODO: do not suppose that the first child of the container is a textnode,
|
||||
// but recursively find the textnodes
|
||||
range.setStart(params.container.firstChild, params.startOffset);
|
||||
range.setEnd(params.container.firstChild, params.endOffset);
|
||||
|
||||
util.setSelection(range);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the inner text of an HTML element (for example a div element)
|
||||
* @param {Element} element
|
||||
* @param {Object} [buffer]
|
||||
* @return {String} innerText
|
||||
*/
|
||||
util.getInnerText = function getInnerText(element, buffer) {
|
||||
var first = (buffer == undefined);
|
||||
if (first) {
|
||||
buffer = {
|
||||
'text': '',
|
||||
'flush': function () {
|
||||
var text = this.text;
|
||||
this.text = '';
|
||||
return text;
|
||||
},
|
||||
'set': function (text) {
|
||||
this.text = text;
|
||||
/**
|
||||
* Get text selection
|
||||
* http://stackoverflow.com/questions/4687808/contenteditable-selected-text-save-and-restore
|
||||
* @return {Range | TextRange | null} range
|
||||
*/
|
||||
util.getSelection = function getSelection() {
|
||||
if (window.getSelection) {
|
||||
var sel = window.getSelection();
|
||||
if (sel.getRangeAt && sel.rangeCount) {
|
||||
return sel.getRangeAt(0);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
// text node
|
||||
if (element.nodeValue) {
|
||||
return buffer.flush() + element.nodeValue;
|
||||
}
|
||||
/**
|
||||
* Set text selection
|
||||
* http://stackoverflow.com/questions/4687808/contenteditable-selected-text-save-and-restore
|
||||
* @param {Range | TextRange | null} range
|
||||
*/
|
||||
util.setSelection = function setSelection(range) {
|
||||
if (range) {
|
||||
if (window.getSelection) {
|
||||
var sel = window.getSelection();
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(range);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// divs or other HTML elements
|
||||
if (element.hasChildNodes()) {
|
||||
var childNodes = element.childNodes;
|
||||
var innerText = '';
|
||||
/**
|
||||
* Get selected text range
|
||||
* @return {Object} params object containing parameters:
|
||||
* {Number} startOffset
|
||||
* {Number} endOffset
|
||||
* {Element} container HTML element holding the
|
||||
* selected text element
|
||||
* Returns null if no text selection is found
|
||||
*/
|
||||
util.getSelectionOffset = function getSelectionOffset() {
|
||||
var range = util.getSelection();
|
||||
|
||||
for (var i = 0, iMax = childNodes.length; i < iMax; i++) {
|
||||
var child = childNodes[i];
|
||||
if (range && 'startOffset' in range && 'endOffset' in range &&
|
||||
range.startContainer && (range.startContainer == range.endContainer)) {
|
||||
return {
|
||||
startOffset: range.startOffset,
|
||||
endOffset: range.endOffset,
|
||||
container: range.startContainer.parentNode
|
||||
};
|
||||
}
|
||||
|
||||
if (child.nodeName == 'DIV' || child.nodeName == 'P') {
|
||||
var prevChild = childNodes[i - 1];
|
||||
var prevName = prevChild ? prevChild.nodeName : undefined;
|
||||
if (prevName && prevName != 'DIV' && prevName != 'P' && prevName != 'BR') {
|
||||
innerText += '\n';
|
||||
buffer.flush();
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set selected text range in given element
|
||||
* @param {Object} params An object containing:
|
||||
* {Element} container
|
||||
* {Number} startOffset
|
||||
* {Number} endOffset
|
||||
*/
|
||||
util.setSelectionOffset = function setSelectionOffset(params) {
|
||||
if (document.createRange && window.getSelection) {
|
||||
var selection = window.getSelection();
|
||||
if(selection) {
|
||||
var range = document.createRange();
|
||||
// TODO: do not suppose that the first child of the container is a textnode,
|
||||
// but recursively find the textnodes
|
||||
range.setStart(params.container.firstChild, params.startOffset);
|
||||
range.setEnd(params.container.firstChild, params.endOffset);
|
||||
|
||||
util.setSelection(range);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the inner text of an HTML element (for example a div element)
|
||||
* @param {Element} element
|
||||
* @param {Object} [buffer]
|
||||
* @return {String} innerText
|
||||
*/
|
||||
util.getInnerText = function getInnerText(element, buffer) {
|
||||
var first = (buffer == undefined);
|
||||
if (first) {
|
||||
buffer = {
|
||||
'text': '',
|
||||
'flush': function () {
|
||||
var text = this.text;
|
||||
this.text = '';
|
||||
return text;
|
||||
},
|
||||
'set': function (text) {
|
||||
this.text = text;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// text node
|
||||
if (element.nodeValue) {
|
||||
return buffer.flush() + element.nodeValue;
|
||||
}
|
||||
|
||||
// divs or other HTML elements
|
||||
if (element.hasChildNodes()) {
|
||||
var childNodes = element.childNodes;
|
||||
var innerText = '';
|
||||
|
||||
for (var i = 0, iMax = childNodes.length; i < iMax; i++) {
|
||||
var child = childNodes[i];
|
||||
|
||||
if (child.nodeName == 'DIV' || child.nodeName == 'P') {
|
||||
var prevChild = childNodes[i - 1];
|
||||
var prevName = prevChild ? prevChild.nodeName : undefined;
|
||||
if (prevName && prevName != 'DIV' && prevName != 'P' && prevName != 'BR') {
|
||||
innerText += '\n';
|
||||
buffer.flush();
|
||||
}
|
||||
innerText += util.getInnerText(child, buffer);
|
||||
buffer.set('\n');
|
||||
}
|
||||
else if (child.nodeName == 'BR') {
|
||||
innerText += buffer.flush();
|
||||
buffer.set('\n');
|
||||
}
|
||||
else {
|
||||
innerText += util.getInnerText(child, buffer);
|
||||
}
|
||||
innerText += util.getInnerText(child, buffer);
|
||||
buffer.set('\n');
|
||||
}
|
||||
else if (child.nodeName == 'BR') {
|
||||
innerText += buffer.flush();
|
||||
buffer.set('\n');
|
||||
}
|
||||
else {
|
||||
innerText += util.getInnerText(child, buffer);
|
||||
|
||||
return innerText;
|
||||
}
|
||||
else {
|
||||
if (element.nodeName == 'P' && util.getInternetExplorerVersion() != -1) {
|
||||
// On Internet Explorer, a <p> with hasChildNodes()==false is
|
||||
// rendered with a new line. Note that a <p> with
|
||||
// hasChildNodes()==true is rendered without a new line
|
||||
// Other browsers always ensure there is a <br> inside the <p>,
|
||||
// and if not, the <p> does not render a new line
|
||||
return buffer.flush();
|
||||
}
|
||||
}
|
||||
|
||||
return innerText;
|
||||
}
|
||||
else {
|
||||
if (element.nodeName == 'P' && util.getInternetExplorerVersion() != -1) {
|
||||
// On Internet Explorer, a <p> with hasChildNodes()==false is
|
||||
// rendered with a new line. Note that a <p> with
|
||||
// hasChildNodes()==true is rendered without a new line
|
||||
// Other browsers always ensure there is a <br> inside the <p>,
|
||||
// and if not, the <p> does not render a new line
|
||||
return buffer.flush();
|
||||
}
|
||||
}
|
||||
// br or unknown
|
||||
return '';
|
||||
};
|
||||
|
||||
// br or unknown
|
||||
return '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the version of Internet Explorer or a -1
|
||||
* (indicating the use of another browser).
|
||||
* Source: http://msdn.microsoft.com/en-us/library/ms537509(v=vs.85).aspx
|
||||
* @return {Number} Internet Explorer version, or -1 in case of an other browser
|
||||
*/
|
||||
util.getInternetExplorerVersion = function getInternetExplorerVersion() {
|
||||
if (_ieVersion == -1) {
|
||||
var rv = -1; // Return value assumes failure.
|
||||
if (navigator.appName == 'Microsoft Internet Explorer')
|
||||
{
|
||||
var ua = navigator.userAgent;
|
||||
var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
|
||||
if (re.exec(ua) != null) {
|
||||
rv = parseFloat( RegExp.$1 );
|
||||
/**
|
||||
* Returns the version of Internet Explorer or a -1
|
||||
* (indicating the use of another browser).
|
||||
* Source: http://msdn.microsoft.com/en-us/library/ms537509(v=vs.85).aspx
|
||||
* @return {Number} Internet Explorer version, or -1 in case of an other browser
|
||||
*/
|
||||
util.getInternetExplorerVersion = function getInternetExplorerVersion() {
|
||||
if (_ieVersion == -1) {
|
||||
var rv = -1; // Return value assumes failure.
|
||||
if (navigator.appName == 'Microsoft Internet Explorer')
|
||||
{
|
||||
var ua = navigator.userAgent;
|
||||
var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
|
||||
if (re.exec(ua) != null) {
|
||||
rv = parseFloat( RegExp.$1 );
|
||||
}
|
||||
}
|
||||
|
||||
_ieVersion = rv;
|
||||
}
|
||||
|
||||
_ieVersion = rv;
|
||||
}
|
||||
return _ieVersion;
|
||||
};
|
||||
|
||||
return _ieVersion;
|
||||
};
|
||||
/**
|
||||
* Test whether the current browser is Firefox
|
||||
* @returns {boolean} isFirefox
|
||||
*/
|
||||
util.isFirefox = function isFirefox () {
|
||||
return (navigator.userAgent.indexOf("Firefox") != -1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the current browser is Firefox
|
||||
* @returns {boolean} isFirefox
|
||||
*/
|
||||
util.isFirefox = function isFirefox () {
|
||||
return (navigator.userAgent.indexOf("Firefox") != -1);
|
||||
};
|
||||
/**
|
||||
* cached internet explorer version
|
||||
* @type {Number}
|
||||
* @private
|
||||
*/
|
||||
var _ieVersion = -1;
|
||||
|
||||
/**
|
||||
* cached internet explorer version
|
||||
* @type {Number}
|
||||
* @private
|
||||
*/
|
||||
var _ieVersion = -1;
|
||||
/**
|
||||
* Add and event listener. Works for all browsers
|
||||
* @param {Element} element An html element
|
||||
* @param {string} action The action, for example "click",
|
||||
* without the prefix "on"
|
||||
* @param {function} listener The callback function to be executed
|
||||
* @param {boolean} [useCapture] false by default
|
||||
* @return {function} the created event listener
|
||||
*/
|
||||
util.addEventListener = function addEventListener(element, action, listener, useCapture) {
|
||||
if (element.addEventListener) {
|
||||
if (useCapture === undefined)
|
||||
useCapture = false;
|
||||
|
||||
/**
|
||||
* Add and event listener. Works for all browsers
|
||||
* @param {Element} element An html element
|
||||
* @param {string} action The action, for example "click",
|
||||
* without the prefix "on"
|
||||
* @param {function} listener The callback function to be executed
|
||||
* @param {boolean} [useCapture] false by default
|
||||
* @return {function} the created event listener
|
||||
*/
|
||||
util.addEventListener = function addEventListener(element, action, listener, useCapture) {
|
||||
if (element.addEventListener) {
|
||||
if (useCapture === undefined)
|
||||
useCapture = false;
|
||||
if (action === "mousewheel" && util.isFirefox()) {
|
||||
action = "DOMMouseScroll"; // For Firefox
|
||||
}
|
||||
|
||||
if (action === "mousewheel" && util.isFirefox()) {
|
||||
action = "DOMMouseScroll"; // For Firefox
|
||||
element.addEventListener(action, listener, useCapture);
|
||||
return listener;
|
||||
} else if (element.attachEvent) {
|
||||
// Old IE browsers
|
||||
var f = function () {
|
||||
return listener.call(element, window.event);
|
||||
};
|
||||
element.attachEvent("on" + action, f);
|
||||
return f;
|
||||
}
|
||||
};
|
||||
|
||||
element.addEventListener(action, listener, useCapture);
|
||||
return listener;
|
||||
} else if (element.attachEvent) {
|
||||
// Old IE browsers
|
||||
var f = function () {
|
||||
return listener.call(element, window.event);
|
||||
};
|
||||
element.attachEvent("on" + action, f);
|
||||
return f;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Remove an event listener from an element
|
||||
* @param {Element} element An html dom element
|
||||
* @param {string} action The name of the event, for example "mousedown"
|
||||
* @param {function} listener The listener function
|
||||
* @param {boolean} [useCapture] false by default
|
||||
*/
|
||||
util.removeEventListener = function removeEventListener(element, action, listener, useCapture) {
|
||||
if (element.removeEventListener) {
|
||||
if (useCapture === undefined)
|
||||
useCapture = false;
|
||||
|
||||
/**
|
||||
* Remove an event listener from an element
|
||||
* @param {Element} element An html dom element
|
||||
* @param {string} action The name of the event, for example "mousedown"
|
||||
* @param {function} listener The listener function
|
||||
* @param {boolean} [useCapture] false by default
|
||||
*/
|
||||
util.removeEventListener = function removeEventListener(element, action, listener, useCapture) {
|
||||
if (element.removeEventListener) {
|
||||
if (useCapture === undefined)
|
||||
useCapture = false;
|
||||
if (action === "mousewheel" && util.isFirefox()) {
|
||||
action = "DOMMouseScroll"; // For Firefox
|
||||
}
|
||||
|
||||
if (action === "mousewheel" && util.isFirefox()) {
|
||||
action = "DOMMouseScroll"; // For Firefox
|
||||
element.removeEventListener(action, listener, useCapture);
|
||||
} else if (element.detachEvent) {
|
||||
// Old IE browsers
|
||||
element.detachEvent("on" + action, listener);
|
||||
}
|
||||
};
|
||||
|
||||
element.removeEventListener(action, listener, useCapture);
|
||||
} else if (element.detachEvent) {
|
||||
// Old IE browsers
|
||||
element.detachEvent("on" + action, listener);
|
||||
}
|
||||
};
|
||||
return util;
|
||||
});
|
|
@ -37,7 +37,7 @@
|
|||
|
||||
function init() {
|
||||
var container = document.getElementById('jsoneditor');
|
||||
editor = (container);
|
||||
editor = new JSONEditor(container);
|
||||
|
||||
document.getElementById('url').focus();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
RequireJS 2.1.13 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved.
|
||||
Available via the MIT or new BSD license.
|
||||
see: http://github.com/jrburke/requirejs for details
|
||||
*/
|
||||
var requirejs,require,define;
|
||||
(function(ba){function G(b){return"[object Function]"===K.call(b)}function H(b){return"[object Array]"===K.call(b)}function v(b,c){if(b){var d;for(d=0;d<b.length&&(!b[d]||!c(b[d],d,b));d+=1);}}function T(b,c){if(b){var d;for(d=b.length-1;-1<d&&(!b[d]||!c(b[d],d,b));d-=1);}}function t(b,c){return fa.call(b,c)}function m(b,c){return t(b,c)&&b[c]}function B(b,c){for(var d in b)if(t(b,d)&&c(b[d],d))break}function U(b,c,d,e){c&&B(c,function(c,g){if(d||!t(b,g))e&&"object"===typeof c&&c&&!H(c)&&!G(c)&&!(c instanceof
|
||||
RegExp)?(b[g]||(b[g]={}),U(b[g],c,d,e)):b[g]=c});return b}function u(b,c){return function(){return c.apply(b,arguments)}}function ca(b){throw b;}function da(b){if(!b)return b;var c=ba;v(b.split("."),function(b){c=c[b]});return c}function C(b,c,d,e){c=Error(c+"\nhttp://requirejs.org/docs/errors.html#"+b);c.requireType=b;c.requireModules=e;d&&(c.originalError=d);return c}function ga(b){function c(a,k,b){var f,l,c,d,e,g,i,p,k=k&&k.split("/"),h=j.map,n=h&&h["*"];if(a){a=a.split("/");l=a.length-1;j.nodeIdCompat&&
|
||||
Q.test(a[l])&&(a[l]=a[l].replace(Q,""));"."===a[0].charAt(0)&&k&&(l=k.slice(0,k.length-1),a=l.concat(a));l=a;for(c=0;c<l.length;c++)if(d=l[c],"."===d)l.splice(c,1),c-=1;else if(".."===d&&!(0===c||1==c&&".."===l[2]||".."===l[c-1])&&0<c)l.splice(c-1,2),c-=2;a=a.join("/")}if(b&&h&&(k||n)){l=a.split("/");c=l.length;a:for(;0<c;c-=1){e=l.slice(0,c).join("/");if(k)for(d=k.length;0<d;d-=1)if(b=m(h,k.slice(0,d).join("/")))if(b=m(b,e)){f=b;g=c;break a}!i&&(n&&m(n,e))&&(i=m(n,e),p=c)}!f&&i&&(f=i,g=p);f&&(l.splice(0,
|
||||
g,f),a=l.join("/"))}return(f=m(j.pkgs,a))?f:a}function d(a){z&&v(document.getElementsByTagName("script"),function(k){if(k.getAttribute("data-requiremodule")===a&&k.getAttribute("data-requirecontext")===i.contextName)return k.parentNode.removeChild(k),!0})}function e(a){var k=m(j.paths,a);if(k&&H(k)&&1<k.length)return k.shift(),i.require.undef(a),i.makeRequire(null,{skipMap:!0})([a]),!0}function n(a){var k,c=a?a.indexOf("!"):-1;-1<c&&(k=a.substring(0,c),a=a.substring(c+1,a.length));return[k,a]}function p(a,
|
||||
k,b,f){var l,d,e=null,g=k?k.name:null,j=a,p=!0,h="";a||(p=!1,a="_@r"+(K+=1));a=n(a);e=a[0];a=a[1];e&&(e=c(e,g,f),d=m(r,e));a&&(e?h=d&&d.normalize?d.normalize(a,function(a){return c(a,g,f)}):c(a,g,f):(h=c(a,g,f),a=n(h),e=a[0],h=a[1],b=!0,l=i.nameToUrl(h)));b=e&&!d&&!b?"_unnormalized"+(O+=1):"";return{prefix:e,name:h,parentMap:k,unnormalized:!!b,url:l,originalName:j,isDefine:p,id:(e?e+"!"+h:h)+b}}function s(a){var k=a.id,b=m(h,k);b||(b=h[k]=new i.Module(a));return b}function q(a,k,b){var f=a.id,c=m(h,
|
||||
f);if(t(r,f)&&(!c||c.defineEmitComplete))"defined"===k&&b(r[f]);else if(c=s(a),c.error&&"error"===k)b(c.error);else c.on(k,b)}function w(a,b){var c=a.requireModules,f=!1;if(b)b(a);else if(v(c,function(b){if(b=m(h,b))b.error=a,b.events.error&&(f=!0,b.emit("error",a))}),!f)g.onError(a)}function x(){R.length&&(ha.apply(A,[A.length,0].concat(R)),R=[])}function y(a){delete h[a];delete V[a]}function F(a,b,c){var f=a.map.id;a.error?a.emit("error",a.error):(b[f]=!0,v(a.depMaps,function(f,d){var e=f.id,g=
|
||||
m(h,e);g&&(!a.depMatched[d]&&!c[e])&&(m(b,e)?(a.defineDep(d,r[e]),a.check()):F(g,b,c))}),c[f]=!0)}function D(){var a,b,c=(a=1E3*j.waitSeconds)&&i.startTime+a<(new Date).getTime(),f=[],l=[],g=!1,h=!0;if(!W){W=!0;B(V,function(a){var i=a.map,j=i.id;if(a.enabled&&(i.isDefine||l.push(a),!a.error))if(!a.inited&&c)e(j)?g=b=!0:(f.push(j),d(j));else if(!a.inited&&(a.fetched&&i.isDefine)&&(g=!0,!i.prefix))return h=!1});if(c&&f.length)return a=C("timeout","Load timeout for modules: "+f,null,f),a.contextName=
|
||||
i.contextName,w(a);h&&v(l,function(a){F(a,{},{})});if((!c||b)&&g)if((z||ea)&&!X)X=setTimeout(function(){X=0;D()},50);W=!1}}function E(a){t(r,a[0])||s(p(a[0],null,!0)).init(a[1],a[2])}function I(a){var a=a.currentTarget||a.srcElement,b=i.onScriptLoad;a.detachEvent&&!Y?a.detachEvent("onreadystatechange",b):a.removeEventListener("load",b,!1);b=i.onScriptError;(!a.detachEvent||Y)&&a.removeEventListener("error",b,!1);return{node:a,id:a&&a.getAttribute("data-requiremodule")}}function J(){var a;for(x();A.length;){a=
|
||||
A.shift();if(null===a[0])return w(C("mismatch","Mismatched anonymous define() module: "+a[a.length-1]));E(a)}}var W,Z,i,L,X,j={waitSeconds:7,baseUrl:"./",paths:{},bundles:{},pkgs:{},shim:{},config:{}},h={},V={},$={},A=[],r={},S={},aa={},K=1,O=1;L={require:function(a){return a.require?a.require:a.require=i.makeRequire(a.map)},exports:function(a){a.usingExports=!0;if(a.map.isDefine)return a.exports?r[a.map.id]=a.exports:a.exports=r[a.map.id]={}},module:function(a){return a.module?a.module:a.module=
|
||||
{id:a.map.id,uri:a.map.url,config:function(){return m(j.config,a.map.id)||{}},exports:a.exports||(a.exports={})}}};Z=function(a){this.events=m($,a.id)||{};this.map=a;this.shim=m(j.shim,a.id);this.depExports=[];this.depMaps=[];this.depMatched=[];this.pluginMaps={};this.depCount=0};Z.prototype={init:function(a,b,c,f){f=f||{};if(!this.inited){this.factory=b;if(c)this.on("error",c);else this.events.error&&(c=u(this,function(a){this.emit("error",a)}));this.depMaps=a&&a.slice(0);this.errback=c;this.inited=
|
||||
!0;this.ignore=f.ignore;f.enabled||this.enabled?this.enable():this.check()}},defineDep:function(a,b){this.depMatched[a]||(this.depMatched[a]=!0,this.depCount-=1,this.depExports[a]=b)},fetch:function(){if(!this.fetched){this.fetched=!0;i.startTime=(new Date).getTime();var a=this.map;if(this.shim)i.makeRequire(this.map,{enableBuildCallback:!0})(this.shim.deps||[],u(this,function(){return a.prefix?this.callPlugin():this.load()}));else return a.prefix?this.callPlugin():this.load()}},load:function(){var a=
|
||||
this.map.url;S[a]||(S[a]=!0,i.load(this.map.id,a))},check:function(){if(this.enabled&&!this.enabling){var a,b,c=this.map.id;b=this.depExports;var f=this.exports,l=this.factory;if(this.inited)if(this.error)this.emit("error",this.error);else{if(!this.defining){this.defining=!0;if(1>this.depCount&&!this.defined){if(G(l)){if(this.events.error&&this.map.isDefine||g.onError!==ca)try{f=i.execCb(c,l,b,f)}catch(d){a=d}else f=i.execCb(c,l,b,f);this.map.isDefine&&void 0===f&&((b=this.module)?f=b.exports:this.usingExports&&
|
||||
(f=this.exports));if(a)return a.requireMap=this.map,a.requireModules=this.map.isDefine?[this.map.id]:null,a.requireType=this.map.isDefine?"define":"require",w(this.error=a)}else f=l;this.exports=f;if(this.map.isDefine&&!this.ignore&&(r[c]=f,g.onResourceLoad))g.onResourceLoad(i,this.map,this.depMaps);y(c);this.defined=!0}this.defining=!1;this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=!0)}}else this.fetch()}},callPlugin:function(){var a=
|
||||
this.map,b=a.id,d=p(a.prefix);this.depMaps.push(d);q(d,"defined",u(this,function(f){var l,d;d=m(aa,this.map.id);var e=this.map.name,P=this.map.parentMap?this.map.parentMap.name:null,n=i.makeRequire(a.parentMap,{enableBuildCallback:!0});if(this.map.unnormalized){if(f.normalize&&(e=f.normalize(e,function(a){return c(a,P,!0)})||""),f=p(a.prefix+"!"+e,this.map.parentMap),q(f,"defined",u(this,function(a){this.init([],function(){return a},null,{enabled:!0,ignore:!0})})),d=m(h,f.id)){this.depMaps.push(f);
|
||||
if(this.events.error)d.on("error",u(this,function(a){this.emit("error",a)}));d.enable()}}else d?(this.map.url=i.nameToUrl(d),this.load()):(l=u(this,function(a){this.init([],function(){return a},null,{enabled:!0})}),l.error=u(this,function(a){this.inited=!0;this.error=a;a.requireModules=[b];B(h,function(a){0===a.map.id.indexOf(b+"_unnormalized")&&y(a.map.id)});w(a)}),l.fromText=u(this,function(f,c){var d=a.name,e=p(d),P=M;c&&(f=c);P&&(M=!1);s(e);t(j.config,b)&&(j.config[d]=j.config[b]);try{g.exec(f)}catch(h){return w(C("fromtexteval",
|
||||
"fromText eval for "+b+" failed: "+h,h,[b]))}P&&(M=!0);this.depMaps.push(e);i.completeLoad(d);n([d],l)}),f.load(a.name,n,l,j))}));i.enable(d,this);this.pluginMaps[d.id]=d},enable:function(){V[this.map.id]=this;this.enabling=this.enabled=!0;v(this.depMaps,u(this,function(a,b){var c,f;if("string"===typeof a){a=p(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=m(L,a.id)){this.depExports[b]=c(this);return}this.depCount+=1;q(a,"defined",u(this,function(a){this.defineDep(b,
|
||||
a);this.check()}));this.errback&&q(a,"error",u(this,this.errback))}c=a.id;f=h[c];!t(L,c)&&(f&&!f.enabled)&&i.enable(a,this)}));B(this.pluginMaps,u(this,function(a){var b=m(h,a.id);b&&!b.enabled&&i.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c=this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){v(this.events[a],function(a){a(b)});"error"===a&&delete this.events[a]}};i={config:j,contextName:b,registry:h,defined:r,urlFetched:S,defQueue:A,Module:Z,makeModuleMap:p,
|
||||
nextTick:g.nextTick,onError:w,configure:function(a){a.baseUrl&&"/"!==a.baseUrl.charAt(a.baseUrl.length-1)&&(a.baseUrl+="/");var b=j.shim,c={paths:!0,bundles:!0,config:!0,map:!0};B(a,function(a,b){c[b]?(j[b]||(j[b]={}),U(j[b],a,!0,!0)):j[b]=a});a.bundles&&B(a.bundles,function(a,b){v(a,function(a){a!==b&&(aa[a]=b)})});a.shim&&(B(a.shim,function(a,c){H(a)&&(a={deps:a});if((a.exports||a.init)&&!a.exportsFn)a.exportsFn=i.makeShimExports(a);b[c]=a}),j.shim=b);a.packages&&v(a.packages,function(a){var b,
|
||||
a="string"===typeof a?{name:a}:a;b=a.name;a.location&&(j.paths[b]=a.location);j.pkgs[b]=a.name+"/"+(a.main||"main").replace(ia,"").replace(Q,"")});B(h,function(a,b){!a.inited&&!a.map.unnormalized&&(a.map=p(b))});if(a.deps||a.callback)i.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b;a.init&&(b=a.init.apply(ba,arguments));return b||a.exports&&da(a.exports)}},makeRequire:function(a,e){function j(c,d,m){var n,q;e.enableBuildCallback&&(d&&G(d))&&(d.__requireJsBuild=
|
||||
!0);if("string"===typeof c){if(G(d))return w(C("requireargs","Invalid require call"),m);if(a&&t(L,c))return L[c](h[a.id]);if(g.get)return g.get(i,c,a,j);n=p(c,a,!1,!0);n=n.id;return!t(r,n)?w(C("notloaded",'Module name "'+n+'" has not been loaded yet for context: '+b+(a?"":". Use require([])"))):r[n]}J();i.nextTick(function(){J();q=s(p(null,a));q.skipMap=e.skipMap;q.init(c,d,m,{enabled:!0});D()});return j}e=e||{};U(j,{isBrowser:z,toUrl:function(b){var d,e=b.lastIndexOf("."),k=b.split("/")[0];if(-1!==
|
||||
e&&(!("."===k||".."===k)||1<e))d=b.substring(e,b.length),b=b.substring(0,e);return i.nameToUrl(c(b,a&&a.id,!0),d,!0)},defined:function(b){return t(r,p(b,a,!1,!0).id)},specified:function(b){b=p(b,a,!1,!0).id;return t(r,b)||t(h,b)}});a||(j.undef=function(b){x();var c=p(b,a,!0),e=m(h,b);d(b);delete r[b];delete S[c.url];delete $[b];T(A,function(a,c){a[0]===b&&A.splice(c,1)});e&&(e.events.defined&&($[b]=e.events),y(b))});return j},enable:function(a){m(h,a.id)&&s(a).enable()},completeLoad:function(a){var b,
|
||||
c,d=m(j.shim,a)||{},g=d.exports;for(x();A.length;){c=A.shift();if(null===c[0]){c[0]=a;if(b)break;b=!0}else c[0]===a&&(b=!0);E(c)}c=m(h,a);if(!b&&!t(r,a)&&c&&!c.inited){if(j.enforceDefine&&(!g||!da(g)))return e(a)?void 0:w(C("nodefine","No define call for "+a,null,[a]));E([a,d.deps||[],d.exportsFn])}D()},nameToUrl:function(a,b,c){var d,e,h;(d=m(j.pkgs,a))&&(a=d);if(d=m(aa,a))return i.nameToUrl(d,b,c);if(g.jsExtRegExp.test(a))d=a+(b||"");else{d=j.paths;a=a.split("/");for(e=a.length;0<e;e-=1)if(h=a.slice(0,
|
||||
e).join("/"),h=m(d,h)){H(h)&&(h=h[0]);a.splice(0,e,h);break}d=a.join("/");d+=b||(/^data\:|\?/.test(d)||c?"":".js");d=("/"===d.charAt(0)||d.match(/^[\w\+\.\-]+:/)?"":j.baseUrl)+d}return j.urlArgs?d+((-1===d.indexOf("?")?"?":"&")+j.urlArgs):d},load:function(a,b){g.load(i,a,b)},execCb:function(a,b,c,d){return b.apply(d,c)},onScriptLoad:function(a){if("load"===a.type||ja.test((a.currentTarget||a.srcElement).readyState))N=null,a=I(a),i.completeLoad(a.id)},onScriptError:function(a){var b=I(a);if(!e(b.id))return w(C("scripterror",
|
||||
"Script error for: "+b.id,a,[b.id]))}};i.require=i.makeRequire();return i}var g,x,y,D,I,E,N,J,s,O,ka=/(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,la=/[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,Q=/\.js$/,ia=/^\.\//;x=Object.prototype;var K=x.toString,fa=x.hasOwnProperty,ha=Array.prototype.splice,z=!!("undefined"!==typeof window&&"undefined"!==typeof navigator&&window.document),ea=!z&&"undefined"!==typeof importScripts,ja=z&&"PLAYSTATION 3"===navigator.platform?/^complete$/:/^(complete|loaded)$/,
|
||||
Y="undefined"!==typeof opera&&"[object Opera]"===opera.toString(),F={},q={},R=[],M=!1;if("undefined"===typeof define){if("undefined"!==typeof requirejs){if(G(requirejs))return;q=requirejs;requirejs=void 0}"undefined"!==typeof require&&!G(require)&&(q=require,require=void 0);g=requirejs=function(b,c,d,e){var n,p="_";!H(b)&&"string"!==typeof b&&(n=b,H(c)?(b=c,c=d,d=e):b=[]);n&&n.context&&(p=n.context);(e=m(F,p))||(e=F[p]=g.s.newContext(p));n&&e.configure(n);return e.require(b,c,d)};g.config=function(b){return g(b)};
|
||||
g.nextTick="undefined"!==typeof setTimeout?function(b){setTimeout(b,4)}:function(b){b()};require||(require=g);g.version="2.1.13";g.jsExtRegExp=/^\/|:|\?|\.js$/;g.isBrowser=z;x=g.s={contexts:F,newContext:ga};g({});v(["toUrl","undef","defined","specified"],function(b){g[b]=function(){var c=F._;return c.require[b].apply(c,arguments)}});if(z&&(y=x.head=document.getElementsByTagName("head")[0],D=document.getElementsByTagName("base")[0]))y=x.head=D.parentNode;g.onError=ca;g.createNode=function(b){var c=
|
||||
b.xhtml?document.createElementNS("http://www.w3.org/1999/xhtml","html:script"):document.createElement("script");c.type=b.scriptType||"text/javascript";c.charset="utf-8";c.async=!0;return c};g.load=function(b,c,d){var e=b&&b.config||{};if(z)return e=g.createNode(e,c,d),e.setAttribute("data-requirecontext",b.contextName),e.setAttribute("data-requiremodule",c),e.attachEvent&&!(e.attachEvent.toString&&0>e.attachEvent.toString().indexOf("[native code"))&&!Y?(M=!0,e.attachEvent("onreadystatechange",b.onScriptLoad)):
|
||||
(e.addEventListener("load",b.onScriptLoad,!1),e.addEventListener("error",b.onScriptError,!1)),e.src=d,J=e,D?y.insertBefore(e,D):y.appendChild(e),J=null,e;if(ea)try{importScripts(d),b.completeLoad(c)}catch(m){b.onError(C("importscripts","importScripts failed for "+c+" at "+d,m,[c]))}};z&&!q.skipDataMain&&T(document.getElementsByTagName("script"),function(b){y||(y=b.parentNode);if(I=b.getAttribute("data-main"))return s=I,q.baseUrl||(E=s.split("/"),s=E.pop(),O=E.length?E.join("/")+"/":"./",q.baseUrl=
|
||||
O),s=s.replace(Q,""),g.jsExtRegExp.test(s)&&(s=I),q.deps=q.deps?q.deps.concat(s):[s],!0});define=function(b,c,d){var e,g;"string"!==typeof b&&(d=c,c=b,b=null);H(c)||(d=c,c=null);!c&&G(d)&&(c=[],d.length&&(d.toString().replace(ka,"").replace(la,function(b,d){c.push(d)}),c=(1===d.length?["require"]:["require","exports","module"]).concat(c)));if(M){if(!(e=J))N&&"interactive"===N.readyState||T(document.getElementsByTagName("script"),function(b){if("interactive"===b.readyState)return N=b}),e=N;e&&(b||
|
||||
(b=e.getAttribute("data-requiremodule")),g=F[e.getAttribute("data-requirecontext")])}(g?g.defQueue:R).push([b,c,d])};define.amd={jQuery:!0};g.exec=function(b){return eval(b)};g(q)}})(this);
|
|
@ -3,9 +3,22 @@
|
|||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|
||||
|
||||
<!-- require.js -->
|
||||
<script src="require.js"></script>
|
||||
<script>
|
||||
require.config({
|
||||
packages: [
|
||||
{
|
||||
name: 'JSONEditor',
|
||||
location: '../src/js/',
|
||||
main: 'JSONEditor'
|
||||
}
|
||||
]
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- json editor -->
|
||||
<link rel="stylesheet" type="text/css" href="../jsoneditor.css">
|
||||
<script type="text/javascript" src="../jsoneditor.js"></script>
|
||||
|
||||
<!-- ace editor -->
|
||||
<script type="text/javascript" src="../node_modules/ace/build/src-min/ace.js"></script>
|
||||
|
@ -43,26 +56,30 @@
|
|||
<div id="jsoneditor"></div>
|
||||
|
||||
<script type="text/javascript" >
|
||||
var container = document.getElementById('jsoneditor');
|
||||
var container, options, json, editor;
|
||||
|
||||
var options = {
|
||||
mode: 'tree',
|
||||
modes: ['code', 'form', 'text', 'tree', 'view'], // allowed modes
|
||||
error: function (err) {
|
||||
alert(err.toString());
|
||||
}
|
||||
};
|
||||
require(['JSONEditor'], function (JSONEditor) {
|
||||
container = document.getElementById('jsoneditor');
|
||||
|
||||
var json = {
|
||||
"array": [1, 2, 3],
|
||||
"boolean": true,
|
||||
"null": null,
|
||||
"number": 123,
|
||||
"object": {"a": "b", "c": "d"},
|
||||
"string": "Hello World"
|
||||
};
|
||||
options = {
|
||||
mode: 'tree',
|
||||
modes: ['code', 'form', 'text', 'tree', 'view'], // allowed modes
|
||||
error: function (err) {
|
||||
alert(err.toString());
|
||||
}
|
||||
};
|
||||
|
||||
var editor = new jsoneditor.JSONEditor(container, options, json);
|
||||
json = {
|
||||
"array": [1, 2, 3],
|
||||
"boolean": true,
|
||||
"null": null,
|
||||
"number": 123,
|
||||
"object": {"a": "b", "c": "d"},
|
||||
"string": "Hello World"
|
||||
};
|
||||
|
||||
editor = new JSONEditor(container, options, json);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|
||||
|
||||
<!-- json editor -->
|
||||
<script type="text/javascript" src="../jsoneditor.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../jsoneditor.css">
|
||||
|
||||
<!-- ace editor -->
|
||||
<script type="text/javascript" src="../node_modules/ace/build/src-min/ace.js"></script>
|
||||
<script type="text/javascript" src="../src/js/ace/theme-jsoneditor.js"></script>
|
||||
|
||||
<!-- json lint -->
|
||||
<script type="text/javascript" src="../node_modules/jsonlint/lib/jsonlint.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
font: 10.5pt arial;
|
||||
color: #4d4d4d;
|
||||
line-height: 150%;
|
||||
width: 500px;
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
#jsoneditor {
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p>
|
||||
Switch editor mode using the mode box.
|
||||
Note that the mode can be changed programmatically as well using the method
|
||||
<code>editor.setMode(mode)</code>, try it in the console of your browser.
|
||||
</p>
|
||||
|
||||
<div id="jsoneditor"></div>
|
||||
|
||||
<script type="text/javascript" >
|
||||
var container, options, json, editor;
|
||||
|
||||
container = document.getElementById('jsoneditor');
|
||||
|
||||
options = {
|
||||
mode: 'tree',
|
||||
modes: ['code', 'form', 'text', 'tree', 'view'], // allowed modes
|
||||
error: function (err) {
|
||||
alert(err.toString());
|
||||
}
|
||||
};
|
||||
|
||||
json = {
|
||||
"array": [1, 2, 3],
|
||||
"boolean": true,
|
||||
"null": null,
|
||||
"number": 123,
|
||||
"object": {"a": "b", "c": "d"},
|
||||
"string": "Hello World"
|
||||
};
|
||||
|
||||
editor = new JSONEditor(container, options, json);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue