Refactored JSONEditor sourcecode to AMD modules

This commit is contained in:
jos 2014-05-29 22:13:37 +02:00
parent 6eea2b6335
commit d6da7f548e
33 changed files with 11909 additions and 11514 deletions

View File

@ -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.

View File

@ -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 = {

View File

@ -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,

View File

@ -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 () {

View File

@ -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 () {

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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 () {

View File

@ -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);

87
gulpfile.js Normal file
View File

@ -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']);

File diff suppressed because it is too large Load Diff

1
jsoneditor.map Normal file

File diff suppressed because one or more lines are too long

7
jsoneditor.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -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"
}

View File

@ -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);
}
};

View File

@ -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;
});

View File

@ -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;
});

View File

@ -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;
});

View File

@ -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;
});

File diff suppressed because it is too large Load Diff

View File

@ -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&nbsp;results'; break;
case 1: this.dom.results.innerHTML = '1&nbsp;result'; break;
default: this.dom.results.innerHTML = resultCount + '&nbsp;results'; break;
// display search results
if (text != undefined) {
var resultCount = this.results.length;
switch (resultCount) {
case 0: this.dom.results.innerHTML = 'no&nbsp;results'; break;
case 1: this.dom.results.innerHTML = '1&nbsp;result'; break;
default: this.dom.results.innerHTML = resultCount + '&nbsp;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
}
};

View File

@ -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;
});

File diff suppressed because it is too large Load Diff

225
src/js/appendNodeFactory.js Normal file
View File

@ -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;
});

View File

@ -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

View File

@ -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 + ' &#x25BE;';
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 + ' &#x25BE;';
box.title = 'Switch editor mode';
box.onclick = function () {
var menu = new ContextMenu(items);
menu.show(box);
};
return box;
}
});

View File

@ -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
};

View File

@ -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;
});

View File

@ -37,7 +37,7 @@
function init() {
var container = document.getElementById('jsoneditor');
editor = (container);
editor = new JSONEditor(container);
document.getElementById('url').focus();
}

36
test/require.js Normal file
View File

@ -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);

View File

@ -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>

70
test/test_build.html Normal file
View File

@ -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>