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