hyperion.ng/assets/webconfig/js/lib/jsoneditor.js
birdhouse 0f2d72fc0a Renaming versionBranch and JSONEditor Enhancement
- versionBranch was renamed to watchedVersionBranch (including all variables and strings. even config)
- Enhancements for JSON Editor. Detection of advanced was not so good. Now you can see advaced if advanced or expert is selected. expert is the highest and should also make advanced visible.
- The levels default, advanced, expert are now also respected in the information tabs. Same logic as for the forms itself. Expert is highest and will also show all advanced settings.
2019-07-13 00:06:58 +02:00

7288 lines
219 KiB
JavaScript
Executable File

/*! JSON Editor v0.7.28 - JSON Schema -> HTML Editor
* By Jeremy Dorn - https://github.com/jdorn/json-editor/
* Released under the MIT license
*
* Date: 2016-08-07
*/
/**
* See README.md for requirements and usage info
*/
(function() {
/*jshint loopfunc: true */
/* Simple JavaScript Inheritance
* By John Resig http://ejohn.org/
* MIT Licensed.
*/
// Inspired by base2 and Prototype
var Class;
(function(){
var initializing = false, fnTest = /xyz/.test(function(){window.postMessage("xyz");}) ? /\b_super\b/ : /.*/;
// The base Class implementation (does nothing)
Class = function(){};
// Create a new Class that inherits from this class
Class.extend = function extend(prop) {
var _super = this.prototype;
// Instantiate a base class (but only create the instance,
// don't run the init constructor)
initializing = true;
var prototype = new this();
initializing = false;
// Copy the properties over onto the new prototype
for (var name in prop) {
// Check if we're overwriting an existing function
prototype[name] = typeof prop[name] == "function" &&
typeof _super[name] == "function" && fnTest.test(prop[name]) ?
(function(name, fn){
return function() {
var tmp = this._super;
// Add a new ._super() method that is the same method
// but on the super-class
this._super = _super[name];
// The method only need to be bound temporarily, so we
// remove it when we're done executing
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, prop[name]) :
prop[name];
}
// The dummy class constructor
function Class() {
// All construction is actually done in the init method
if ( !initializing && this.init )
this.init.apply(this, arguments);
}
// Populate our constructed prototype object
Class.prototype = prototype;
// Enforce the constructor to be what we expect
Class.prototype.constructor = Class;
// And make this class extendable
Class.extend = extend;
return Class;
};
return Class;
})();
// CustomEvent constructor polyfill
// From MDN
(function () {
function CustomEvent ( event, params ) {
params = params || { bubbles: false, cancelable: false, detail: undefined };
var evt = document.createEvent( 'CustomEvent' );
evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail );
return evt;
}
CustomEvent.prototype = window.Event.prototype;
window.CustomEvent = CustomEvent;
})();
// requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel
// MIT license
(function() {
var lastTime = 0;
var vendors = ['ms', 'moz', 'webkit', 'o'];
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] ||
window[vendors[x]+'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame)
window.requestAnimationFrame = function(callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function() { callback(currTime + timeToCall); },
timeToCall);
lastTime = currTime + timeToCall;
return id;
};
if (!window.cancelAnimationFrame)
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}());
// Array.isArray polyfill
// From MDN
(function() {
if(!Array.isArray) {
Array.isArray = function(arg) {
return Object.prototype.toString.call(arg) === '[object Array]';
};
}
}());
/**
* Taken from jQuery 2.1.3
*
* @param obj
* @returns {boolean}
*/
var $isplainobject = function( obj ) {
// Not plain objects:
// - Any object or value whose internal [[Class]] property is not "[object Object]"
// - DOM nodes
// - window
if (typeof obj !== "object" || obj.nodeType || (obj !== null && obj === obj.window)) {
return false;
}
if (obj.constructor && !Object.prototype.hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf")) {
return false;
}
// If the function hasn't returned already, we're confident that
// |obj| is a plain object, created by {} or constructed with new Object
return true;
};
var $extend = function(destination) {
var source, i,property;
for(i=1; i<arguments.length; i++) {
source = arguments[i];
for (property in source) {
if(!source.hasOwnProperty(property)) continue;
if(source[property] && $isplainobject(source[property])) {
if(!destination.hasOwnProperty(property)) destination[property] = {};
$extend(destination[property], source[property]);
}
else {
destination[property] = source[property];
}
}
}
return destination;
};
var $each = function(obj,callback) {
if(!obj || typeof obj !== "object") return;
var i;
if(Array.isArray(obj) || (typeof obj.length === 'number' && obj.length > 0 && (obj.length - 1) in obj)) {
for(i=0; i<obj.length; i++) {
if(callback(i,obj[i])===false) return;
}
}
else {
if (Object.keys) {
var keys = Object.keys(obj);
for(i=0; i<keys.length; i++) {
if(callback(keys[i],obj[keys[i]])===false) return;
}
}
else {
for(i in obj) {
if(!obj.hasOwnProperty(i)) continue;
if(callback(i,obj[i])===false) return;
}
}
}
};
var $trigger = function(el,event) {
var e = document.createEvent('HTMLEvents');
e.initEvent(event, true, true);
el.dispatchEvent(e);
};
var $triggerc = function(el,event) {
var e = new CustomEvent(event,{
bubbles: true,
cancelable: true
});
el.dispatchEvent(e);
};
var JSONEditor = function(element,options) {
if (!(element instanceof Element)) {
throw new Error('element should be an instance of Element');
}
options = $extend({},JSONEditor.defaults.options,options||{});
this.element = element;
this.options = options;
this.init();
};
JSONEditor.prototype = {
// necessary since we remove the ctor property by doing a literal assignment. Without this
// the $isplainobject function will think that this is a plain object.
constructor: JSONEditor,
init: function() {
var self = this;
this.ready = false;
var theme_class = JSONEditor.defaults.themes[this.options.theme || JSONEditor.defaults.theme];
if(!theme_class) throw "Unknown theme " + (this.options.theme || JSONEditor.defaults.theme);
this.access = this.options.access;
this.schema = this.options.schema;
this.theme = new theme_class();
this.template = this.options.template;
this.refs = this.options.refs || {};
this.uuid = 0;
this.__data = {};
var icon_class = JSONEditor.defaults.iconlibs[this.options.iconlib || JSONEditor.defaults.iconlib];
if(icon_class) this.iconlib = new icon_class();
this.root_container = this.theme.getContainer();
this.element.appendChild(this.root_container);
// Fetch all external refs via ajax
this._loadExternalRefs(this.schema, function() {
self._getDefinitions(self.schema);
// Validator options
var validator_options = {};
if(self.options.custom_validators) {
validator_options.custom_validators = self.options.custom_validators;
}
self.validator = new JSONEditor.Validator(self,null,validator_options);
// Create the root editor
var editor_class = self.getEditorClass(self.schema);
self.root = self.createEditor(editor_class, {
jsoneditor: self,
schema: self.schema,
required: true,
container: self.root_container
});
self.root.preBuild();
self.root.build();
self.root.postBuild();
// Starting data
if(self.options.startval) self.root.setValue(self.options.startval);
self.validation_results = self.validator.validate(self.root.getValue());
self.root.showValidationErrors(self.validation_results);
self.ready = true;
// Fire ready event asynchronously
window.requestAnimationFrame(function() {
if(!self.ready) return;
self.validation_results = self.validator.validate(self.root.getValue());
self.root.showValidationErrors(self.validation_results);
self.trigger('ready');
self.trigger('change');
});
});
},
getValue: function() {
if(!this.ready) throw "JSON Editor not ready yet. Listen for 'ready' event before getting the value";
return this.root.getValue();
},
setValue: function(value) {
if(!this.ready) throw "JSON Editor not ready yet. Listen for 'ready' event before setting the value";
this.root.setValue(value);
return this;
},
validate: function(value) {
if(!this.ready) throw "JSON Editor not ready yet. Listen for 'ready' event before validating";
// Custom value
if(arguments.length === 1) {
return this.validator.validate(value);
}
// Current value (use cached result)
else {
return this.validation_results;
}
},
destroy: function() {
if(this.destroyed) return;
if(!this.ready) return;
this.schema = null;
this.options = null;
this.root.destroy();
this.root = null;
this.root_container = null;
this.validator = null;
this.validation_results = null;
this.theme = null;
this.iconlib = null;
this.access = null;
this.template = null;
this.__data = null;
this.ready = false;
this.element.innerHTML = '';
this.destroyed = true;
},
on: function(event, callback) {
this.callbacks = this.callbacks || {};
this.callbacks[event] = this.callbacks[event] || [];
this.callbacks[event].push(callback);
return this;
},
off: function(event, callback) {
// Specific callback
if(event && callback) {
this.callbacks = this.callbacks || {};
this.callbacks[event] = this.callbacks[event] || [];
var newcallbacks = [];
for(var i=0; i<this.callbacks[event].length; i++) {
if(this.callbacks[event][i]===callback) continue;
newcallbacks.push(this.callbacks[event][i]);
}
this.callbacks[event] = newcallbacks;
}
// All callbacks for a specific event
else if(event) {
this.callbacks = this.callbacks || {};
this.callbacks[event] = [];
}
// All callbacks for all events
else {
this.callbacks = {};
}
return this;
},
trigger: function(event) {
if(this.callbacks && this.callbacks[event] && this.callbacks[event].length) {
for(var i=0; i<this.callbacks[event].length; i++) {
this.callbacks[event][i]();
}
}
return this;
},
setOption: function(option, value) {
if(option === "show_errors") {
this.options.show_errors = value;
this.onChange();
}
// Only the `show_errors` option is supported for now
else {
throw "Option "+option+" must be set during instantiation and cannot be changed later";
}
return this;
},
getEditorClass: function(schema) {
var classname;
schema = this.expandSchema(schema);
$each(JSONEditor.defaults.resolvers,function(i,resolver) {
var tmp = resolver(schema);
if(tmp) {
if(JSONEditor.defaults.editors[tmp]) {
classname = tmp;
return false;
}
}
});
if(!classname) throw "Unknown editor for schema "+JSON.stringify(schema);
if(!JSONEditor.defaults.editors[classname]) throw "Unknown editor "+classname;
return JSONEditor.defaults.editors[classname];
},
createEditor: function(editor_class, options) {
options = $extend({},editor_class.options||{},options);
return new editor_class(options);
},
onChange: function() {
if(!this.ready) return;
if(this.firing_change) return;
this.firing_change = true;
var self = this;
window.requestAnimationFrame(function() {
self.firing_change = false;
if(!self.ready) return;
// Validate and cache results
self.validation_results = self.validator.validate(self.root.getValue());
if(self.options.show_errors !== "never") {
self.root.showValidationErrors(self.validation_results);
}
else {
self.root.showValidationErrors([]);
}
// Fire change event
self.trigger('change');
});
return this;
},
compileTemplate: function(template, name) {
name = name || JSONEditor.defaults.template;
var engine;
// Specifying a preset engine
if(typeof name === 'string') {
if(!JSONEditor.defaults.templates[name]) throw "Unknown template engine "+name;
engine = JSONEditor.defaults.templates[name]();
if(!engine) throw "Template engine "+name+" missing required library.";
}
// Specifying a custom engine
else {
engine = name;
}
if(!engine) throw "No template engine set";
if(!engine.compile) throw "Invalid template engine set";
return engine.compile(template);
},
_data: function(el,key,value) {
// Setting data
if(arguments.length === 3) {
var uuid;
if(el.hasAttribute('data-jsoneditor-'+key)) {
uuid = el.getAttribute('data-jsoneditor-'+key);
}
else {
uuid = this.uuid++;
el.setAttribute('data-jsoneditor-'+key,uuid);
}
this.__data[uuid] = value;
}
// Getting data
else {
// No data stored
if(!el.hasAttribute('data-jsoneditor-'+key)) return null;
return this.__data[el.getAttribute('data-jsoneditor-'+key)];
}
},
registerEditor: function(editor) {
this.editors = this.editors || {};
this.editors[editor.path] = editor;
return this;
},
unregisterEditor: function(editor) {
this.editors = this.editors || {};
this.editors[editor.path] = null;
return this;
},
getEditor: function(path) {
if(!this.editors) return;
return this.editors[path];
},
watch: function(path,callback) {
this.watchlist = this.watchlist || {};
this.watchlist[path] = this.watchlist[path] || [];
this.watchlist[path].push(callback);
return this;
},
unwatch: function(path,callback) {
if(!this.watchlist || !this.watchlist[path]) return this;
// If removing all callbacks for a path
if(!callback) {
this.watchlist[path] = null;
return this;
}
var newlist = [];
for(var i=0; i<this.watchlist[path].length; i++) {
if(this.watchlist[path][i] === callback) continue;
else newlist.push(this.watchlist[path][i]);
}
this.watchlist[path] = newlist.length? newlist : null;
return this;
},
notifyWatchers: function(path) {
if(!this.watchlist || !this.watchlist[path]) return this;
for(var i=0; i<this.watchlist[path].length; i++) {
this.watchlist[path][i]();
}
},
isEnabled: function() {
return !this.root || this.root.isEnabled();
},
enable: function() {
this.root.enable();
},
disable: function() {
this.root.disable();
},
_getDefinitions: function(schema,path) {
path = path || '#/definitions/';
if(schema.definitions) {
for(var i in schema.definitions) {
if(!schema.definitions.hasOwnProperty(i)) continue;
this.refs[path+i] = schema.definitions[i];
if(schema.definitions[i].definitions) {
this._getDefinitions(schema.definitions[i],path+i+'/definitions/');
}
}
}
},
_getExternalRefs: function(schema) {
var refs = {};
var merge_refs = function(newrefs) {
for(var i in newrefs) {
if(newrefs.hasOwnProperty(i)) {
refs[i] = true;
}
}
};
if(schema.$ref && typeof schema.$ref !== "object" && schema.$ref.substr(0,1) !== "#" && !this.refs[schema.$ref]) {
refs[schema.$ref] = true;
}
for(var i in schema) {
if(!schema.hasOwnProperty(i)) continue;
if(schema[i] && typeof schema[i] === "object" && Array.isArray(schema[i])) {
for(var j=0; j<schema[i].length; j++) {
if(typeof schema[i][j]==="object") {
merge_refs(this._getExternalRefs(schema[i][j]));
}
}
}
else if(schema[i] && typeof schema[i] === "object") {
merge_refs(this._getExternalRefs(schema[i]));
}
}
return refs;
},
_loadExternalRefs: function(schema, callback) {
var self = this;
var refs = this._getExternalRefs(schema);
var done = 0, waiting = 0, callback_fired = false;
$each(refs,function(url) {
if(self.refs[url]) return;
if(!self.options.ajax) throw "Must set ajax option to true to load external ref "+url;
self.refs[url] = 'loading';
waiting++;
var r = new XMLHttpRequest();
r.open("GET", url, true);
r.onreadystatechange = function () {
if (r.readyState != 4) return;
// Request succeeded
if(r.status === 200) {
var response;
try {
response = JSON.parse(r.responseText);
}
catch(e) {
window.console.log(e);
throw "Failed to parse external ref "+url;
}
if(!response || typeof response !== "object") throw "External ref does not contain a valid schema - "+url;
self.refs[url] = response;
self._loadExternalRefs(response,function() {
done++;
if(done >= waiting && !callback_fired) {
callback_fired = true;
callback();
}
});
}
// Request failed
else {
window.console.log(r);
throw "Failed to fetch ref via ajax- "+url;
}
};
r.send();
});
if(!waiting) {
callback();
}
},
expandRefs: function(schema) {
schema = $extend({},schema);
while (schema.$ref) {
var ref = schema.$ref;
delete schema.$ref;
if(!this.refs[ref]) ref = decodeURIComponent(ref);
schema = this.extendSchemas(schema,this.refs[ref]);
}
return schema;
},
expandSchema: function(schema) {
var self = this;
var extended = $extend({},schema);
var i;
// Version 3 `type`
if(typeof schema.type === 'object') {
// Array of types
if(Array.isArray(schema.type)) {
$each(schema.type, function(key,value) {
// Schema
if(typeof value === 'object') {
schema.type[key] = self.expandSchema(value);
}
});
}
// Schema
else {
schema.type = self.expandSchema(schema.type);
}
}
// Version 3 `disallow`
if(typeof schema.disallow === 'object') {
// Array of types
if(Array.isArray(schema.disallow)) {
$each(schema.disallow, function(key,value) {
// Schema
if(typeof value === 'object') {
schema.disallow[key] = self.expandSchema(value);
}
});
}
// Schema
else {
schema.disallow = self.expandSchema(schema.disallow);
}
}
// Version 4 `anyOf`
if(schema.anyOf) {
$each(schema.anyOf, function(key,value) {
schema.anyOf[key] = self.expandSchema(value);
});
}
// Version 4 `dependencies` (schema dependencies)
if(schema.dependencies) {
$each(schema.dependencies,function(key,value) {
if(typeof value === "object" && !(Array.isArray(value))) {
schema.dependencies[key] = self.expandSchema(value);
}
});
}
// Version 4 `not`
if(schema.not) {
schema.not = this.expandSchema(schema.not);
}
// allOf schemas should be merged into the parent
if(schema.allOf) {
for(i=0; i<schema.allOf.length; i++) {
extended = this.extendSchemas(extended,this.expandSchema(schema.allOf[i]));
}
delete extended.allOf;
}
// extends schemas should be merged into parent
if(schema["extends"]) {
// If extends is a schema
if(!(Array.isArray(schema["extends"]))) {
extended = this.extendSchemas(extended,this.expandSchema(schema["extends"]));
}
// If extends is an array of schemas
else {
for(i=0; i<schema["extends"].length; i++) {
extended = this.extendSchemas(extended,this.expandSchema(schema["extends"][i]));
}
}
delete extended["extends"];
}
// parent should be merged into oneOf schemas
if(schema.oneOf) {
var tmp = $extend({},extended);
delete tmp.oneOf;
for(i=0; i<schema.oneOf.length; i++) {
extended.oneOf[i] = this.extendSchemas(this.expandSchema(schema.oneOf[i]),tmp);
}
}
return this.expandRefs(extended);
},
extendSchemas: function(obj1, obj2) {
obj1 = $extend({},obj1);
obj2 = $extend({},obj2);
var self = this;
var extended = {};
$each(obj1, function(prop,val) {
// If this key is also defined in obj2, merge them
if(typeof obj2[prop] !== "undefined") {
// Required and defaultProperties arrays should be unioned together
if((prop === 'required'||prop === 'defaultProperties') && typeof val === "object" && Array.isArray(val)) {
// Union arrays and unique
extended[prop] = val.concat(obj2[prop]).reduce(function(p, c) {
if (p.indexOf(c) < 0) p.push(c);
return p;
}, []);
}
// Type should be intersected and is either an array or string
else if(prop === 'type' && (typeof val === "string" || Array.isArray(val))) {
// Make sure we're dealing with arrays
if(typeof val === "string") val = [val];
if(typeof obj2.type === "string") obj2.type = [obj2.type];
// If type is only defined in the first schema, keep it
if(!obj2.type || !obj2.type.length) {
extended.type = val;
}
// If type is defined in both schemas, do an intersect
else {
extended.type = val.filter(function(n) {
return obj2.type.indexOf(n) !== -1;
});
}
// If there's only 1 type and it's a primitive, use a string instead of array
if(extended.type.length === 1 && typeof extended.type[0] === "string") {
extended.type = extended.type[0];
}
// Remove the type property if it's empty
else if(extended.type.length === 0) {
delete extended.type;
}
}
// All other arrays should be intersected (enum, etc.)
else if(typeof val === "object" && Array.isArray(val)){
extended[prop] = val.filter(function(n) {
return obj2[prop].indexOf(n) !== -1;
});
}
// Objects should be recursively merged
else if(typeof val === "object" && val !== null) {
extended[prop] = self.extendSchemas(val,obj2[prop]);
}
// Otherwise, use the first value
else {
extended[prop] = val;
}
}
// Otherwise, just use the one in obj1
else {
extended[prop] = val;
}
});
// Properties in obj2 that aren't in obj1
$each(obj2, function(prop,val) {
if(typeof obj1[prop] === "undefined") {
extended[prop] = val;
}
});
return extended;
}
};
JSONEditor.defaults = {
themes: {},
templates: {},
iconlibs: {},
editors: {},
languages: {},
resolvers: [],
custom_validators: []
};
JSONEditor.Validator = Class.extend({
init: function(jsoneditor,schema,options) {
this.jsoneditor = jsoneditor;
this.schema = schema || this.jsoneditor.schema;
this.options = options || {};
this.translate = this.jsoneditor.translate || JSONEditor.defaults.translate;
},
validate: function(value) {
return this._validateSchema(this.schema, value);
},
_validateSchema: function(schema,value,path) {
var self = this;
var errors = [];
var valid, i, j;
var stringified = JSON.stringify(value);
path = path || 'root';
// Work on a copy of the schema
schema = $extend({},this.jsoneditor.expandRefs(schema));
/*
* Type Agnostic Validation
*/
// Version 3 `required`
if(schema.required && schema.required === true) {
if(typeof value === "undefined") {
errors.push({
path: path,
property: 'required',
message: this.translate("edt_msg_error_notset")
});
// Can't do any more validation at this point
return errors;
}
}
// Value not defined
else if(typeof value === "undefined") {
// If required_by_default is set, all fields are required
if(this.jsoneditor.options.required_by_default) {
errors.push({
path: path,
property: 'required',
message: this.translate("edt_msg_error_notset")
});
}
// Not required, no further validation needed
else {
return errors;
}
}
// `enum`
if(schema["enum"]) {
valid = false;
for(i=0; i<schema["enum"].length; i++) {
if(stringified === JSON.stringify(schema["enum"][i])) valid = true;
}
if(!valid) {
errors.push({
path: path,
property: 'enum',
message: this.translate("edt_msg_error_enum")
});
}
}
// `extends` (version 3)
if(schema["extends"]) {
for(i=0; i<schema["extends"].length; i++) {
errors = errors.concat(this._validateSchema(schema["extends"][i],value,path));
}
}
// `allOf`
if(schema.allOf) {
for(i=0; i<schema.allOf.length; i++) {
errors = errors.concat(this._validateSchema(schema.allOf[i],value,path));
}
}
// `anyOf`
if(schema.anyOf) {
valid = false;
for(i=0; i<schema.anyOf.length; i++) {
if(!this._validateSchema(schema.anyOf[i],value,path).length) {
valid = true;
break;
}
}
if(!valid) {
errors.push({
path: path,
property: 'anyOf',
message: this.translate('edt_msg_error_anyOf')
});
}
}
// `oneOf`
if(schema.oneOf) {
valid = 0;
var oneof_errors = [];
for(i=0; i<schema.oneOf.length; i++) {
// Set the error paths to be path.oneOf[i].rest.of.path
var tmp = this._validateSchema(schema.oneOf[i],value,path);
if(!tmp.length) {
valid++;
}
for(j=0; j<tmp.length; j++) {
tmp[j].path = path+'.oneOf['+i+']'+tmp[j].path.substr(path.length);
}
oneof_errors = oneof_errors.concat(tmp);
}
if(valid !== 1) {
errors.push({
path: path,
property: 'oneOf',
message: this.translate('edt_msg_error_oneOf', [valid])
});
errors = errors.concat(oneof_errors);
}
}
// `not`
if(schema.not) {
if(!this._validateSchema(schema.not,value,path).length) {
errors.push({
path: path,
property: 'not',
message: this.translate('edt_msg_error_not')
});
}
}
// `type` (both Version 3 and Version 4 support)
if(schema.type) {
// Union type
if(Array.isArray(schema.type)) {
valid = false;
for(i=0;i<schema.type.length;i++) {
if(this._checkType(schema.type[i], value)) {
valid = true;
break;
}
}
if(!valid) {
errors.push({
path: path,
property: 'type',
message: this.translate('edt_msg_error_type_union')
});
}
}
// Simple type
else {
if(!this._checkType(schema.type, value)) {
errors.push({
path: path,
property: 'type',
message: this.translate('edt_msg_error_type', [schema.type])
});
}
}
}
// `disallow` (version 3)
if(schema.disallow) {
// Union type
if(Array.isArray(schema.disallow)) {
valid = true;
for(i=0;i<schema.disallow.length;i++) {
if(this._checkType(schema.disallow[i], value)) {
valid = false;
break;
}
}
if(!valid) {
errors.push({
path: path,
property: 'disallow',
message: this.translate('edt_msg_error_disallow_union')
});
}
}
// Simple type
else {
if(this._checkType(schema.disallow, value)) {
errors.push({
path: path,
property: 'disallow',
message: this.translate('edt_msg_error_disallow', [schema.disallow])
});
}
}
}
/*
* Type Specific Validation
*/
// Number Specific Validation
if(typeof value === "number") {
// `multipleOf` and `divisibleBy`
if(schema.multipleOf || schema.divisibleBy) {
var divisor = schema.multipleOf || schema.divisibleBy;
// Vanilla JS, prone to floating point rounding errors (e.g. 1.14 / .01 == 113.99999)
valid = (value/divisor === Math.floor(value/divisor));
// Use math.js is available
if(window.math) {
valid = window.math.mod(window.math.bignumber(value), window.math.bignumber(divisor)).equals(0);
}
// Use decimal.js is available
else if(window.Decimal) {
valid = (new window.Decimal(value)).mod(new window.Decimal(divisor)).equals(0);
}
if(!valid) {
errors.push({
path: path,
property: schema.multipleOf? 'multipleOf' : 'divisibleBy',
message: this.translate('edt_msg_error_multipleOf', [divisor])
});
}
}
// `maximum`
if(schema.hasOwnProperty('maximum')) {
// Vanilla JS, prone to floating point rounding errors (e.g. .999999999999999 == 1)
valid = schema.exclusiveMaximum? (value < schema.maximum) : (value <= schema.maximum);
// Use math.js is available
if(window.math) {
valid = window.math[schema.exclusiveMaximum?'smaller':'smallerEq'](
window.math.bignumber(value),
window.math.bignumber(schema.maximum)
);
}
// Use Decimal.js if available
else if(window.Decimal) {
valid = (new window.Decimal(value))[schema.exclusiveMaximum?'lt':'lte'](new window.Decimal(schema.maximum));
}
if(!valid) {
errors.push({
path: path,
property: 'maximum',
message: this.translate(
(schema.exclusiveMaximum?'edt_msg_error_maximum_excl':'edt_msg_error_maximum_incl'),
[schema.maximum]
)
});
}
}
// `minimum`
if(schema.hasOwnProperty('minimum')) {
// Vanilla JS, prone to floating point rounding errors (e.g. .999999999999999 == 1)
valid = schema.exclusiveMinimum? (value > schema.minimum) : (value >= schema.minimum);
// Use math.js is available
if(window.math) {
valid = window.math[schema.exclusiveMinimum?'larger':'largerEq'](
window.math.bignumber(value),
window.math.bignumber(schema.minimum)
);
}
// Use Decimal.js if available
else if(window.Decimal) {
valid = (new window.Decimal(value))[schema.exclusiveMinimum?'gt':'gte'](new window.Decimal(schema.minimum));
}
if(!valid) {
errors.push({
path: path,
property: 'minimum',
message: this.translate(
(schema.exclusiveMinimum?'edt_msg_error_minimum_excl':'edt_msg_error_minimum_incl'),
[schema.minimum]
)
});
}
}
}
// String specific validation
else if(typeof value === "string") {
// `maxLength`
if(schema.maxLength) {
if((value+"").length > schema.maxLength) {
errors.push({
path: path,
property: 'maxLength',
message: this.translate('edt_msg_error_maxLength', [schema.maxLength])
});
}
}
// `minLength`
if(schema.minLength) {
if((value+"").length < schema.minLength) {
errors.push({
path: path,
property: 'minLength',
message: this.translate((schema.minLength===1?'edt_msg_error_notempty':'edt_msg_error_minLength'), [schema.minLength])
});
}
}
// `pattern`
if(schema.pattern) {
if(!(new RegExp(schema.pattern)).test(value)) {
errors.push({
path: path,
property: 'pattern',
message: this.translate('edt_msg_error_pattern', [schema.pattern])
});
}
}
}
// Array specific validation
else if(typeof value === "object" && value !== null && Array.isArray(value)) {
// `items` and `additionalItems`
if(schema.items) {
// `items` is an array
if(Array.isArray(schema.items)) {
for(i=0; i<value.length; i++) {
// If this item has a specific schema tied to it
// Validate against it
if(schema.items[i]) {
errors = errors.concat(this._validateSchema(schema.items[i],value[i],path+'.'+i));
}
// If all additional items are allowed
else if(schema.additionalItems === true) {
break;
}
// If additional items is a schema
// TODO: Incompatibility between version 3 and 4 of the spec
else if(schema.additionalItems) {
errors = errors.concat(this._validateSchema(schema.additionalItems,value[i],path+'.'+i));
}
// If no additional items are allowed
else if(schema.additionalItems === false) {
errors.push({
path: path,
property: 'additionalItems',
message: this.translate('edt_msg_error_additionalItems')
});
break;
}
// Default for `additionalItems` is an empty schema
else {
break;
}
}
}
// `items` is a schema
else {
// Each item in the array must validate against the schema
for(i=0; i<value.length; i++) {
errors = errors.concat(this._validateSchema(schema.items,value[i],path+'.'+i));
}
}
}
// `maxItems`
if(schema.maxItems) {
if(value.length > schema.maxItems) {
errors.push({
path: path,
property: 'maxItems',
message: this.translate('edt_msg_error_maxItems', [schema.maxItems])
});
}
}
// `minItems`
if(schema.minItems) {
if(value.length < schema.minItems) {
errors.push({
path: path,
property: 'minItems',
message: this.translate('edt_msg_error_minItems', [schema.minItems])
});
}
}
// `uniqueItems`
if(schema.uniqueItems) {
var seen = {};
for(i=0; i<value.length; i++) {
valid = JSON.stringify(value[i]);
if(seen[valid]) {
errors.push({
path: path,
property: 'uniqueItems',
message: this.translate('edt_msg_error_uniqueItems')
});
break;
}
seen[valid] = true;
}
}
}
// Object specific validation
else if(typeof value === "object" && value !== null) {
// `maxProperties`
if(schema.maxProperties) {
valid = 0;
for(i in value) {
if(!value.hasOwnProperty(i)) continue;
valid++;
}
if(valid > schema.maxProperties) {
errors.push({
path: path,
property: 'maxProperties',
message: this.translate('edt_msg_error_maxProperties', [schema.maxProperties])
});
}
}
// `minProperties`
if(schema.minProperties) {
valid = 0;
for(i in value) {
if(!value.hasOwnProperty(i)) continue;
valid++;
}
if(valid < schema.minProperties) {
errors.push({
path: path,
property: 'minProperties',
message: this.translate('edt_msg_error_minProperties', [schema.minProperties])
});
}
}
// Version 4 `required`
if(schema.required && Array.isArray(schema.required)) {
for(i=0; i<schema.required.length; i++) {
if(typeof value[schema.required[i]] === "undefined") {
errors.push({
path: path,
property: 'required',
message: this.translate('edt_msg_error_required', [schema.required[i]])
});
}
}
}
// `properties`
var validated_properties = {};
if(schema.properties) {
for(i in schema.properties) {
if(!schema.properties.hasOwnProperty(i)) continue;
validated_properties[i] = true;
errors = errors.concat(this._validateSchema(schema.properties[i],value[i],path+'.'+i));
}
}
// `patternProperties`
if(schema.patternProperties) {
for(i in schema.patternProperties) {
if(!schema.patternProperties.hasOwnProperty(i)) continue;
var regex = new RegExp(i);
// Check which properties match
for(j in value) {
if(!value.hasOwnProperty(j)) continue;
if(regex.test(j)) {
validated_properties[j] = true;
errors = errors.concat(this._validateSchema(schema.patternProperties[i],value[j],path+'.'+j));
}
}
}
}
// The no_additional_properties option currently doesn't work with extended schemas that use oneOf or anyOf
if(typeof schema.additionalProperties === "undefined" && this.jsoneditor.options.no_additional_properties && !schema.oneOf && !schema.anyOf) {
schema.additionalProperties = false;
}
// `additionalProperties`
if(typeof schema.additionalProperties !== "undefined") {
for(i in value) {
if(!value.hasOwnProperty(i)) continue;
if(!validated_properties[i]) {
// No extra properties allowed
if(!schema.additionalProperties) {
errors.push({
path: path,
property: 'additionalProperties',
message: this.translate('edt_msg_error_additional_properties', [i])
});
break;
}
// Allowed
else if(schema.additionalProperties === true) {
break;
}
// Must match schema
// TODO: incompatibility between version 3 and 4 of the spec
else {
errors = errors.concat(this._validateSchema(schema.additionalProperties,value[i],path+'.'+i));
}
}
}
}
// `dependencies`
if(schema.dependencies) {
for(i in schema.dependencies) {
if(!schema.dependencies.hasOwnProperty(i)) continue;
// Doesn't need to meet the dependency
if(typeof value[i] === "undefined") continue;
// Property dependency
if(Array.isArray(schema.dependencies[i])) {
for(j=0; j<schema.dependencies[i].length; j++) {
if(typeof value[schema.dependencies[i][j]] === "undefined") {
errors.push({
path: path,
property: 'dependencies',
message: this.translate('edt_msg_error_dependency', [schema.dependencies[i][j]])
});
}
}
}
// Schema dependency
else {
errors = errors.concat(this._validateSchema(schema.dependencies[i],value,path));
}
}
}
}
// Custom type validation (global)
$each(JSONEditor.defaults.custom_validators,function(i,validator) {
errors = errors.concat(validator.call(self,schema,value,path));
});
// Custom type validation (instance specific)
if(this.options.custom_validators) {
$each(this.options.custom_validators,function(i,validator) {
errors = errors.concat(validator.call(self,schema,value,path));
});
}
return errors;
},
_checkType: function(type, value) {
// Simple types
if(typeof type === "string") {
if(type==="string") return typeof value === "string";
else if(type==="number") return typeof value === "number";
else if(type==="integer") return typeof value === "number" && value === Math.floor(value);
else if(type==="boolean") return typeof value === "boolean";
else if(type==="array") return Array.isArray(value);
else if(type === "object") return value !== null && !(Array.isArray(value)) && typeof value === "object";
else if(type === "null") return value === null;
else return true;
}
// Schema
else {
return !this._validateSchema(type,value).length;
}
}
});
/**
* All editors should extend from this class
*/
JSONEditor.AbstractEditor = Class.extend({
onChildEditorChange: function(editor) {
this.onChange(true);
},
notify: function() {
this.jsoneditor.notifyWatchers(this.path);
},
change: function() {
if(this.parent) this.parent.onChildEditorChange(this);
else this.jsoneditor.onChange();
},
onChange: function(bubble) {
this.notify();
if(this.watch_listener) this.watch_listener();
if(bubble) this.change();
},
register: function() {
this.jsoneditor.registerEditor(this);
this.onChange();
},
unregister: function() {
if(!this.jsoneditor) return;
this.jsoneditor.unregisterEditor(this);
},
getNumColumns: function() {
return 12;
},
init: function(options) {
this.jsoneditor = options.jsoneditor;
this.theme = this.jsoneditor.theme;
this.template_engine = this.jsoneditor.template;
this.iconlib = this.jsoneditor.iconlib;
this.access = this.jsoneditor.access;
this.translate = this.jsoneditor.translate || JSONEditor.defaults.translate;
this.original_schema = options.schema;
this.schema = this.jsoneditor.expandSchema(this.original_schema);
this.options = $extend({}, (this.options || {}), (options.schema.options || {}), options);
if(!options.path && !this.schema.id) this.schema.id = 'root';
this.path = options.path || 'root';
this.formname = options.formname || this.path.replace(/\.([^.]+)/g,'_$1');
if(this.jsoneditor.options.form_name_root) this.formname = this.formname.replace(/^root\[/,this.jsoneditor.options.form_name_root+'[');
this.key = this.path.split('.').pop();
this.parent = options.parent;
this.link_watchers = [];
if(options.container) this.setContainer(options.container);
this.registerDependencies();
},
registerDependencies: function() {
this.dependenciesFulfilled = true;
var deps = this.options.dependencies;
if (!deps) {
return;
}
var self = this;
Object.keys(deps).forEach(function(dependency) {
var path = self.path.split('.');
path[path.length - 1] = dependency;
path = path.join('.');
var choices = deps[dependency];
self.jsoneditor.watch(path, function() {
self.checkDependency(path, choices);
});
});
},
checkDependency: function(path, choices) {
var wrapper = this.control || this.container;
if (this.path === path || !wrapper) {
return;
}
var self = this;
var editor = this.jsoneditor.getEditor(path);
var value = editor ? editor.getValue() : undefined;
var previousStatus = this.dependenciesFulfilled;
this.dependenciesFulfilled = false;
if (!editor || !editor.dependenciesFulfilled) {
this.dependenciesFulfilled = false;
} else if (Array.isArray(choices)) {
choices.some(function(choice) {
if (value === choice) {
self.dependenciesFulfilled = true;
return true;
}
});
} else if (typeof choices === 'object') {
if (typeof value !== 'object') {
this.dependenciesFulfilled = choices === value;
} else {
Object.keys(choices).some(function(key) {
if (!choices.hasOwnProperty(key)) {
return false;
}
if (!value.hasOwnProperty(key) || choices[key] !== value[key]) {
self.dependenciesFulfilled = false;
return true;
}
self.dependenciesFulfilled = true;
});
}
} else if (typeof choices === 'string' || typeof choices === 'number') {
this.dependenciesFulfilled = value === choices;
} else if (typeof choices === 'boolean') {
if (choices) {
this.dependenciesFulfilled = value;
} else {
this.dependenciesFulfilled = !value;
}
}
if (this.dependenciesFulfilled !== previousStatus) {
this.notify();
}
if (this.dependenciesFulfilled) {
wrapper.style.display = 'block';
} else {
wrapper.style.display = 'none';
}
},
setContainer: function(container) {
this.container = container;
if(this.schema.id) this.container.setAttribute('data-schemaid',this.schema.id);
if(this.schema.type && typeof this.schema.type === "string") this.container.setAttribute('data-schematype',this.schema.type);
this.container.setAttribute('data-schemapath',this.path);
},
preBuild: function() {
},
build: function() {
},
postBuild: function() {
this.setupWatchListeners();
this.addLinks();
this.setValue(this.getDefault(), true);
this.updateHeaderText();
this.register();
this.onWatchedFieldChange();
//hide input fields, if they didn't match the current access level
var storedAccess = this.access
if(this.schema.access){
if(this.schema.access == 'system')
this.container.style.display = "none";
else if(this.schema.access == 'advanced' && storedAccess == 'default')
{
this.container.style.display = "none";
}
else if(this.schema.access == 'expert' && storedAccess != 'expert')
{
this.container.style.display = "none";
//this.disable();
}
}
},
setupWatchListeners: function() {
var self = this;
// Watched fields
this.watched = {};
if(this.schema.vars) this.schema.watch = this.schema.vars;
this.watched_values = {};
this.watch_listener = function() {
if(self.refreshWatchedFieldValues()) {
self.onWatchedFieldChange();
}
};
this.register();
if(this.schema.hasOwnProperty('watch')) {
var path,path_parts,first,root,adjusted_path;
for(var name in this.schema.watch) {
if(!this.schema.watch.hasOwnProperty(name)) continue;
path = this.schema.watch[name];
if(Array.isArray(path)) {
if(path.length<2) continue;
path_parts = [path[0]].concat(path[1].split('.'));
}
else {
path_parts = path.split('.');
if(!self.theme.closest(self.container,'[data-schemaid="'+path_parts[0]+'"]')) path_parts.unshift('#');
}
first = path_parts.shift();
if(first === '#') first = self.jsoneditor.schema.id || 'root';
// Find the root node for this template variable
root = self.theme.closest(self.container,'[data-schemaid="'+first+'"]');
if(!root) throw "Could not find ancestor node with id "+first;
// Keep track of the root node and path for use when rendering the template
adjusted_path = root.getAttribute('data-schemapath') + '.' + path_parts.join('.');
self.jsoneditor.watch(adjusted_path,self.watch_listener);
self.watched[name] = adjusted_path;
}
}
// Dynamic header
if(this.schema.headerTemplate) {
this.header_template = this.jsoneditor.compileTemplate(this.schema.headerTemplate, this.template_engine);
}
},
addLinks: function() {
// Add links
if(!this.no_link_holder) {
this.link_holder = this.theme.getLinksHolder();
this.container.appendChild(this.link_holder);
if(this.schema.links) {
for(var i=0; i<this.schema.links.length; i++) {
this.addLink(this.getLink(this.schema.links[i]));
}
}
}
},
getButton: function(text, icon, title) {
var btnClass = 'json-editor-btn-'+icon;
if(!this.iconlib) icon = null;
else icon = this.iconlib.getIcon(icon);
if(!icon && title) {
text = title;
title = null;
}
var btn = this.theme.getButton(text, icon, title);
btn.className += ' ' + btnClass + ' ';
return btn;
},
setButtonText: function(button, text, icon, title) {
if(!this.iconlib) icon = null;
else icon = this.iconlib.getIcon(icon);
if(!icon && title) {
text = title;
title = null;
}
return this.theme.setButtonText(button, text, icon, title);
},
addLink: function(link) {
if(this.link_holder) this.link_holder.appendChild(link);
},
getLink: function(data) {
var holder, link;
// Get mime type of the link
var mime = data.mediaType || 'application/javascript';
var type = mime.split('/')[0];
// Template to generate the link href
var href = this.jsoneditor.compileTemplate(data.href,this.template_engine);
// Template to generate the link's download attribute
var download = null;
if(data.download) download = data.download;
if(download && download !== true) {
download = this.jsoneditor.compileTemplate(download, this.template_engine);
}
// Image links
if(type === 'image') {
holder = this.theme.getBlockLinkHolder();
link = document.createElement('a');
link.setAttribute('target','_blank');
var image = document.createElement('img');
this.theme.createImageLink(holder,link,image);
// When a watched field changes, update the url
this.link_watchers.push(function(vars) {
var url = href(vars);
link.setAttribute('href',url);
link.setAttribute('title',data.rel || url);
image.setAttribute('src',url);
});
}
// Audio/Video links
else if(['audio','video'].indexOf(type) >=0) {
holder = this.theme.getBlockLinkHolder();
link = this.theme.getBlockLink();
link.setAttribute('target','_blank');
var media = document.createElement(type);
media.setAttribute('controls','controls');
this.theme.createMediaLink(holder,link,media);
// When a watched field changes, update the url
this.link_watchers.push(function(vars) {
var url = href(vars);
link.setAttribute('href',url);
link.textContent = data.rel || url;
media.setAttribute('src',url);
});
}
// Text links
else {
link = holder = this.theme.getBlockLink();
holder.setAttribute('target','_blank');
holder.textContent = data.rel;
// When a watched field changes, update the url
this.link_watchers.push(function(vars) {
var url = href(vars);
holder.setAttribute('href',url);
holder.textContent = data.rel || url;
});
}
if(download && link) {
if(download === true) {
link.setAttribute('download','');
}
else {
this.link_watchers.push(function(vars) {
link.setAttribute('download',download(vars));
});
}
}
if(data.class) link.className = link.className + ' ' + data.class;
return holder;
},
refreshWatchedFieldValues: function() {
if(!this.watched_values) return;
var watched = {};
var changed = false;
var self = this;
if(this.watched) {
var val,editor;
for(var name in this.watched) {
if(!this.watched.hasOwnProperty(name)) continue;
editor = self.jsoneditor.getEditor(this.watched[name]);
val = editor? editor.getValue() : null;
if(self.watched_values[name] !== val) changed = true;
watched[name] = val;
}
}
watched.self = this.getValue();
if(this.watched_values.self !== watched.self) changed = true;
this.watched_values = watched;
return changed;
},
getWatchedFieldValues: function() {
return this.watched_values;
},
updateHeaderText: function() {
if(this.header) {
// If the header has children, only update the text node's value
if(this.header.children.length) {
for(var i=0; i<this.header.childNodes.length; i++) {
if(this.header.childNodes[i].nodeType===3) {
this.header.childNodes[i].nodeValue = this.getHeaderText();
break;
}
}
}
// Otherwise, just update the entire node
else {
this.header.textContent = this.getHeaderText();
}
}
},
getHeaderText: function(title_only) {
if(this.header_text) return this.header_text;
else if(title_only) return this.schema.title;
else return this.getTitle();
},
onWatchedFieldChange: function() {
var vars;
if(this.header_template) {
vars = $extend(this.getWatchedFieldValues(),{
key: this.key,
i: this.key,
i0: (this.key*1),
i1: (this.key*1+1),
title: this.getTitle()
});
var header_text = this.header_template(vars);
if(header_text !== this.header_text) {
this.header_text = header_text;
this.updateHeaderText();
this.notify();
//this.fireChangeHeaderEvent();
}
}
if(this.link_watchers.length) {
vars = this.getWatchedFieldValues();
for(var i=0; i<this.link_watchers.length; i++) {
this.link_watchers[i](vars);
}
}
},
setValue: function(value) {
this.value = value;
},
getValue: function() {
return this.value;
},
refreshValue: function() {
},
getChildEditors: function() {
return false;
},
destroy: function() {
var self = this;
this.unregister(this);
$each(this.watched,function(name,adjusted_path) {
self.jsoneditor.unwatch(adjusted_path,self.watch_listener);
});
this.watched = null;
this.watched_values = null;
this.watch_listener = null;
this.header_text = null;
this.header_template = null;
this.value = null;
if(this.container && this.container.parentNode) this.container.parentNode.removeChild(this.container);
this.container = null;
this.jsoneditor = null;
this.schema = null;
this.path = null;
this.key = null;
this.parent = null;
},
getDefault: function() {
if(this.schema["default"]) return this.schema["default"];
if(this.schema["enum"]) return this.schema["enum"][0];
var type = this.schema.type || this.schema.oneOf;
if(type && Array.isArray(type)) type = type[0];
if(type && typeof type === "object") type = type.type;
if(type && Array.isArray(type)) type = type[0];
if(typeof type === "string") {
if(type === "number") return 0.0;
if(type === "boolean") return false;
if(type === "integer") return 0;
if(type === "string") return "";
if(type === "object") return {};
if(type === "array") return [];
}
return null;
},
getTitle: function() {
if (this.schema.title == null)
return this.key;
else
return $.i18n(this.schema.title);
},
getAppend: function() {
return $.i18n(this.schema.append);
},
enable: function() {
this.disabled = false;
},
disable: function() {
this.disabled = true;
},
isEnabled: function() {
return !this.disabled;
},
isRequired: function() {
if(typeof this.schema.required === "boolean") return this.schema.required;
else if(this.parent && this.parent.schema && Array.isArray(this.parent.schema.required)) return this.parent.schema.required.indexOf(this.key) > -1;
else if(this.jsoneditor.options.required_by_default) return true;
else return true;
},
getDisplayText: function(arr) {
var disp = [];
var used = {};
// Determine how many times each attribute name is used.
// This helps us pick the most distinct display text for the schemas.
$each(arr,function(i,el) {
if(el.title) {
used[el.title] = used[el.title] || 0;
used[el.title]++;
}
if(el.description) {
used[el.description] = used[el.description] || 0;
used[el.description]++;
}
if(el.format) {
used[el.format] = used[el.format] || 0;
used[el.format]++;
}
if(el.type) {
used[el.type] = used[el.type] || 0;
used[el.type]++;
}
});
// Determine display text for each element of the array
$each(arr,function(i,el) {
var name;
// If it's a simple string
if(typeof el === "string") name = el;
// Object
else if(el.title && used[el.title]<=1) name = el.title;
else if(el.format && used[el.format]<=1) name = el.format;
else if(el.type && used[el.type]<=1) name = el.type;
else if(el.description && used[el.description]<=1) name = el.descripton;
else if(el.title) name = el.title;
else if(el.format) name = el.format;
else if(el.type) name = el.type;
else if(el.description) name = el.description;
else if(JSON.stringify(el).length < 50) name = JSON.stringify(el);
else name = "type";
disp.push(name);
});
// Replace identical display text with "text 1", "text 2", etc.
var inc = {};
$each(disp,function(i,name) {
inc[name] = inc[name] || 0;
inc[name]++;
if(used[name] > 1) disp[i] = name + " " + inc[name];
});
return disp;
},
getOption: function(key) {
try {
throw "getOption is deprecated";
}
catch(e) {
window.console.error(e);
}
return this.options[key];
},
showValidationErrors: function(errors) {
}
});
JSONEditor.defaults.editors["null"] = JSONEditor.AbstractEditor.extend({
getValue: function() {
return null;
},
setValue: function() {
this.onChange();
},
getNumColumns: function() {
return 2;
}
});
JSONEditor.defaults.editors.string = JSONEditor.AbstractEditor.extend({
register: function() {
this._super();
if(!this.input) return;
this.input.setAttribute('id',this.formname);
},
unregister: function() {
this._super();
if(!this.input) return;
this.input.removeAttribute('id');
},
setValue: function(value,initial,from_template) {
var self = this;
if(this.template && !from_template) {
return;
}
if(value === null || typeof value === 'undefined') value = "";
else if(typeof value === "object") value = JSON.stringify(value);
else if(typeof value !== "string") value = ""+value;
if(value === this.serialized) return;
// Sanitize value before setting it
var sanitized = this.sanitize(value);
if(this.input.value === sanitized) {
return;
}
this.input.value = sanitized;
// If using SCEditor, update the WYSIWYG
if(this.sceditor_instance) {
this.sceditor_instance.val(sanitized);
}
else if(this.epiceditor) {
this.epiceditor.importFile(null,sanitized);
}
else if(this.ace_editor) {
this.ace_editor.setValue(sanitized);
}
var changed = from_template || this.getValue() !== value;
this.refreshValue();
if(initial) this.is_dirty = false;
else if(this.jsoneditor.options.show_errors === "change") this.is_dirty = true;
if(this.adjust_height) this.adjust_height(this.input);
// Bubble this setValue to parents if the value changed
this.onChange(changed);
},
getNumColumns: function() {
var min = Math.ceil(Math.max(this.getTitle().length,this.schema.maxLength||0,this.schema.minLength||0)/5);
var num;
if(this.input_type === 'textarea') num = 6;
else if(['text','email'].indexOf(this.input_type) >= 0) num = 4;
else num = 2;
return Math.min(12,Math.max(min,num));
},
build: function() {
var self = this, i;
if(!this.options.compact) this.header = this.label = this.theme.getFormInputLabel(this.getTitle());
if(this.schema.description) this.description = this.theme.getFormInputDescription(this.schema.description);
if(this.schema.append) this.append = this.theme.getFormInputAppend(this.getAppend());
this.placeholder = this.schema.default;
this.format = this.schema.format;
if(!this.format && this.schema.media && this.schema.media.type) {
this.format = this.schema.media.type.replace(/(^(application|text)\/(x-)?(script\.)?)|(-source$)/g,'');
}
if(!this.format && this.options.default_format) {
this.format = this.options.default_format;
}
if(this.options.format) {
this.format = this.options.format;
}
// Specific format
if(this.format) {
// Text Area
if(this.format === 'textarea') {
this.input_type = 'textarea';
this.input = this.theme.getTextareaInput();
}
// Range Input
else if(this.format === 'range') {
this.input_type = 'range';
var min = this.schema.minimum || 0;
var max = this.schema.maximum || Math.max(100,min+1);
var step = 1;
if(this.schema.multipleOf) {
if(min%this.schema.multipleOf) min = Math.ceil(min/this.schema.multipleOf)*this.schema.multipleOf;
if(max%this.schema.multipleOf) max = Math.floor(max/this.schema.multipleOf)*this.schema.multipleOf;
step = this.schema.multipleOf;
}
this.input = this.theme.getRangeInput(min,max,step);
}
// Source Code
else if([
'actionscript',
'batchfile',
'bbcode',
'c',
'c++',
'cpp',
'coffee',
'csharp',
'css',
'dart',
'django',
'ejs',
'erlang',
'golang',
'groovy',
'handlebars',
'haskell',
'haxe',
'html',
'ini',
'jade',
'java',
'javascript',
'json',
'less',
'lisp',
'lua',
'makefile',
'markdown',
'matlab',
'mysql',
'objectivec',
'pascal',
'perl',
'pgsql',
'php',
'python',
'r',
'ruby',
'sass',
'scala',
'scss',
'smarty',
'sql',
'stylus',
'svg',
'twig',
'vbscript',
'xml',
'yaml'
].indexOf(this.format) >= 0
) {
this.input_type = this.format;
this.source_code = true;
this.input = this.theme.getTextareaInput();
}
// HTML5 Input type
else {
this.input_type = this.format;
this.input = this.theme.getFormInputField(this.input_type);
}
}
// Number or integer adds html5 tag 'number'
else if (this.schema.type == "number" || this.schema.type == "integer"){
var min = this.schema.minimum
var max = this.schema.maximum
var step = this.schema.step
this.input = this.theme.getRangeInput(min,max,step);
}
// Normal text input
else {
this.input_type = 'text';
this.input = this.theme.getFormInputField(this.input_type);
}
// minLength, maxLength, and pattern
if(typeof this.schema.maxLength !== "undefined") this.input.setAttribute('maxlength',this.schema.maxLength);
if(typeof this.schema.pattern !== "undefined") this.input.setAttribute('pattern',this.schema.pattern);
else if(typeof this.schema.minLength !== "undefined") this.input.setAttribute('pattern','.{'+this.schema.minLength+',}');
if(this.options.compact) {
this.container.className += ' compact';
}
else {
if(this.options.input_width) this.input.style.width = this.options.input_width;
}
if(this.schema.readOnly || this.schema.readonly || this.schema.template) {
this.always_disabled = true;
this.input.disabled = true;
}
this.input
.addEventListener('change',function(e) {
e.preventDefault();
e.stopPropagation();
// Don't allow changing if this field is a template
if(self.schema.template) {
this.value = self.value;
return;
}
var val = this.value;
// sanitize value
var sanitized = self.sanitize(val);
if(val !== sanitized) {
this.value = sanitized;
}
self.is_dirty = true;
self.refreshValue();
self.onChange(true);
});
if(this.options.input_height) this.input.style.height = this.options.input_height;
if(this.options.expand_height) {
this.adjust_height = function(el) {
if(!el) return;
var i, ch=el.offsetHeight;
// Input too short
if(el.offsetHeight < el.scrollHeight) {
i=0;
while(el.offsetHeight < el.scrollHeight+3) {
if(i>100) break;
i++;
ch++;
el.style.height = ch+'px';
}
}
else {
i=0;
while(el.offsetHeight >= el.scrollHeight+3) {
if(i>100) break;
i++;
ch--;
el.style.height = ch+'px';
}
el.style.height = (ch+1)+'px';
}
};
this.input.addEventListener('keyup',function(e) {
self.adjust_height(this);
});
this.input.addEventListener('change',function(e) {
self.adjust_height(this);
});
this.adjust_height();
}
if(this.format) this.input.setAttribute('data-schemaformat',this.format);
if(this.defaultValue) this.input.setAttribute('data-schemaformat',this.format);
if(this.formname && this.label)this.label.setAttribute('for',this.formname);
this.control = this.theme.getFormControl(this.label, this.input, this.description, this.append, this.placeholder);
this.container.appendChild(this.control);
// Any special formatting that needs to happen after the input is added to the dom
window.requestAnimationFrame(function() {
// Skip in case the input is only a temporary editor,
// otherwise, in the case of an ace_editor creation,
// it will generate an error trying to append it to the missing parentNode
if(self.input.parentNode) self.afterInputReady();
if(self.adjust_height) self.adjust_height(self.input);
});
// Compile and store the template
if(this.schema.template) {
this.template = this.jsoneditor.compileTemplate(this.schema.template, this.template_engine);
this.refreshValue();
}
else {
this.refreshValue();
}
},
enable: function() {
if(!this.always_disabled) {
this.input.disabled = false;
// TODO: WYSIWYG and Markdown editors
}
this._super();
},
disable: function() {
this.input.disabled = true;
// TODO: WYSIWYG and Markdown editors
this._super();
},
afterInputReady: function() {
var self = this, options;
// Code editor
if(this.source_code) {
// WYSIWYG html and bbcode editor
if(this.options.wysiwyg &&
['html','bbcode'].indexOf(this.input_type) >= 0 &&
window.jQuery && window.jQuery.fn && window.jQuery.fn.sceditor
) {
options = $extend({},{
plugins: self.input_type==='html'? 'xhtml' : 'bbcode',
emoticonsEnabled: false,
width: '100%',
height: 300
},JSONEditor.plugins.sceditor,self.options.sceditor_options||{});
window.jQuery(self.input).sceditor(options);
self.sceditor_instance = window.jQuery(self.input).sceditor('instance');
self.sceditor_instance.blur(function() {
// Get editor's value
var val = window.jQuery("<div>"+self.sceditor_instance.val()+"</div>");
// Remove sceditor spans/divs
window.jQuery('#sceditor-start-marker,#sceditor-end-marker,.sceditor-nlf',val).remove();
// Set the value and update
self.input.value = val.html();
self.value = self.input.value;
self.is_dirty = true;
self.onChange(true);
});
}
// EpicEditor for markdown (if it's loaded)
else if (this.input_type === 'markdown' && window.EpicEditor) {
this.epiceditor_container = document.createElement('div');
this.input.parentNode.insertBefore(this.epiceditor_container,this.input);
this.input.style.display = 'none';
options = $extend({},JSONEditor.plugins.epiceditor,{
container: this.epiceditor_container,
clientSideStorage: false
});
this.epiceditor = new window.EpicEditor(options).load();
this.epiceditor.importFile(null,this.getValue());
this.epiceditor.on('update',function() {
var val = self.epiceditor.exportFile();
self.input.value = val;
self.value = val;
self.is_dirty = true;
self.onChange(true);
});
}
// ACE editor for everything else
else if(window.ace) {
var mode = this.input_type;
// aliases for c/cpp
if(mode === 'cpp' || mode === 'c++' || mode === 'c') {
mode = 'c_cpp';
}
this.ace_container = document.createElement('div');
this.ace_container.style.width = '100%';
this.ace_container.style.position = 'relative';
this.ace_container.style.height = '400px';
this.input.parentNode.insertBefore(this.ace_container,this.input);
this.input.style.display = 'none';
this.ace_editor = window.ace.edit(this.ace_container);
this.ace_editor.setValue(this.getValue());
// The theme
if(JSONEditor.plugins.ace.theme) this.ace_editor.setTheme('ace/theme/'+JSONEditor.plugins.ace.theme);
// The mode
mode = window.ace.require("ace/mode/"+mode);
if(mode) this.ace_editor.getSession().setMode(new mode.Mode());
// Listen for changes
this.ace_editor.on('change',function() {
var val = self.ace_editor.getValue();
self.input.value = val;
self.refreshValue();
self.is_dirty = true;
self.onChange(true);
});
}
}
self.theme.afterInputReady(self.input);
},
refreshValue: function() {
this.value = this.input.value;
if(typeof this.value !== "string") this.value = '';
this.serialized = this.value;
},
destroy: function() {
// If using SCEditor, destroy the editor instance
if(this.sceditor_instance) {
this.sceditor_instance.destroy();
}
else if(this.epiceditor) {
this.epiceditor.unload();
}
else if(this.ace_editor) {
this.ace_editor.destroy();
}
this.template = null;
if(this.input && this.input.parentNode) this.input.parentNode.removeChild(this.input);
if(this.label && this.label.parentNode) this.label.parentNode.removeChild(this.label);
if(this.description && this.description.parentNode) this.description.parentNode.removeChild(this.description);
this._super();
},
/**
* This is overridden in derivative editors
*/
sanitize: function(value) {
return value;
},
/**
* Re-calculates the value if needed
*/
onWatchedFieldChange: function() {
var self = this, vars, j;
// If this editor needs to be rendered by a macro template
if(this.template) {
vars = this.getWatchedFieldValues();
this.setValue(this.template(vars),false,true);
}
this._super();
},
showValidationErrors: function(errors) {
var self = this;
if(this.jsoneditor.options.show_errors === "always") {}
else if(!this.is_dirty && this.previous_error_setting===this.jsoneditor.options.show_errors) return;
this.previous_error_setting = this.jsoneditor.options.show_errors;
var messages = [];
$each(errors,function(i,error) {
if(error.path === self.path) {
messages.push(error.message);
}
});
if(messages.length) {
this.theme.addInputError(this.input, messages.join('. ')+'.');
}
else {
this.theme.removeInputError(this.input);
}
}
});
JSONEditor.defaults.editors.number = JSONEditor.defaults.editors.string.extend({
sanitize: function(value) {
return (value+"").replace(/[^0-9\.\-eE]/g,'');
},
getNumColumns: function() {
return 2;
},
getValue: function() {
return this.value*1;
}
});
JSONEditor.defaults.editors.integer = JSONEditor.defaults.editors.number.extend({
sanitize: function(value) {
value = value + "";
return value.replace(/[^0-9\-]/g,'');
},
getNumColumns: function() {
return 2;
}
});
JSONEditor.defaults.editors.object = JSONEditor.AbstractEditor.extend({
getDefault: function() {
return $extend({},this.schema["default"] || {});
},
getChildEditors: function() {
return this.editors;
},
register: function() {
this._super();
if(this.editors) {
for(var i in this.editors) {
if(!this.editors.hasOwnProperty(i)) continue;
this.editors[i].register();
}
}
},
unregister: function() {
this._super();
if(this.editors) {
for(var i in this.editors) {
if(!this.editors.hasOwnProperty(i)) continue;
this.editors[i].unregister();
}
}
},
getNumColumns: function() {
return Math.max(Math.min(12,this.maxwidth),3);
},
enable: function() {
if(this.editjson_button) this.editjson_button.disabled = false;
if(this.addproperty_button) this.addproperty_button.disabled = false;
this._super();
if(this.editors) {
for(var i in this.editors) {
if(!this.editors.hasOwnProperty(i)) continue;
this.editors[i].enable();
}
}
},
disable: function() {
if(this.editjson_button) this.editjson_button.disabled = true;
if(this.addproperty_button) this.addproperty_button.disabled = true;
this.hideEditJSON();
this._super();
if(this.editors) {
for(var i in this.editors) {
if(!this.editors.hasOwnProperty(i)) continue;
this.editors[i].disable();
}
}
},
layoutEditors: function() {
var self = this, i, j;
if(!this.row_container) return;
// Sort editors by propertyOrder
this.property_order = Object.keys(this.editors);
this.property_order = this.property_order.sort(function(a,b) {
var ordera = self.editors[a].schema.propertyOrder;
var orderb = self.editors[b].schema.propertyOrder;
if(typeof ordera !== "number") ordera = 1000;
if(typeof orderb !== "number") orderb = 1000;
return ordera - orderb;
});
var container;
if(this.format === 'grid') {
var rows = [];
$each(this.property_order, function(j,key) {
var editor = self.editors[key];
if(editor.property_removed) return;
var found = false;
var width = editor.options.hidden? 0 : (editor.options.grid_columns || editor.getNumColumns());
var height = editor.options.hidden? 0 : editor.container.offsetHeight;
// See if the editor will fit in any of the existing rows first
for(var i=0; i<rows.length; i++) {
// If the editor will fit in the row horizontally
if(rows[i].width + width <= 12) {
// If the editor is close to the other elements in height
// i.e. Don't put a really tall editor in an otherwise short row or vice versa
if(!height || (rows[i].minh*0.5 < height && rows[i].maxh*2 > height)) {
found = i;
}
}
}
// If there isn't a spot in any of the existing rows, start a new row
if(found === false) {
rows.push({
width: 0,
minh: 999999,
maxh: 0,
editors: []
});
found = rows.length-1;
}
rows[found].editors.push({
key: key,
//editor: editor,
width: width,
height: height
});
rows[found].width += width;
rows[found].minh = Math.min(rows[found].minh,height);
rows[found].maxh = Math.max(rows[found].maxh,height);
});
// Make almost full rows width 12
// Do this by increasing all editors' sizes proprotionately
// Any left over space goes to the biggest editor
// Don't touch rows with a width of 6 or less
for(i=0; i<rows.length; i++) {
if(rows[i].width < 12) {
var biggest = false;
var new_width = 0;
for(j=0; j<rows[i].editors.length; j++) {
if(biggest === false) biggest = j;
else if(rows[i].editors[j].width > rows[i].editors[biggest].width) biggest = j;
rows[i].editors[j].width *= 12/rows[i].width;
rows[i].editors[j].width = Math.floor(rows[i].editors[j].width);
new_width += rows[i].editors[j].width;
}
if(new_width < 12) rows[i].editors[biggest].width += 12-new_width;
rows[i].width = 12;
}
}
// layout hasn't changed
if(this.layout === JSON.stringify(rows)) return false;
this.layout = JSON.stringify(rows);
// Layout the form
container = document.createElement('div');
for(i=0; i<rows.length; i++) {
var row = this.theme.getGridRow();
container.appendChild(row);
for(j=0; j<rows[i].editors.length; j++) {
var key = rows[i].editors[j].key;
var editor = this.editors[key];
if(editor.options.hidden) editor.container.style.display = 'none';
else this.theme.setGridColumnSize(editor.container,rows[i].editors[j].width);
row.appendChild(editor.container);
}
}
}
// Normal layout
else {
container = document.createElement('div');
$each(this.property_order, function(i,key) {
var editor = self.editors[key];
if(editor.property_removed) return;
var row = self.theme.getGridRow();
container.appendChild(row);
if(editor.options.hidden) editor.container.style.display = 'none';
else self.theme.setGridColumnSize(editor.container,12);
row.appendChild(editor.container);
});
}
this.row_container.innerHTML = '';
this.row_container.appendChild(container);
},
getPropertySchema: function(key) {
// Schema declared directly in properties
var schema = this.schema.properties[key] || {};
schema = $extend({},schema);
var matched = this.schema.properties[key]? true : false;
// Any matching patternProperties should be merged in
if(this.schema.patternProperties) {
for(var i in this.schema.patternProperties) {
if(!this.schema.patternProperties.hasOwnProperty(i)) continue;
var regex = new RegExp(i);
if(regex.test(key)) {
schema.allOf = schema.allOf || [];
schema.allOf.push(this.schema.patternProperties[i]);
matched = true;
}
}
}
// Hasn't matched other rules, use additionalProperties schema
if(!matched && this.schema.additionalProperties && typeof this.schema.additionalProperties === "object") {
schema = $extend({},this.schema.additionalProperties);
}
return schema;
},
preBuild: function() {
this._super();
this.editors = {};
this.cached_editors = {};
var self = this;
this.format = this.options.layout || this.options.object_layout || this.schema.format || this.jsoneditor.options.object_layout || 'normal';
this.schema.properties = this.schema.properties || {};
this.minwidth = 0;
this.maxwidth = 0;
// If the object should be rendered as a table row
if(this.options.table_row) {
$each(this.schema.properties, function(key,schema) {
var editor = self.jsoneditor.getEditorClass(schema);
self.editors[key] = self.jsoneditor.createEditor(editor,{
jsoneditor: self.jsoneditor,
schema: schema,
path: self.path+'.'+key,
parent: self,
compact: true,
required: true
});
self.editors[key].preBuild();
var width = self.editors[key].options.hidden? 0 : (self.editors[key].options.grid_columns || self.editors[key].getNumColumns());
self.minwidth += width;
self.maxwidth += width;
});
this.no_link_holder = true;
}
// If the object should be rendered as a table
else if(this.options.table) {
// TODO: table display format
throw "Not supported yet";
}
// If the object should be rendered as a div
else {
if(!this.schema.defaultProperties) {
if(this.jsoneditor.options.display_required_only || this.options.display_required_only) {
this.schema.defaultProperties = [];
$each(this.schema.properties, function(k,s) {
if(self.isRequired({key: k, schema: s})) {
self.schema.defaultProperties.push(k);
}
});
}
else {
self.schema.defaultProperties = Object.keys(self.schema.properties);
}
}
// Increase the grid width to account for padding
self.maxwidth += 1;
$each(this.schema.defaultProperties, function(i,key) {
self.addObjectProperty(key, true);
if(self.editors[key]) {
self.minwidth = Math.max(self.minwidth,(self.editors[key].options.grid_columns || self.editors[key].getNumColumns()));
self.maxwidth += (self.editors[key].options.grid_columns || self.editors[key].getNumColumns());
}
});
}
// Sort editors by propertyOrder
this.property_order = Object.keys(this.editors);
this.property_order = this.property_order.sort(function(a,b) {
var ordera = self.editors[a].schema.propertyOrder;
var orderb = self.editors[b].schema.propertyOrder;
if(typeof ordera !== "number") ordera = 1000;
if(typeof orderb !== "number") orderb = 1000;
return ordera - orderb;
});
},
build: function() {
var self = this;
// If the object should be rendered as a table row
if(this.options.table_row) {
this.editor_holder = this.container;
$each(this.editors, function(key,editor) {
var holder = self.theme.getTableCell();
self.editor_holder.appendChild(holder);
editor.setContainer(holder);
editor.build();
editor.postBuild();
if(self.editors[key].options.hidden) {
holder.style.display = 'none';
}
if(self.editors[key].options.input_width) {
holder.style.width = self.editors[key].options.input_width;
}
});
}
// If the object should be rendered as a table
else if(this.options.table) {
// TODO: table display format
throw "Not supported yet";
}
// If the object should be rendered as a div
else {
this.header = document.createElement('span');
this.header.textContent = this.getTitle();
this.title = this.theme.getHeader(this.header);
this.container.appendChild(this.title);
this.container.style.position = 'relative';
// Edit JSON modal
this.editjson_holder = this.theme.getModal();
this.editjson_textarea = this.theme.getTextareaInput();
this.editjson_textarea.style.height = '170px';
this.editjson_textarea.style.width = '300px';
this.editjson_textarea.style.display = 'block';
this.editjson_save = this.getButton('Save','save','Save');
this.editjson_save.addEventListener('click',function(e) {
e.preventDefault();
e.stopPropagation();
self.saveJSON();
});
this.editjson_cancel = this.getButton('Cancel','cancel','Cancel');
this.editjson_cancel.addEventListener('click',function(e) {
e.preventDefault();
e.stopPropagation();
self.hideEditJSON();
});
this.editjson_holder.appendChild(this.editjson_textarea);
this.editjson_holder.appendChild(this.editjson_save);
this.editjson_holder.appendChild(this.editjson_cancel);
// Manage Properties modal
this.addproperty_holder = this.theme.getModal();
this.addproperty_list = document.createElement('div');
this.addproperty_list.style.width = '295px';
this.addproperty_list.style.maxHeight = '160px';
this.addproperty_list.style.padding = '5px 0';
this.addproperty_list.style.overflowY = 'auto';
this.addproperty_list.style.overflowX = 'hidden';
this.addproperty_list.style.paddingLeft = '5px';
this.addproperty_list.setAttribute('class', 'property-selector');
this.addproperty_add = this.getButton('add','add','add');
this.addproperty_input = this.theme.getFormInputField('text');
this.addproperty_input.setAttribute('placeholder','Property name...');
this.addproperty_input.style.width = '220px';
this.addproperty_input.style.marginBottom = '0';
this.addproperty_input.style.display = 'inline-block';
this.addproperty_add.addEventListener('click',function(e) {
e.preventDefault();
e.stopPropagation();
if(self.addproperty_input.value) {
if(self.editors[self.addproperty_input.value]) {
window.alert('there is already a property with that name');
return;
}
self.addObjectProperty(self.addproperty_input.value);
if(self.editors[self.addproperty_input.value]) {
self.editors[self.addproperty_input.value].disable();
}
self.onChange(true);
}
});
this.addproperty_holder.appendChild(this.addproperty_list);
this.addproperty_holder.appendChild(this.addproperty_input);
this.addproperty_holder.appendChild(this.addproperty_add);
var spacer = document.createElement('div');
spacer.style.clear = 'both';
this.addproperty_holder.appendChild(spacer);
// Description
if(this.schema.description) {
this.description = this.theme.getDescription(this.schema.description);
this.container.appendChild(this.description);
}
// Validation error placeholder area
this.error_holder = document.createElement('div');
this.container.appendChild(this.error_holder);
// Container for child editor area
this.editor_holder = this.theme.getIndentedPanel();
this.container.appendChild(this.editor_holder);
// Container for rows of child editors
this.row_container = this.theme.getGridContainer();
this.editor_holder.appendChild(this.row_container);
$each(this.editors, function(key,editor) {
var holder = self.theme.getGridColumn();
self.row_container.appendChild(holder);
editor.setContainer(holder);
editor.build();
editor.postBuild();
});
// Control buttons
this.title_controls = this.theme.getHeaderButtonHolder();
this.editjson_controls = this.theme.getHeaderButtonHolder();
this.addproperty_controls = this.theme.getHeaderButtonHolder();
this.title.appendChild(this.title_controls);
this.title.appendChild(this.editjson_controls);
this.title.appendChild(this.addproperty_controls);
// Show/Hide button
this.collapsed = false;
this.toggle_button = this.getButton('','collapse',this.translate('edt_msg_button_collapse'));
this.title_controls.appendChild(this.toggle_button);
this.toggle_button.addEventListener('click',function(e) {
e.preventDefault();
e.stopPropagation();
if(self.collapsed) {
self.editor_holder.style.display = '';
self.collapsed = false;
self.setButtonText(self.toggle_button,'','collapse',self.translate('edt_msg_button_collapse'));
}
else {
self.editor_holder.style.display = 'none';
self.collapsed = true;
self.setButtonText(self.toggle_button,'','expand',self.translate('edt_msg_button_expand'));
}
});
// If it should start collapsed
if(this.options.collapsed) {
$trigger(this.toggle_button,'click');
}
// Collapse button disabled
if(this.schema.options && typeof this.schema.options.disable_collapse !== "undefined") {
if(this.schema.options.disable_collapse) this.toggle_button.style.display = 'none';
}
else if(this.jsoneditor.options.disable_collapse) {
this.toggle_button.style.display = 'none';
}
// Edit JSON Button
this.editjson_button = this.getButton('JSON','edit','Edit JSON');
this.editjson_button.addEventListener('click',function(e) {
e.preventDefault();
e.stopPropagation();
self.toggleEditJSON();
});
this.editjson_controls.appendChild(this.editjson_button);
this.editjson_controls.appendChild(this.editjson_holder);
// Edit JSON Buttton disabled
if(this.schema.options && typeof this.schema.options.disable_edit_json !== "undefined") {
if(this.schema.options.disable_edit_json) this.editjson_button.style.display = 'none';
}
else if(this.jsoneditor.options.disable_edit_json) {
this.editjson_button.style.display = 'none';
}
// Object Properties Button
this.addproperty_button = this.getButton('Properties','edit','Object Properties');
this.addproperty_button.addEventListener('click',function(e) {
e.preventDefault();
e.stopPropagation();
self.toggleAddProperty();
});
this.addproperty_controls.appendChild(this.addproperty_button);
this.addproperty_controls.appendChild(this.addproperty_holder);
this.refreshAddProperties();
}
// Fix table cell ordering
if(this.options.table_row) {
this.editor_holder = this.container;
$each(this.property_order,function(i,key) {
self.editor_holder.appendChild(self.editors[key].container);
});
}
// Layout object editors in grid if needed
else {
// Initial layout
this.layoutEditors();
// Do it again now that we know the approximate heights of elements
this.layoutEditors();
}
},
showEditJSON: function() {
if(!this.editjson_holder) return;
this.hideAddProperty();
// Position the form directly beneath the button
// TODO: edge detection
this.editjson_holder.style.left = this.editjson_button.offsetLeft+"px";
this.editjson_holder.style.top = this.editjson_button.offsetTop + this.editjson_button.offsetHeight+"px";
// Start the textarea with the current value
this.editjson_textarea.value = JSON.stringify(this.getValue(),null,2);
// Disable the rest of the form while editing JSON
this.disable();
this.editjson_holder.style.display = '';
this.editjson_button.disabled = false;
this.editing_json = true;
},
hideEditJSON: function() {
if(!this.editjson_holder) return;
if(!this.editing_json) return;
this.editjson_holder.style.display = 'none';
this.enable();
this.editing_json = false;
},
saveJSON: function() {
if(!this.editjson_holder) return;
try {
var json = JSON.parse(this.editjson_textarea.value);
this.setValue(json);
this.hideEditJSON();
}
catch(e) {
window.alert('invalid JSON');
throw e;
}
},
toggleEditJSON: function() {
if(this.editing_json) this.hideEditJSON();
else this.showEditJSON();
},
insertPropertyControlUsingPropertyOrder: function (property, control, container) {
var propertyOrder;
if (this.schema.properties[property])
propertyOrder = this.schema.properties[property].propertyOrder;
if (typeof propertyOrder !== "number") propertyOrder = 1000;
control.propertyOrder = propertyOrder;
for (var i = 0; i < container.childNodes.length; i++) {
var child = container.childNodes[i];
if (control.propertyOrder < child.propertyOrder) {
this.addproperty_list.insertBefore(control, child);
control = null;
break;
}
}
if (control) {
this.addproperty_list.appendChild(control);
}
},
addPropertyCheckbox: function(key) {
var self = this;
var checkbox, label, labelText, control;
checkbox = self.theme.getCheckbox();
checkbox.style.width = 'auto';
if (this.schema.properties[key] && this.schema.properties[key].title)
labelText = this.schema.properties[key].title;
else
labelText = key;
label = self.theme.getCheckboxLabel(labelText);
control = self.theme.getFormControl(label,checkbox);
control.style.paddingBottom = control.style.marginBottom = control.style.paddingTop = control.style.marginTop = 0;
control.style.height = 'auto';
//control.style.overflowY = 'hidden';
this.insertPropertyControlUsingPropertyOrder(key, control, this.addproperty_list);
checkbox.checked = key in this.editors;
checkbox.addEventListener('change',function() {
if(checkbox.checked) {
self.addObjectProperty(key);
}
else {
self.removeObjectProperty(key);
}
self.onChange(true);
});
self.addproperty_checkboxes[key] = checkbox;
return checkbox;
},
showAddProperty: function() {
if(!this.addproperty_holder) return;
this.hideEditJSON();
// Position the form directly beneath the button
// TODO: edge detection
this.addproperty_holder.style.left = this.addproperty_button.offsetLeft+"px";
this.addproperty_holder.style.top = this.addproperty_button.offsetTop + this.addproperty_button.offsetHeight+"px";
// Disable the rest of the form while editing JSON
this.disable();
this.adding_property = true;
this.addproperty_button.disabled = false;
this.addproperty_holder.style.display = '';
this.refreshAddProperties();
},
hideAddProperty: function() {
if(!this.addproperty_holder) return;
if(!this.adding_property) return;
this.addproperty_holder.style.display = 'none';
this.enable();
this.adding_property = false;
},
toggleAddProperty: function() {
if(this.adding_property) this.hideAddProperty();
else this.showAddProperty();
},
removeObjectProperty: function(property) {
if(this.editors[property]) {
this.editors[property].unregister();
delete this.editors[property];
this.refreshValue();
this.layoutEditors();
}
},
addObjectProperty: function(name, prebuild_only) {
var self = this;
// Property is already added
if(this.editors[name]) return;
// Property was added before and is cached
if(this.cached_editors[name]) {
this.editors[name] = this.cached_editors[name];
if(prebuild_only) return;
this.editors[name].register();
}
// New property
else {
if(!this.canHaveAdditionalProperties() && (!this.schema.properties || !this.schema.properties[name])) {
return;
}
var schema = self.getPropertySchema(name);
// Add the property
var editor = self.jsoneditor.getEditorClass(schema);
self.editors[name] = self.jsoneditor.createEditor(editor,{
jsoneditor: self.jsoneditor,
schema: schema,
path: self.path+'.'+name,
parent: self
});
self.editors[name].preBuild();
if(!prebuild_only) {
var holder = self.theme.getChildEditorHolder();
self.editor_holder.appendChild(holder);
self.editors[name].setContainer(holder);
self.editors[name].build();
self.editors[name].postBuild();
}
self.cached_editors[name] = self.editors[name];
}
// If we're only prebuilding the editors, don't refresh values
if(!prebuild_only) {
self.refreshValue();
self.layoutEditors();
}
},
onChildEditorChange: function(editor) {
this.refreshValue();
this._super(editor);
},
canHaveAdditionalProperties: function() {
if (typeof this.schema.additionalProperties === "boolean") {
return this.schema.additionalProperties;
}
return !this.jsoneditor.options.no_additional_properties;
},
destroy: function() {
$each(this.cached_editors, function(i,el) {
el.destroy();
});
if(this.editor_holder) this.editor_holder.innerHTML = '';
if(this.title && this.title.parentNode) this.title.parentNode.removeChild(this.title);
if(this.error_holder && this.error_holder.parentNode) this.error_holder.parentNode.removeChild(this.error_holder);
this.editors = null;
this.cached_editors = null;
if(this.editor_holder && this.editor_holder.parentNode) this.editor_holder.parentNode.removeChild(this.editor_holder);
this.editor_holder = null;
this._super();
},
getValue: function() {
var result = this._super();
if(this.jsoneditor.options.remove_empty_properties || this.options.remove_empty_properties) {
for(var i in result) {
if(result.hasOwnProperty(i)) {
if(!result[i]) delete result[i];
}
}
}
return result;
},
refreshValue: function() {
this.value = {};
var self = this;
for(var i in this.editors) {
if(!this.editors.hasOwnProperty(i)) continue;
this.value[i] = this.editors[i].getValue();
}
if(this.adding_property) this.refreshAddProperties();
},
refreshAddProperties: function() {
if(this.options.disable_properties || (this.options.disable_properties !== false && this.jsoneditor.options.disable_properties)) {
this.addproperty_controls.style.display = 'none';
return;
}
var can_add = false, can_remove = false, num_props = 0, i, show_modal = false;
// Get number of editors
for(i in this.editors) {
if(!this.editors.hasOwnProperty(i)) continue;
num_props++;
}
// Determine if we can add back removed properties
can_add = this.canHaveAdditionalProperties() && !(typeof this.schema.maxProperties !== "undefined" && num_props >= this.schema.maxProperties);
if(this.addproperty_checkboxes) {
this.addproperty_list.innerHTML = '';
}
this.addproperty_checkboxes = {};
// Check for which editors can't be removed or added back
for(i in this.cached_editors) {
if(!this.cached_editors.hasOwnProperty(i)) continue;
this.addPropertyCheckbox(i);
if(this.isRequired(this.cached_editors[i]) && i in this.editors) {
this.addproperty_checkboxes[i].disabled = true;
}
if(typeof this.schema.minProperties !== "undefined" && num_props <= this.schema.minProperties) {
this.addproperty_checkboxes[i].disabled = this.addproperty_checkboxes[i].checked;
if(!this.addproperty_checkboxes[i].checked) show_modal = true;
}
else if(!(i in this.editors)) {
if(!can_add && !this.schema.properties.hasOwnProperty(i)) {
this.addproperty_checkboxes[i].disabled = true;
}
else {
this.addproperty_checkboxes[i].disabled = false;
show_modal = true;
}
}
else {
show_modal = true;
can_remove = true;
}
}
if(this.canHaveAdditionalProperties()) {
show_modal = true;
}
// Additional addproperty checkboxes not tied to a current editor
for(i in this.schema.properties) {
if(!this.schema.properties.hasOwnProperty(i)) continue;
if(this.cached_editors[i]) continue;
show_modal = true;
this.addPropertyCheckbox(i);
}
// If no editors can be added or removed, hide the modal button
if(!show_modal) {
this.hideAddProperty();
this.addproperty_controls.style.display = 'none';
}
// If additional properties are disabled
else if(!this.canHaveAdditionalProperties()) {
this.addproperty_add.style.display = 'none';
this.addproperty_input.style.display = 'none';
}
// If no new properties can be added
else if(!can_add) {
this.addproperty_add.disabled = true;
}
// If new properties can be added
else {
this.addproperty_add.disabled = false;
}
},
isRequired: function(editor) {
if(typeof editor.schema.required === "boolean") return editor.schema.required;
else if(Array.isArray(this.schema.required)) return this.schema.required.indexOf(editor.key) > -1;
else if(this.jsoneditor.options.required_by_default) return true;
else return false;
},
setValue: function(value, initial) {
var self = this;
value = value || {};
if(typeof value !== "object" || Array.isArray(value)) value = {};
// First, set the values for all of the defined properties
$each(this.cached_editors, function(i,editor) {
// Value explicitly set
if(typeof value[i] !== "undefined") {
self.addObjectProperty(i);
editor.setValue(value[i],initial);
}
// Otherwise, remove value unless this is the initial set or it's required
else if(!initial && !self.isRequired(editor)) {
self.removeObjectProperty(i);
}
// Otherwise, set the value to the default
else {
editor.setValue(editor.getDefault(),initial);
}
});
$each(value, function(i,val) {
if(!self.cached_editors[i]) {
self.addObjectProperty(i);
if(self.editors[i]) self.editors[i].setValue(val,initial);
}
});
this.refreshValue();
this.layoutEditors();
this.onChange();
},
showValidationErrors: function(errors) {
var self = this;
// Get all the errors that pertain to this editor
var my_errors = [];
var other_errors = [];
$each(errors, function(i,error) {
if(error.path === self.path) {
my_errors.push(error);
}
else {
other_errors.push(error);
}
});
// Show errors for this editor
if(this.error_holder) {
if(my_errors.length) {
var message = [];
this.error_holder.innerHTML = '';
this.error_holder.style.display = '';
$each(my_errors, function(i,error) {
self.error_holder.appendChild(self.theme.getErrorMessage(error.message));
});
}
// Hide error area
else {
this.error_holder.style.display = 'none';
}
}
// Show error for the table row if this is inside a table
if(this.options.table_row) {
if(my_errors.length) {
this.theme.addTableRowError(this.container);
}
else {
this.theme.removeTableRowError(this.container);
}
}
// Show errors for child editors
$each(this.editors, function(i,editor) {
editor.showValidationErrors(other_errors);
});
}
});
JSONEditor.defaults.editors.array = JSONEditor.AbstractEditor.extend({
getDefault: function() {
return this.schema["default"] || [];
},
register: function() {
this._super();
if(this.rows) {
for(var i=0; i<this.rows.length; i++) {
this.rows[i].register();
}
}
},
unregister: function() {
this._super();
if(this.rows) {
for(var i=0; i<this.rows.length; i++) {
this.rows[i].unregister();
}
}
},
getNumColumns: function() {
var info = this.getItemInfo(0);
// Tabs require extra horizontal space
if(this.tabs_holder) {
return Math.max(Math.min(12,info.width+2),4);
}
else {
return info.width;
}
},
enable: function() {
if(this.add_row_button) this.add_row_button.disabled = false;
if(this.remove_all_rows_button) this.remove_all_rows_button.disabled = false;
if(this.delete_last_row_button) this.delete_last_row_button.disabled = false;
if(this.rows) {
for(var i=0; i<this.rows.length; i++) {
this.rows[i].enable();
if(this.rows[i].moveup_button) this.rows[i].moveup_button.disabled = false;
if(this.rows[i].movedown_button) this.rows[i].movedown_button.disabled = false;
if(this.rows[i].delete_button) this.rows[i].delete_button.disabled = false;
}
}
this._super();
},
disable: function() {
if(this.add_row_button) this.add_row_button.disabled = true;
if(this.remove_all_rows_button) this.remove_all_rows_button.disabled = true;
if(this.delete_last_row_button) this.delete_last_row_button.disabled = true;
if(this.rows) {
for(var i=0; i<this.rows.length; i++) {
this.rows[i].disable();
if(this.rows[i].moveup_button) this.rows[i].moveup_button.disabled = true;
if(this.rows[i].movedown_button) this.rows[i].movedown_button.disabled = true;
if(this.rows[i].delete_button) this.rows[i].delete_button.disabled = true;
}
}
this._super();
},
preBuild: function() {
this._super();
this.rows = [];
this.row_cache = [];
this.hide_delete_buttons = this.options.disable_array_delete || this.jsoneditor.options.disable_array_delete;
this.hide_delete_all_rows_buttons = this.hide_delete_buttons || this.options.disable_array_delete_all_rows || this.jsoneditor.options.disable_array_delete_all_rows;
this.hide_delete_last_row_buttons = this.hide_delete_buttons || this.options.disable_array_delete_last_row || this.jsoneditor.options.disable_array_delete_last_row;
this.hide_move_buttons = this.options.disable_array_reorder || this.jsoneditor.options.disable_array_reorder;
this.hide_add_button = this.options.disable_array_add || this.jsoneditor.options.disable_array_add;
},
build: function() {
var self = this;
if(!this.options.compact) {
this.header = document.createElement('span');
this.header.textContent = this.getTitle();
this.title = this.theme.getHeader(this.header);
this.container.appendChild(this.title);
this.title_controls = this.theme.getHeaderButtonHolder();
this.title.appendChild(this.title_controls);
if(this.schema.description) {
this.description = this.theme.getDescription(this.schema.description);
this.container.appendChild(this.description);
}
if(this.schema.append) {
this.append = this.theme.getAppend(this.schema.append);
this.container.appendChild(this.append);
}
this.error_holder = document.createElement('div');
this.container.appendChild(this.error_holder);
if(this.schema.format === 'tabs') {
this.controls = this.theme.getHeaderButtonHolder();
this.title.appendChild(this.controls);
this.tabs_holder = this.theme.getTabHolder();
this.container.appendChild(this.tabs_holder);
this.row_holder = this.theme.getTabContentHolder(this.tabs_holder);
this.active_tab = null;
}
else {
this.panel = this.theme.getIndentedPanel();
this.container.appendChild(this.panel);
this.row_holder = document.createElement('div');
this.panel.appendChild(this.row_holder);
this.controls = this.theme.getButtonHolder();
this.panel.appendChild(this.controls);
}
}
else {
this.panel = this.theme.getIndentedPanel();
this.container.appendChild(this.panel);
this.controls = this.theme.getButtonHolder();
this.panel.appendChild(this.controls);
this.row_holder = document.createElement('div');
this.panel.appendChild(this.row_holder);
}
// Add controls
this.addControls();
},
onChildEditorChange: function(editor) {
this.refreshValue();
this.refreshTabs(true);
this._super(editor);
},
getItemTitle: function() {
if(!this.item_title) {
if(this.schema.items && !Array.isArray(this.schema.items)) {
var tmp = this.jsoneditor.expandRefs(this.schema.items);
if (typeof tmp.title == 'undefined')
this.item_title = 'item';
else
this.item_title = $.i18n(tmp.title);
}
else {
this.item_title = 'item';
}
}
return this.item_title;
},
getItemSchema: function(i) {
if(Array.isArray(this.schema.items)) {
if(i >= this.schema.items.length) {
if(this.schema.additionalItems===true) {
return {};
}
else if(this.schema.additionalItems) {
return $extend({},this.schema.additionalItems);
}
}
else {
return $extend({},this.schema.items[i]);
}
}
else if(this.schema.items) {
return $extend({},this.schema.items);
}
else {
return {};
}
},
getItemInfo: function(i) {
var schema = this.getItemSchema(i);
// Check if it's cached
this.item_info = this.item_info || {};
var stringified = JSON.stringify(schema);
if(typeof this.item_info[stringified] !== "undefined") return this.item_info[stringified];
// Get the schema for this item
schema = this.jsoneditor.expandRefs(schema);
this.item_info[stringified] = {
title: schema.title || "item",
'default': schema["default"],
width: 12,
child_editors: schema.properties || schema.items
};
return this.item_info[stringified];
},
getElementEditor: function(i) {
var item_info = this.getItemInfo(i);
var schema = this.getItemSchema(i);
schema = this.jsoneditor.expandRefs(schema);
schema.title = $.i18n(item_info.title)+' '+(i+1);
var editor = this.jsoneditor.getEditorClass(schema);
var holder;
if(this.tabs_holder) {
holder = this.theme.getTabContent();
}
else if(item_info.child_editors) {
holder = this.theme.getChildEditorHolder();
}
else {
holder = this.theme.getIndentedPanel();
}
this.row_holder.appendChild(holder);
var ret = this.jsoneditor.createEditor(editor,{
jsoneditor: this.jsoneditor,
schema: schema,
container: holder,
path: this.path+'.'+i,
parent: this,
required: true
});
ret.preBuild();
ret.build();
ret.postBuild();
if(!ret.title_controls) {
ret.array_controls = this.theme.getButtonHolder();
holder.appendChild(ret.array_controls);
}
return ret;
},
destroy: function() {
this.empty(true);
if(this.title && this.title.parentNode) this.title.parentNode.removeChild(this.title);
if(this.description && this.description.parentNode) this.description.parentNode.removeChild(this.description);
if(this.row_holder && this.row_holder.parentNode) this.row_holder.parentNode.removeChild(this.row_holder);
if(this.controls && this.controls.parentNode) this.controls.parentNode.removeChild(this.controls);
if(this.panel && this.panel.parentNode) this.panel.parentNode.removeChild(this.panel);
this.rows = this.row_cache = this.title = this.description = this.row_holder = this.panel = this.controls = null;
this._super();
},
empty: function(hard) {
if(!this.rows) return;
var self = this;
$each(this.rows,function(i,row) {
if(hard) {
if(row.tab && row.tab.parentNode) row.tab.parentNode.removeChild(row.tab);
self.destroyRow(row,true);
self.row_cache[i] = null;
}
self.rows[i] = null;
});
self.rows = [];
if(hard) self.row_cache = [];
},
destroyRow: function(row,hard) {
var holder = row.container;
if(hard) {
row.destroy();
if(holder.parentNode) holder.parentNode.removeChild(holder);
if(row.tab && row.tab.parentNode) row.tab.parentNode.removeChild(row.tab);
}
else {
if(row.tab) row.tab.style.display = 'none';
holder.style.display = 'none';
row.unregister();
}
},
getMax: function() {
if((Array.isArray(this.schema.items)) && this.schema.additionalItems === false) {
return Math.min(this.schema.items.length,this.schema.maxItems || Infinity);
}
else {
return this.schema.maxItems || Infinity;
}
},
refreshTabs: function(refresh_headers) {
var self = this;
$each(this.rows, function(i,row) {
if(!row.tab) return;
if(refresh_headers) {
row.tab_text.textContent = row.getHeaderText();
}
else {
if(row.tab === self.active_tab) {
self.theme.markTabActive(row.tab);
row.container.style.display = '';
}
else {
self.theme.markTabInactive(row.tab);
row.container.style.display = 'none';
}
}
});
},
setValue: function(value, initial) {
// Update the array's value, adding/removing rows when necessary
value = value || [];
if(!(Array.isArray(value))) value = [value];
var serialized = JSON.stringify(value);
if(serialized === this.serialized) return;
// Make sure value has between minItems and maxItems items in it
if(this.schema.minItems) {
while(value.length < this.schema.minItems) {
value.push(this.getItemInfo(value.length)["default"]);
}
}
if(this.getMax() && value.length > this.getMax()) {
value = value.slice(0,this.getMax());
}
var self = this;
$each(value,function(i,val) {
if(self.rows[i]) {
// TODO: don't set the row's value if it hasn't changed
self.rows[i].setValue(val,initial);
}
else if(self.row_cache[i]) {
self.rows[i] = self.row_cache[i];
self.rows[i].setValue(val,initial);
self.rows[i].container.style.display = '';
if(self.rows[i].tab) self.rows[i].tab.style.display = '';
self.rows[i].register();
}
else {
self.addRow(val,initial);
}
});
for(var j=value.length; j<self.rows.length; j++) {
self.destroyRow(self.rows[j]);
self.rows[j] = null;
}
self.rows = self.rows.slice(0,value.length);
// Set the active tab
var new_active_tab = null;
$each(self.rows, function(i,row) {
if(row.tab === self.active_tab) {
new_active_tab = row.tab;
return false;
}
});
if(!new_active_tab && self.rows.length) new_active_tab = self.rows[0].tab;
self.active_tab = new_active_tab;
self.refreshValue(initial);
self.refreshTabs(true);
self.refreshTabs();
self.onChange();
// TODO: sortable
},
refreshValue: function(force) {
var self = this;
var oldi = this.value? this.value.length : 0;
this.value = [];
$each(this.rows,function(i,editor) {
// Get the value for this editor
self.value[i] = editor.getValue();
});
if(oldi !== this.value.length || force) {
// If we currently have minItems items in the array
var minItems = this.schema.minItems && this.schema.minItems >= this.rows.length;
$each(this.rows,function(i,editor) {
// Hide the move down button for the last row
if(editor.movedown_button) {
if(i === self.rows.length - 1) {
editor.movedown_button.style.display = 'none';
}
else {
editor.movedown_button.style.display = '';
}
}
// Hide the delete button if we have minItems items
if(editor.delete_button) {
if(minItems) {
editor.delete_button.style.display = 'none';
}
else {
editor.delete_button.style.display = '';
}
}
// Get the value for this editor
self.value[i] = editor.getValue();
});
var controls_needed = false;
if(!this.value.length) {
this.delete_last_row_button.style.display = 'none';
this.remove_all_rows_button.style.display = 'none';
}
else if(this.value.length === 1) {
this.remove_all_rows_button.style.display = 'none';
// If there are minItems items in the array, or configured to hide the delete_last_row button, hide the delete button beneath the rows
if(minItems || this.hide_delete_last_row_buttons) {
this.delete_last_row_button.style.display = 'none';
}
else {
this.delete_last_row_button.style.display = '';
controls_needed = true;
}
}
else {
if(minItems || this.hide_delete_last_row_buttons) {
this.delete_last_row_button.style.display = 'none';
}
else {
this.delete_last_row_button.style.display = '';
controls_needed = true;
}
if(minItems || this.hide_delete_all_rows_buttons) {
this.remove_all_rows_button.style.display = 'none';
}
else {
this.remove_all_rows_button.style.display = '';
controls_needed = true;
}
}
// If there are maxItems in the array, hide the add button beneath the rows
if((this.getMax() && this.getMax() <= this.rows.length) || this.hide_add_button){
this.add_row_button.style.display = 'none';
}
else {
this.add_row_button.style.display = '';
controls_needed = true;
}
if(!this.collapsed && controls_needed) {
this.controls.style.display = 'inline-block';
}
else {
this.controls.style.display = 'none';
}
}
},
addRow: function(value, initial) {
var self = this;
var i = this.rows.length;
self.rows[i] = this.getElementEditor(i);
self.row_cache[i] = self.rows[i];
if(self.tabs_holder) {
self.rows[i].tab_text = document.createElement('span');
self.rows[i].tab_text.textContent = self.rows[i].getHeaderText();
self.rows[i].tab = self.theme.getTab(self.rows[i].tab_text);
self.rows[i].tab.addEventListener('click', function(e) {
self.active_tab = self.rows[i].tab;
self.refreshTabs();
e.preventDefault();
e.stopPropagation();
});
self.theme.addTab(self.tabs_holder, self.rows[i].tab);
}
var controls_holder = self.rows[i].title_controls || self.rows[i].array_controls;
// Buttons to delete row, move row up, and move row down
if(!self.hide_delete_buttons) {
self.rows[i].delete_button = this.getButton(self.getItemTitle(),'delete',this.translate('edt_msg_button_delete_row_title',[self.getItemTitle()]));
self.rows[i].delete_button.className += ' delete';
self.rows[i].delete_button.setAttribute('data-i',i);
self.rows[i].delete_button.addEventListener('click',function(e) {
e.preventDefault();
e.stopPropagation();
var i = this.getAttribute('data-i')*1;
var value = self.getValue();
var newval = [];
var new_active_tab = null;
$each(value,function(j,row) {
if(j===i) {
// If the one we're deleting is the active tab
if(self.rows[j].tab === self.active_tab) {
// Make the next tab active if there is one
// Note: the next tab is going to be the current tab after deletion
if(self.rows[j+1]) new_active_tab = self.rows[j].tab;
// Otherwise, make the previous tab active if there is one
else if(j) new_active_tab = self.rows[j-1].tab;
}
return; // If this is the one we're deleting
}
newval.push(row);
});
self.setValue(newval);
if(new_active_tab) {
self.active_tab = new_active_tab;
self.refreshTabs();
}
self.onChange(true);
});
if(controls_holder) {
controls_holder.appendChild(self.rows[i].delete_button);
}
}
if(i && !self.hide_move_buttons) {
self.rows[i].moveup_button = this.getButton('','moveup',this.translate('edt_msg_button_move_up_title'));
self.rows[i].moveup_button.className += ' moveup';
self.rows[i].moveup_button.setAttribute('data-i',i);
self.rows[i].moveup_button.addEventListener('click',function(e) {
e.preventDefault();
e.stopPropagation();
var i = this.getAttribute('data-i')*1;
if(i<=0) return;
var rows = self.getValue();
var tmp = rows[i-1];
rows[i-1] = rows[i];
rows[i] = tmp;
self.setValue(rows);
self.active_tab = self.rows[i-1].tab;
self.refreshTabs();
self.onChange(true);
});
if(controls_holder) {
controls_holder.appendChild(self.rows[i].moveup_button);
}
}
if(!self.hide_move_buttons) {
self.rows[i].movedown_button = this.getButton('','movedown',this.translate('edt_msg_button_move_down_title'));
self.rows[i].movedown_button.className += ' movedown';
self.rows[i].movedown_button.setAttribute('data-i',i);
self.rows[i].movedown_button.addEventListener('click',function(e) {
e.preventDefault();
e.stopPropagation();
var i = this.getAttribute('data-i')*1;
var rows = self.getValue();
if(i>=rows.length-1) return;
var tmp = rows[i+1];
rows[i+1] = rows[i];
rows[i] = tmp;
self.setValue(rows);
self.active_tab = self.rows[i+1].tab;
self.refreshTabs();
self.onChange(true);
});
if(controls_holder) {
controls_holder.appendChild(self.rows[i].movedown_button);
}
}
if(value) self.rows[i].setValue(value, initial);
self.refreshTabs();
},
addControls: function() {
var self = this;
this.collapsed = false;
this.toggle_button = this.getButton('','collapse',this.translate('edt_msg_button_collapse'));
this.title_controls.appendChild(this.toggle_button);
var row_holder_display = self.row_holder.style.display;
var controls_display = self.controls.style.display;
this.toggle_button.addEventListener('click',function(e) {
e.preventDefault();
e.stopPropagation();
if(self.collapsed) {
self.collapsed = false;
if(self.panel) self.panel.style.display = '';
self.row_holder.style.display = row_holder_display;
if(self.tabs_holder) self.tabs_holder.style.display = '';
self.controls.style.display = controls_display;
self.setButtonText(this,'','collapse',self.translate('edt_msg_button_collapse'));
}
else {
self.collapsed = true;
self.row_holder.style.display = 'none';
if(self.tabs_holder) self.tabs_holder.style.display = 'none';
self.controls.style.display = 'none';
if(self.panel) self.panel.style.display = 'none';
self.setButtonText(this,'','expand',self.translate('edt_msg_button_expand'));
}
});
// If it should start collapsed
if(this.options.collapsed) {
$trigger(this.toggle_button,'click');
}
// Collapse button disabled
if(this.schema.options && typeof this.schema.options.disable_collapse !== "undefined") {
if(this.schema.options.disable_collapse) this.toggle_button.style.display = 'none';
}
else if(this.jsoneditor.options.disable_collapse) {
this.toggle_button.style.display = 'none';
}
// Add "new row" and "delete last" buttons below editor
this.add_row_button = this.getButton(this.getItemTitle(),'add',this.translate('edt_msg_button_add_row_title',[this.getItemTitle()]));
this.add_row_button.addEventListener('click',function(e) {
e.preventDefault();
e.stopPropagation();
var i = self.rows.length;
if(self.row_cache[i]) {
self.rows[i] = self.row_cache[i];
self.rows[i].setValue(self.rows[i].getDefault(), true);
self.rows[i].container.style.display = '';
if(self.rows[i].tab) self.rows[i].tab.style.display = '';
self.rows[i].register();
}
else {
self.addRow();
}
self.active_tab = self.rows[i].tab;
self.refreshTabs();
self.refreshValue();
self.onChange(true);
});
self.controls.appendChild(this.add_row_button);
this.delete_last_row_button = this.getButton(this.translate('edt_msg_button_delete_last',[this.getItemTitle()]),'delete',this.translate('edt_msg_button_delete_last_title',[this.getItemTitle()]));
this.delete_last_row_button.addEventListener('click',function(e) {
e.preventDefault();
e.stopPropagation();
var rows = self.getValue();
var new_active_tab = null;
if(self.rows.length > 1 && self.rows[self.rows.length-1].tab === self.active_tab) new_active_tab = self.rows[self.rows.length-2].tab;
rows.pop();
self.setValue(rows);
if(new_active_tab) {
self.active_tab = new_active_tab;
self.refreshTabs();
}
self.onChange(true);
});
self.controls.appendChild(this.delete_last_row_button);
this.remove_all_rows_button = this.getButton(this.translate('edt_msg_button_delete_all'),'delete',this.translate('edt_msg_button_delete_all_title'));
this.remove_all_rows_button.addEventListener('click',function(e) {
e.preventDefault();
e.stopPropagation();
self.setValue([]);
self.onChange(true);
});
self.controls.appendChild(this.remove_all_rows_button);
if(self.tabs) {
this.add_row_button.style.width = '100%';
this.add_row_button.style.textAlign = 'left';
this.add_row_button.style.marginBottom = '3px';
this.delete_last_row_button.style.width = '100%';
this.delete_last_row_button.style.textAlign = 'left';
this.delete_last_row_button.style.marginBottom = '3px';
this.remove_all_rows_button.style.width = '100%';
this.remove_all_rows_button.style.textAlign = 'left';
this.remove_all_rows_button.style.marginBottom = '3px';
}
},
showValidationErrors: function(errors) {
var self = this;
// Get all the errors that pertain to this editor
var my_errors = [];
var other_errors = [];
$each(errors, function(i,error) {
if(error.path === self.path) {
my_errors.push(error);
}
else {
other_errors.push(error);
}
});
// Show errors for this editor
if(this.error_holder) {
if(my_errors.length) {
var message = [];
this.error_holder.innerHTML = '';
this.error_holder.style.display = '';
$each(my_errors, function(i,error) {
self.error_holder.appendChild(self.theme.getErrorMessage(error.message));
});
}
// Hide error area
else {
this.error_holder.style.display = 'none';
}
}
// Show errors for child editors
$each(this.rows, function(i,row) {
row.showValidationErrors(other_errors);
});
}
});
JSONEditor.defaults.editors.table = JSONEditor.defaults.editors.array.extend({
register: function() {
this._super();
if(this.rows) {
for(var i=0; i<this.rows.length; i++) {
this.rows[i].register();
}
}
},
unregister: function() {
this._super();
if(this.rows) {
for(var i=0; i<this.rows.length; i++) {
this.rows[i].unregister();
}
}
},
getNumColumns: function() {
return Math.max(Math.min(12,this.width),3);
},
preBuild: function() {
var item_schema = this.jsoneditor.expandRefs(this.schema.items || {});
this.item_title = item_schema.title || 'row';
this.item_default = item_schema["default"] || null;
this.item_has_child_editors = item_schema.properties || item_schema.items;
this.width = 12;
this._super();
},
build: function() {
var self = this;
this.table = this.theme.getTable();
this.container.appendChild(this.table);
this.thead = this.theme.getTableHead();
this.table.appendChild(this.thead);
this.header_row = this.theme.getTableRow();
this.thead.appendChild(this.header_row);
this.row_holder = this.theme.getTableBody();
this.table.appendChild(this.row_holder);
// Determine the default value of array element
var tmp = this.getElementEditor(0,true);
this.item_default = tmp.getDefault();
this.width = tmp.getNumColumns() + 2;
if(!this.options.compact) {
this.title = this.theme.getHeader(this.getTitle());
this.container.appendChild(this.title);
this.title_controls = this.theme.getHeaderButtonHolder();
this.title.appendChild(this.title_controls);
if(this.schema.description) {
this.description = this.theme.getDescription(this.schema.description);
this.container.appendChild(this.description);
}
if(this.schema.append) {
this.append = this.theme.getAppend(this.getAppend());
this.container.appendChild(this.append);
}
this.panel = this.theme.getIndentedPanel();
this.container.appendChild(this.panel);
this.error_holder = document.createElement('div');
this.panel.appendChild(this.error_holder);
}
else {
this.panel = document.createElement('div');
this.container.appendChild(this.panel);
}
this.panel.appendChild(this.table);
this.controls = this.theme.getButtonHolder();
this.panel.appendChild(this.controls);
if(this.item_has_child_editors) {
var ce = tmp.getChildEditors();
var order = tmp.property_order || Object.keys(ce);
for(var i=0; i<order.length; i++) {
var th = self.theme.getTableHeaderCell(ce[order[i]].getTitle());
if(ce[order[i]].options.hidden) th.style.display = 'none';
self.header_row.appendChild(th);
}
}
else {
self.header_row.appendChild(self.theme.getTableHeaderCell(this.item_title));
}
tmp.destroy();
this.row_holder.innerHTML = '';
// Row Controls column
this.controls_header_cell = self.theme.getTableHeaderCell(" ");
self.header_row.appendChild(this.controls_header_cell);
// Add controls
this.addControls();
},
onChildEditorChange: function(editor) {
this.refreshValue();
this._super();
},
getItemDefault: function() {
return $extend({},{"default":this.item_default})["default"];
},
getItemTitle: function() {
return this.item_title;
},
getElementEditor: function(i,ignore) {
var schema_copy = $extend({},this.schema.items);
var editor = this.jsoneditor.getEditorClass(schema_copy, this.jsoneditor);
var row = this.row_holder.appendChild(this.theme.getTableRow());
var holder = row;
if(!this.item_has_child_editors) {
holder = this.theme.getTableCell();
row.appendChild(holder);
}
var ret = this.jsoneditor.createEditor(editor,{
jsoneditor: this.jsoneditor,
schema: schema_copy,
container: holder,
path: this.path+'.'+i,
parent: this,
compact: true,
table_row: true
});
ret.preBuild();
if(!ignore) {
ret.build();
ret.postBuild();
ret.controls_cell = row.appendChild(this.theme.getTableCell());
ret.row = row;
ret.table_controls = this.theme.getButtonHolder();
ret.controls_cell.appendChild(ret.table_controls);
ret.table_controls.style.margin = 0;
ret.table_controls.style.padding = 0;
}
return ret;
},
destroy: function() {
this.innerHTML = '';
if(this.title && this.title.parentNode) this.title.parentNode.removeChild(this.title);
if(this.description && this.description.parentNode) this.description.parentNode.removeChild(this.description);
if(this.row_holder && this.row_holder.parentNode) this.row_holder.parentNode.removeChild(this.row_holder);
if(this.table && this.table.parentNode) this.table.parentNode.removeChild(this.table);
if(this.panel && this.panel.parentNode) this.panel.parentNode.removeChild(this.panel);
this.rows = this.title = this.description = this.row_holder = this.table = this.panel = null;
this._super();
},
setValue: function(value, initial) {
// Update the array's value, adding/removing rows when necessary
value = value || [];
// Make sure value has between minItems and maxItems items in it
if(this.schema.minItems) {
while(value.length < this.schema.minItems) {
value.push(this.getItemDefault());
}
}
if(this.schema.maxItems && value.length > this.schema.maxItems) {
value = value.slice(0,this.schema.maxItems);
}
var serialized = JSON.stringify(value);
if(serialized === this.serialized) return;
var numrows_changed = false;
var self = this;
$each(value,function(i,val) {
if(self.rows[i]) {
// TODO: don't set the row's value if it hasn't changed
self.rows[i].setValue(val);
}
else {
self.addRow(val);
numrows_changed = true;
}
});
for(var j=value.length; j<self.rows.length; j++) {
var holder = self.rows[j].container;
if(!self.item_has_child_editors) {
self.rows[j].row.parentNode.removeChild(self.rows[j].row);
}
self.rows[j].destroy();
if(holder.parentNode) holder.parentNode.removeChild(holder);
self.rows[j] = null;
numrows_changed = true;
}
self.rows = self.rows.slice(0,value.length);
self.refreshValue();
if(numrows_changed || initial) self.refreshRowButtons();
self.onChange();
// TODO: sortable
},
refreshRowButtons: function() {
var self = this;
// If we currently have minItems items in the array
var minItems = this.schema.minItems && this.schema.minItems >= this.rows.length;
var need_row_buttons = false;
$each(this.rows,function(i,editor) {
// Hide the move down button for the last row
if(editor.movedown_button) {
if(i === self.rows.length - 1) {
editor.movedown_button.style.display = 'none';
}
else {
need_row_buttons = true;
editor.movedown_button.style.display = '';
}
}
// Hide the delete button if we have minItems items
if(editor.delete_button) {
if(minItems) {
editor.delete_button.style.display = 'none';
}
else {
need_row_buttons = true;
editor.delete_button.style.display = '';
}
}
if(editor.moveup_button) {
need_row_buttons = true;
}
});
// Show/hide controls column in table
$each(this.rows,function(i,editor) {
if(need_row_buttons) {
editor.controls_cell.style.display = '';
}
else {
editor.controls_cell.style.display = 'none';
}
});
if(need_row_buttons) {
this.controls_header_cell.style.display = '';
}
else {
this.controls_header_cell.style.display = 'none';
}
var controls_needed = false;
if(!this.value.length) {
this.delete_last_row_button.style.display = 'none';
this.remove_all_rows_button.style.display = 'none';
this.table.style.display = 'none';
}
else if(this.value.length === 1) {
this.table.style.display = '';
this.remove_all_rows_button.style.display = 'none';
// If there are minItems items in the array, or configured to hide the delete_last_row button, hide the delete button beneath the rows
if(minItems || this.hide_delete_last_row_buttons) {
this.delete_last_row_button.style.display = 'none';
}
else {
this.delete_last_row_button.style.display = '';
controls_needed = true;
}
}
else {
this.table.style.display = '';
if(minItems || this.hide_delete_last_row_buttons) {
this.delete_last_row_button.style.display = 'none';
}
else {
this.delete_last_row_button.style.display = '';
controls_needed = true;
}
if(minItems || this.hide_delete_all_rows_buttons) {
this.remove_all_rows_button.style.display = 'none';
}
else {
this.remove_all_rows_button.style.display = '';
controls_needed = true;
}
}
// If there are maxItems in the array, hide the add button beneath the rows
if((this.schema.maxItems && this.schema.maxItems <= this.rows.length) || this.hide_add_button) {
this.add_row_button.style.display = 'none';
}
else {
this.add_row_button.style.display = '';
controls_needed = true;
}
if(!controls_needed) {
this.controls.style.display = 'none';
}
else {
this.controls.style.display = '';
}
},
refreshValue: function() {
var self = this;
this.value = [];
$each(this.rows,function(i,editor) {
// Get the value for this editor
self.value[i] = editor.getValue();
});
this.serialized = JSON.stringify(this.value);
},
addRow: function(value) {
var self = this;
var i = this.rows.length;
self.rows[i] = this.getElementEditor(i);
var controls_holder = self.rows[i].table_controls;
// Buttons to delete row, move row up, and move row down
if(!this.hide_delete_buttons) {
self.rows[i].delete_button = this.getButton('','delete',this.translate('edt_msg_button_delete_row_title_short'));
self.rows[i].delete_button.className += ' delete';
self.rows[i].delete_button.setAttribute('data-i',i);
self.rows[i].delete_button.addEventListener('click',function(e) {
e.preventDefault();
e.stopPropagation();
var i = this.getAttribute('data-i')*1;
var value = self.getValue();
var newval = [];
$each(value,function(j,row) {
if(j===i) return; // If this is the one we're deleting
newval.push(row);
});
self.setValue(newval);
self.onChange(true);
});
controls_holder.appendChild(self.rows[i].delete_button);
}
if(i && !this.hide_move_buttons) {
self.rows[i].moveup_button = this.getButton('','moveup',this.translate('edt_msg_button_move_up_title'));
self.rows[i].moveup_button.className += ' moveup';
self.rows[i].moveup_button.setAttribute('data-i',i);
self.rows[i].moveup_button.addEventListener('click',function(e) {
e.preventDefault();
e.stopPropagation();
var i = this.getAttribute('data-i')*1;
if(i<=0) return;
var rows = self.getValue();
var tmp = rows[i-1];
rows[i-1] = rows[i];
rows[i] = tmp;
self.setValue(rows);
self.onChange(true);
});
controls_holder.appendChild(self.rows[i].moveup_button);
}
if(!this.hide_move_buttons) {
self.rows[i].movedown_button = this.getButton('','movedown',this.translate('edt_msg_button_move_down_title'));
self.rows[i].movedown_button.className += ' movedown';
self.rows[i].movedown_button.setAttribute('data-i',i);
self.rows[i].movedown_button.addEventListener('click',function(e) {
e.preventDefault();
e.stopPropagation();
var i = this.getAttribute('data-i')*1;
var rows = self.getValue();
if(i>=rows.length-1) return;
var tmp = rows[i+1];
rows[i+1] = rows[i];
rows[i] = tmp;
self.setValue(rows);
self.onChange(true);
});
controls_holder.appendChild(self.rows[i].movedown_button);
}
if(value) self.rows[i].setValue(value);
},
addControls: function() {
var self = this;
this.collapsed = false;
this.toggle_button = this.getButton('','collapse',this.translate('edt_msg_button_collapse'));
if(this.title_controls) {
this.title_controls.appendChild(this.toggle_button);
this.toggle_button.addEventListener('click',function(e) {
e.preventDefault();
e.stopPropagation();
if(self.collapsed) {
self.collapsed = false;
self.panel.style.display = '';
self.setButtonText(this,'','collapse',self.translate('edt_msg_button_collapse'));
}
else {
self.collapsed = true;
self.panel.style.display = 'none';
self.setButtonText(this,'','expand',self.translate('edt_msg_button_expand'));
}
});
// If it should start collapsed
if(this.options.collapsed) {
$trigger(this.toggle_button,'click');
}
// Collapse button disabled
if(this.schema.options && typeof this.schema.options.disable_collapse !== "undefined") {
if(this.schema.options.disable_collapse) this.toggle_button.style.display = 'none';
}
else if(this.jsoneditor.options.disable_collapse) {
this.toggle_button.style.display = 'none';
}
}
// Add "new row" and "delete last" buttons below editor
this.add_row_button = this.getButton(this.getItemTitle(),'add',this.translate('edt_msg_button_add_row_title',[this.getItemTitle()]));
this.add_row_button.addEventListener('click',function(e) {
e.preventDefault();
e.stopPropagation();
self.addRow();
self.refreshValue();
self.refreshRowButtons();
self.onChange(true);
});
self.controls.appendChild(this.add_row_button);
this.delete_last_row_button = this.getButton(this.translate('edt_msg_button_delete_last',[this.getItemTitle()]),'delete',this.translate('edt_msg_button_delete_last_title',[this.getItemTitle()]));
this.delete_last_row_button.addEventListener('click',function(e) {
e.preventDefault();
e.stopPropagation();
var rows = self.getValue();
rows.pop();
self.setValue(rows);
self.onChange(true);
});
self.controls.appendChild(this.delete_last_row_button);
this.remove_all_rows_button = this.getButton(this.translate('edt_msg_button_delete_all'),'delete',this.translate('edt_msg_button_delete_all_title'));
this.remove_all_rows_button.addEventListener('click',function(e) {
e.preventDefault();
e.stopPropagation();
self.setValue([]);
self.onChange(true);
});
self.controls.appendChild(this.remove_all_rows_button);
}
});
// Multiple Editor (for when `type` is an array)
JSONEditor.defaults.editors.multiple = JSONEditor.AbstractEditor.extend({
register: function() {
if(this.editors) {
for(var i=0; i<this.editors.length; i++) {
if(!this.editors[i]) continue;
this.editors[i].unregister();
}
if(this.editors[this.type]) this.editors[this.type].register();
}
this._super();
},
unregister: function() {
this._super();
if(this.editors) {
for(var i=0; i<this.editors.length; i++) {
if(!this.editors[i]) continue;
this.editors[i].unregister();
}
}
},
getNumColumns: function() {
if(!this.editors[this.type]) return 4;
return Math.max(this.editors[this.type].getNumColumns(),4);
},
enable: function() {
if(this.editors) {
for(var i=0; i<this.editors.length; i++) {
if(!this.editors[i]) continue;
this.editors[i].enable();
}
}
this.switcher.disabled = false;
this._super();
},
disable: function() {
if(this.editors) {
for(var i=0; i<this.editors.length; i++) {
if(!this.editors[i]) continue;
this.editors[i].disable();
}
}
this.switcher.disabled = true;
this._super();
},
switchEditor: function(i) {
var self = this;
if(!this.editors[i]) {
this.buildChildEditor(i);
}
var current_value = self.getValue();
self.type = i;
self.register();
$each(self.editors,function(type,editor) {
if(!editor) return;
if(self.type === type) {
if(self.keep_values) editor.setValue(current_value,true);
editor.container.style.display = '';
}
else editor.container.style.display = 'none';
});
self.refreshValue();
self.refreshHeaderText();
},
buildChildEditor: function(i) {
var self = this;
var type = this.types[i];
var holder = self.theme.getChildEditorHolder();
self.editor_holder.appendChild(holder);
var schema;
if(typeof type === "string") {
schema = $extend({},self.schema);
schema.type = type;
}
else {
schema = $extend({},self.schema,type);
schema = self.jsoneditor.expandRefs(schema);
// If we need to merge `required` arrays
if(type.required && Array.isArray(type.required) && self.schema.required && Array.isArray(self.schema.required)) {
schema.required = self.schema.required.concat(type.required);
}
}
var editor = self.jsoneditor.getEditorClass(schema);
self.editors[i] = self.jsoneditor.createEditor(editor,{
jsoneditor: self.jsoneditor,
schema: schema,
container: holder,
path: self.path,
parent: self,
required: true
});
self.editors[i].preBuild();
self.editors[i].build();
self.editors[i].postBuild();
if(self.editors[i].header) self.editors[i].header.style.display = 'none';
self.editors[i].option = self.switcher_options[i];
holder.addEventListener('change_header_text',function() {
self.refreshHeaderText();
});
if(i !== self.type) holder.style.display = 'none';
},
preBuild: function() {
var self = this;
this.types = [];
this.type = 0;
this.editors = [];
this.validators = [];
this.keep_values = true;
if(typeof this.jsoneditor.options.keep_oneof_values !== "undefined") this.keep_values = this.jsoneditor.options.keep_oneof_values;
if(typeof this.options.keep_oneof_values !== "undefined") this.keep_values = this.options.keep_oneof_values;
if(this.schema.oneOf) {
this.oneOf = true;
this.types = this.schema.oneOf;
delete this.schema.oneOf;
}
else if(this.schema.anyOf) {
this.anyOf = true;
this.types = this.schema.anyOf;
delete this.schema.anyOf;
}
else {
if(!this.schema.type || this.schema.type === "any") {
this.types = ['string','number','integer','boolean','object','array','null'];
// If any of these primitive types are disallowed
if(this.schema.disallow) {
var disallow = this.schema.disallow;
if(typeof disallow !== 'object' || !(Array.isArray(disallow))) {
disallow = [disallow];
}
var allowed_types = [];
$each(this.types,function(i,type) {
if(disallow.indexOf(type) === -1) allowed_types.push(type);
});
this.types = allowed_types;
}
}
else if(Array.isArray(this.schema.type)) {
this.types = this.schema.type;
}
else {
this.types = [this.schema.type];
}
delete this.schema.type;
}
this.display_text = this.getDisplayText(this.types);
},
build: function() {
var self = this;
var container = this.container;
this.header = this.label = this.theme.getFormInputLabel(this.getTitle());
this.container.appendChild(this.header);
this.switcher = this.theme.getSwitcher(this.display_text);
container.appendChild(this.switcher);
this.switcher.addEventListener('change',function(e) {
e.preventDefault();
e.stopPropagation();
self.switchEditor(self.display_text.indexOf(this.value));
self.onChange(true);
});
this.editor_holder = document.createElement('div');
container.appendChild(this.editor_holder);
var validator_options = {};
if(self.jsoneditor.options.custom_validators) {
validator_options.custom_validators = self.jsoneditor.options.custom_validators;
}
this.switcher_options = this.theme.getSwitcherOptions(this.switcher);
$each(this.types,function(i,type) {
self.editors[i] = false;
var schema;
if(typeof type === "string") {
schema = $extend({},self.schema);
schema.type = type;
}
else {
schema = $extend({},self.schema,type);
// If we need to merge `required` arrays
if(type.required && Array.isArray(type.required) && self.schema.required && Array.isArray(self.schema.required)) {
schema.required = self.schema.required.concat(type.required);
}
}
self.validators[i] = new JSONEditor.Validator(self.jsoneditor,schema,validator_options);
});
this.switchEditor(0);
},
onChildEditorChange: function(editor) {
if(this.editors[this.type]) {
this.refreshValue();
this.refreshHeaderText();
}
this._super();
},
refreshHeaderText: function() {
var display_text = this.getDisplayText(this.types);
$each(this.switcher_options, function(i,option) {
option.textContent = display_text[i];
});
},
refreshValue: function() {
this.value = this.editors[this.type].getValue();
},
setValue: function(val,initial) {
// Determine type by getting the first one that validates
var self = this;
$each(this.validators, function(i,validator) {
if(!validator.validate(val).length) {
self.type = i;
self.switcher.value = self.display_text[i];
return false;
}
});
this.switchEditor(this.type);
this.editors[this.type].setValue(val,initial);
this.refreshValue();
self.onChange();
},
destroy: function() {
$each(this.editors, function(type,editor) {
if(editor) editor.destroy();
});
if(this.editor_holder && this.editor_holder.parentNode) this.editor_holder.parentNode.removeChild(this.editor_holder);
if(this.switcher && this.switcher.parentNode) this.switcher.parentNode.removeChild(this.switcher);
this._super();
},
showValidationErrors: function(errors) {
var self = this;
// oneOf and anyOf error paths need to remove the oneOf[i] part before passing to child editors
if(this.oneOf || this.anyOf) {
var check_part = this.oneOf? 'oneOf' : 'anyOf';
$each(this.editors,function(i,editor) {
if(!editor) return;
var check = self.path+'.'+check_part+'['+i+']';
var new_errors = [];
$each(errors, function(j,error) {
if(error.path.substr(0,check.length)===check) {
var new_error = $extend({},error);
new_error.path = self.path+new_error.path.substr(check.length);
new_errors.push(new_error);
}
});
editor.showValidationErrors(new_errors);
});
}
else {
$each(this.editors,function(type,editor) {
if(!editor) return;
editor.showValidationErrors(errors);
});
}
}
});
// Enum Editor (used for objects and arrays with enumerated values)
JSONEditor.defaults.editors["enum"] = JSONEditor.AbstractEditor.extend({
getNumColumns: function() {
return 4;
},
build: function() {
var container = this.container;
this.title = this.header = this.label = this.theme.getFormInputLabel(this.getTitle());
this.container.appendChild(this.title);
this.options.enum_titles = this.options.enum_titles || [];
this["enum"] = this.schema["enum"];
this.selected = 0;
this.select_options = [];
this.html_values = [];
var self = this;
for(var i=0; i<this["enum"].length; i++) {
this.select_options[i] = this.options.enum_titles[i] || "Value "+(i+1);
this.html_values[i] = this.getHTML(this["enum"][i]);
}
// Switcher
this.switcher = this.theme.getSwitcher(this.select_options);
this.container.appendChild(this.switcher);
// Display area
this.display_area = this.theme.getIndentedPanel();
this.container.appendChild(this.display_area);
if(this.options.hide_display) this.display_area.style.display = "none";
this.switcher.addEventListener('change',function() {
self.selected = self.select_options.indexOf(this.value);
self.value = self["enum"][self.selected];
self.refreshValue();
self.onChange(true);
});
this.value = this["enum"][0];
this.refreshValue();
if(this["enum"].length === 1) this.switcher.style.display = 'none';
},
refreshValue: function() {
var self = this;
self.selected = -1;
var stringified = JSON.stringify(this.value);
$each(this["enum"], function(i, el) {
if(stringified === JSON.stringify(el)) {
self.selected = i;
return false;
}
});
if(self.selected<0) {
self.setValue(self["enum"][0]);
return;
}
this.switcher.value = this.select_options[this.selected];
this.display_area.innerHTML = this.html_values[this.selected];
},
enable: function() {
if(!this.always_disabled) this.switcher.disabled = false;
this._super();
},
disable: function() {
this.switcher.disabled = true;
this._super();
},
getHTML: function(el) {
var self = this;
if(el === null) {
return '<em>null</em>';
}
// Array or Object
else if(typeof el === "object") {
// TODO: use theme
var ret = '';
$each(el,function(i,child) {
var html = self.getHTML(child);
// Add the keys to object children
if(!(Array.isArray(el))) {
// TODO: use theme
html = '<div><em>'+i+'</em>: '+html+'</div>';
}
// TODO: use theme
ret += '<li>'+html+'</li>';
});
if(Array.isArray(el)) ret = '<ol>'+ret+'</ol>';
else ret = "<ul style='margin-top:0;margin-bottom:0;padding-top:0;padding-bottom:0;'>"+ret+'</ul>';
return ret;
}
// Boolean
else if(typeof el === "boolean") {
return el? 'true' : 'false';
}
// String
else if(typeof el === "string") {
return el.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
}
// Number
else {
return el;
}
},
setValue: function(val) {
if(this.value !== val) {
this.value = val;
this.refreshValue();
this.onChange();
}
},
destroy: function() {
if(this.display_area && this.display_area.parentNode) this.display_area.parentNode.removeChild(this.display_area);
if(this.title && this.title.parentNode) this.title.parentNode.removeChild(this.title);
if(this.switcher && this.switcher.parentNode) this.switcher.parentNode.removeChild(this.switcher);
this._super();
}
});
JSONEditor.defaults.editors.select = JSONEditor.AbstractEditor.extend({
setValue: function(value,initial) {
value = this.typecast(value||'');
//Sanitize value before setting it
var sanitized = value;
if(this.enum_values.indexOf(sanitized) < 0) {
sanitized = this.enum_values[0];
}
if(this.value === sanitized) {
return;
}
this.input.value = this.enum_options[this.enum_values.indexOf(sanitized)];
if(this.select2) this.select2.select2('val',this.input.value);
this.value = sanitized;
this.onChange();
},
register: function() {
this._super();
if(!this.input) return;
this.input.setAttribute('id',this.formname);
},
unregister: function() {
this._super();
if(!this.input) return;
this.input.removeAttribute('id');
},
getNumColumns: function() {
if(!this.enum_options) return 3;
var longest_text = this.getTitle().length;
for(var i=0; i<this.enum_options.length; i++) {
longest_text = Math.max(longest_text,this.enum_options[i].length+4);
}
return Math.min(12,Math.max(longest_text/7,2));
},
typecast: function(value) {
if(this.schema.type === "boolean") {
return !!value;
}
else if(this.schema.type === "number") {
return 1*value;
}
else if(this.schema.type === "integer") {
return Math.floor(value*1);
}
else {
return ""+value;
}
},
getValue: function() {
return this.value;
},
preBuild: function() {
var self = this;
this.input_type = 'select';
this.enum_options = [];
this.enum_values = [];
this.enum_display = [];
var i;
// Enum options enumerated
if(this.schema["enum"]) {
var display = this.schema.options && this.schema.options.enum_titles || [];
$each(this.schema["enum"],function(i,option) {
self.enum_options[i] = ""+option;
self.enum_display[i] = ""+(display[i] || option);
self.enum_values[i] = self.typecast(option);
});
if(!this.isRequired()){
self.enum_display.unshift(' ');
self.enum_options.unshift('undefined');
self.enum_values.unshift(undefined);
}
}
// Boolean
else if(this.schema.type === "boolean") {
self.enum_display = this.schema.options && this.schema.options.enum_titles || ['true','false'];
self.enum_options = ['1',''];
self.enum_values = [true,false];
if(!this.isRequired()){
self.enum_display.unshift(' ');
self.enum_options.unshift('undefined');
self.enum_values.unshift(undefined);
}
}
// Dynamic Enum
else if(this.schema.enumSource) {
this.enumSource = [];
this.enum_display = [];
this.enum_options = [];
this.enum_values = [];
// Shortcut declaration for using a single array
if(!(Array.isArray(this.schema.enumSource))) {
if(this.schema.enumValue) {
this.enumSource = [
{
source: this.schema.enumSource,
value: this.schema.enumValue
}
];
}
else {
this.enumSource = [
{
source: this.schema.enumSource
}
];
}
}
else {
for(i=0; i<this.schema.enumSource.length; i++) {
// Shorthand for watched variable
if(typeof this.schema.enumSource[i] === "string") {
this.enumSource[i] = {
source: this.schema.enumSource[i]
};
}
// Make a copy of the schema
else if(!(Array.isArray(this.schema.enumSource[i]))) {
this.enumSource[i] = $extend({},this.schema.enumSource[i]);
}
else {
this.enumSource[i] = this.schema.enumSource[i];
}
}
}
// Now, enumSource is an array of sources
// Walk through this array and fix up the values
for(i=0; i<this.enumSource.length; i++) {
if(this.enumSource[i].value) {
this.enumSource[i].value = this.jsoneditor.compileTemplate(this.enumSource[i].value, this.template_engine);
}
if(this.enumSource[i].title) {
this.enumSource[i].title = this.jsoneditor.compileTemplate(this.enumSource[i].title, this.template_engine);
}
if(this.enumSource[i].filter) {
this.enumSource[i].filter = this.jsoneditor.compileTemplate(this.enumSource[i].filter, this.template_engine);
}
}
}
// Other, not supported
else {
//throw "'select' editor requires the enum property to be set.";
}
},
build: function() {
var self = this;
if(!this.options.compact) this.header = this.label = this.theme.getFormInputLabel(this.getTitle());
if(this.schema.description) this.description = this.theme.getFormInputDescription(this.schema.description);
if(this.schema.append) this.append = this.theme.getFormInputAppend(this.getAppend());
if(this.options.compact) this.container.className += ' compact';
this.input = this.theme.getSelectInput(this.enum_options);
this.theme.setSelectOptions(this.input,this.enum_options,this.enum_display);
if(this.schema.readOnly || this.schema.readonly) {
this.always_disabled = true;
this.input.disabled = true;
}
this.input.addEventListener('change',function(e) {
e.preventDefault();
e.stopPropagation();
self.onInputChange();
});
if(this.formname)this.label.setAttribute('for',this.formname);
this.control = this.theme.getFormControl(this.label, this.input, this.description);
this.container.appendChild(this.control);
this.value = this.enum_values[0];
},
onInputChange: function() {
var val = this.input.value;
// Store new value and propogate change event
this.value = val;
this.onChange(true);
},
setupSelect2: function() {
// If the Select2 library is loaded use it when we have lots of items
if(window.jQuery && window.jQuery.fn && window.jQuery.fn.select2 && (this.enum_options.length > 2 || (this.enum_options.length && this.enumSource))) {
var options = $extend({},JSONEditor.plugins.select2);
if(this.schema.options && this.schema.options.select2_options) options = $extend(options,this.schema.options.select2_options);
this.select2 = window.jQuery(this.input).select2(options);
var self = this;
this.select2.on('select2-blur',function() {
self.input.value = self.select2.select2('val');
self.onInputChange();
});
this.select2.on('change',function() {
self.input.value = self.select2.select2('val');
self.onInputChange();
});
}
else {
this.select2 = null;
}
},
postBuild: function() {
this._super();
this.theme.afterInputReady(this.input);
this.setupSelect2();
},
onWatchedFieldChange: function() {
var self = this, vars, j;
// If this editor uses a dynamic select box
if(this.enumSource) {
vars = this.getWatchedFieldValues();
var select_options = [];
var select_titles = [];
for(var i=0; i<this.enumSource.length; i++) {
// Constant values
if(Array.isArray(this.enumSource[i])) {
select_options = select_options.concat(this.enumSource[i]);
select_titles = select_titles.concat(this.enumSource[i]);
}
else {
var items = [];
// Static list of items
if(Array.isArray(this.enumSource[i].source)) {
items = this.enumSource[i].source;
// A watched field
} else {
items = vars[this.enumSource[i].source];
}
if(items) {
// Only use a predefined part of the array
if(this.enumSource[i].slice) {
items = Array.prototype.slice.apply(items,this.enumSource[i].slice);
}
// Filter the items
if(this.enumSource[i].filter) {
var new_items = [];
for(j=0; j<items.length; j++) {
if(this.enumSource[i].filter({i:j,item:items[j],watched:vars})) new_items.push(items[j]);
}
items = new_items;
}
var item_titles = [];
var item_values = [];
for(j=0; j<items.length; j++) {
var item = items[j];
// Rendered value
if(this.enumSource[i].value) {
item_values[j] = this.enumSource[i].value({
i: j,
item: item
});
}
// Use value directly
else {
item_values[j] = items[j];
}
// Rendered title
if(this.enumSource[i].title) {
item_titles[j] = this.enumSource[i].title({
i: j,
item: item
});
}
// Use value as the title also
else {
item_titles[j] = item_values[j];
}
}
// TODO: sort
select_options = select_options.concat(item_values);
select_titles = select_titles.concat(item_titles);
}
}
}
var prev_value = this.value;
this.theme.setSelectOptions(this.input, select_options, select_titles);
this.enum_options = select_options;
this.enum_display = select_titles;
this.enum_values = select_options;
if(this.select2) {
this.select2.select2('destroy');
}
// If the previous value is still in the new select options, stick with it
if(select_options.indexOf(prev_value) !== -1) {
this.input.value = prev_value;
this.value = prev_value;
}
// Otherwise, set the value to the first select option
else {
this.input.value = select_options[0];
this.value = select_options[0] || "";
if(this.parent) this.parent.onChildEditorChange(this);
else this.jsoneditor.onChange();
this.jsoneditor.notifyWatchers(this.path);
}
this.setupSelect2();
}
this._super();
},
enable: function() {
if(!this.always_disabled) {
this.input.disabled = false;
if(this.select2) this.select2.select2("enable",true);
}
this._super();
},
disable: function() {
this.input.disabled = true;
if(this.select2) this.select2.select2("enable",false);
this._super();
},
destroy: function() {
if(this.label && this.label.parentNode) this.label.parentNode.removeChild(this.label);
if(this.description && this.description.parentNode) this.description.parentNode.removeChild(this.description);
if(this.input && this.input.parentNode) this.input.parentNode.removeChild(this.input);
if(this.select2) {
this.select2.select2('destroy');
this.select2 = null;
}
this._super();
}
});
JSONEditor.defaults.editors.selectize = JSONEditor.AbstractEditor.extend({
setValue: function(value,initial) {
value = this.typecast(value||'');
// Sanitize value before setting it
var sanitized = value;
if(this.enum_values.indexOf(sanitized) < 0) {
sanitized = this.enum_values[0];
}
if(this.value === sanitized) {
return;
}
this.input.value = this.enum_options[this.enum_values.indexOf(sanitized)];
if(this.selectize) {
this.selectize[0].selectize.addItem(sanitized);
}
this.value = sanitized;
this.onChange();
},
register: function() {
this._super();
if(!this.input) return;
this.input.setAttribute('name',this.formname);
},
unregister: function() {
this._super();
if(!this.input) return;
this.input.removeAttribute('name');
},
getNumColumns: function() {
if(!this.enum_options) return 3;
var longest_text = this.getTitle().length;
for(var i=0; i<this.enum_options.length; i++) {
longest_text = Math.max(longest_text,this.enum_options[i].length+4);
}
return Math.min(12,Math.max(longest_text/7,2));
},
typecast: function(value) {
if(this.schema.type === "boolean") {
return !!value;
}
else if(this.schema.type === "number") {
return 1*value;
}
else if(this.schema.type === "integer") {
return Math.floor(value*1);
}
else {
return ""+value;
}
},
getValue: function() {
return this.value;
},
preBuild: function() {
var self = this;
this.input_type = 'select';
this.enum_options = [];
this.enum_values = [];
this.enum_display = [];
var i;
// Enum options enumerated
if(this.schema.enum) {
var display = this.schema.options && this.schema.options.enum_titles || [];
$each(this.schema.enum,function(i,option) {
self.enum_options[i] = ""+option;
self.enum_display[i] = ""+(display[i] || option);
self.enum_values[i] = self.typecast(option);
});
}
// Boolean
else if(this.schema.type === "boolean") {
self.enum_display = this.schema.options && this.schema.options.enum_titles || ['true','false'];
self.enum_options = ['1','0'];
self.enum_values = [true,false];
}
// Dynamic Enum
else if(this.schema.enumSource) {
this.enumSource = [];
this.enum_display = [];
this.enum_options = [];
this.enum_values = [];
// Shortcut declaration for using a single array
if(!(Array.isArray(this.schema.enumSource))) {
if(this.schema.enumValue) {
this.enumSource = [
{
source: this.schema.enumSource,
value: this.schema.enumValue
}
];
}
else {
this.enumSource = [
{
source: this.schema.enumSource
}
];
}
}
else {
for(i=0; i<this.schema.enumSource.length; i++) {
// Shorthand for watched variable
if(typeof this.schema.enumSource[i] === "string") {
this.enumSource[i] = {
source: this.schema.enumSource[i]
};
}
// Make a copy of the schema
else if(!(Array.isArray(this.schema.enumSource[i]))) {
this.enumSource[i] = $extend({},this.schema.enumSource[i]);
}
else {
this.enumSource[i] = this.schema.enumSource[i];
}
}
}
// Now, enumSource is an array of sources
// Walk through this array and fix up the values
for(i=0; i<this.enumSource.length; i++) {
if(this.enumSource[i].value) {
this.enumSource[i].value = this.jsoneditor.compileTemplate(this.enumSource[i].value, this.template_engine);
}
if(this.enumSource[i].title) {
this.enumSource[i].title = this.jsoneditor.compileTemplate(this.enumSource[i].title, this.template_engine);
}
if(this.enumSource[i].filter) {
this.enumSource[i].filter = this.jsoneditor.compileTemplate(this.enumSource[i].filter, this.template_engine);
}
}
}
// Other, not supported
else {
throw "'select' editor requires the enum property to be set.";
}
},
build: function() {
var self = this;
if(!this.options.compact) this.header = this.label = this.theme.getFormInputLabel(this.getTitle());
if(this.schema.description) this.description = this.theme.getFormInputDescription(this.schema.description);
if(this.schema.append) this.append = this.theme.getFormInputAppend(this.getAppend());
if(this.options.compact) this.container.className += ' compact';
this.input = this.theme.getSelectInput(this.enum_options);
this.theme.setSelectOptions(this.input,this.enum_options,this.enum_display);
if(this.schema.readOnly || this.schema.readonly) {
this.always_disabled = true;
this.input.disabled = true;
}
this.input.addEventListener('change',function(e) {
e.preventDefault();
e.stopPropagation();
self.onInputChange();
});
this.control = this.theme.getFormControl(this.label, this.input, this.description);
this.container.appendChild(this.control);
this.value = this.enum_values[0];
},
onInputChange: function() {
var val = this.input.value;
var sanitized = val;
if(this.enum_options.indexOf(val) === -1) {
sanitized = this.enum_options[0];
}
this.value = this.enum_values[this.enum_options.indexOf(val)];
this.onChange(true);
},
setupSelectize: function() {
// If the Selectize library is loaded use it when we have lots of items
var self = this;
if(window.jQuery && window.jQuery.fn && window.jQuery.fn.selectize && (this.enum_options.length >= 2 || (this.enum_options.length && this.enumSource))) {
var options = $extend({},JSONEditor.plugins.selectize);
if(this.schema.options && this.schema.options.selectize_options) options = $extend(options,this.schema.options.selectize_options);
this.selectize = window.jQuery(this.input).selectize($extend(options,
{
create: true,
onChange : function() {
self.onInputChange();
}
}));
}
else {
this.selectize = null;
}
},
postBuild: function() {
this._super();
this.theme.afterInputReady(this.input);
this.setupSelectize();
},
onWatchedFieldChange: function() {
var self = this, vars, j;
// If this editor uses a dynamic select box
if(this.enumSource) {
vars = this.getWatchedFieldValues();
var select_options = [];
var select_titles = [];
for(var i=0; i<this.enumSource.length; i++) {
// Constant values
if(Array.isArray(this.enumSource[i])) {
select_options = select_options.concat(this.enumSource[i]);
select_titles = select_titles.concat(this.enumSource[i]);
}
// A watched field
else if(vars[this.enumSource[i].source]) {
var items = vars[this.enumSource[i].source];
// Only use a predefined part of the array
if(this.enumSource[i].slice) {
items = Array.prototype.slice.apply(items,this.enumSource[i].slice);
}
// Filter the items
if(this.enumSource[i].filter) {
var new_items = [];
for(j=0; j<items.length; j++) {
if(this.enumSource[i].filter({i:j,item:items[j]})) new_items.push(items[j]);
}
items = new_items;
}
var item_titles = [];
var item_values = [];
for(j=0; j<items.length; j++) {
var item = items[j];
// Rendered value
if(this.enumSource[i].value) {
item_values[j] = this.enumSource[i].value({
i: j,
item: item
});
}
// Use value directly
else {
item_values[j] = items[j];
}
// Rendered title
if(this.enumSource[i].title) {
item_titles[j] = this.enumSource[i].title({
i: j,
item: item
});
}
// Use value as the title also
else {
item_titles[j] = item_values[j];
}
}
// TODO: sort
select_options = select_options.concat(item_values);
select_titles = select_titles.concat(item_titles);
}
}
var prev_value = this.value;
this.theme.setSelectOptions(this.input, select_options, select_titles);
this.enum_options = select_options;
this.enum_display = select_titles;
this.enum_values = select_options;
// If the previous value is still in the new select options, stick with it
if(select_options.indexOf(prev_value) !== -1) {
this.input.value = prev_value;
this.value = prev_value;
}
// Otherwise, set the value to the first select option
else {
this.input.value = select_options[0];
this.value = select_options[0] || "";
if(this.parent) this.parent.onChildEditorChange(this);
else this.jsoneditor.onChange();
this.jsoneditor.notifyWatchers(this.path);
}
if(this.selectize) {
// Update the Selectize options
this.updateSelectizeOptions(select_options);
}
else {
this.setupSelectize();
}
this._super();
}
},
updateSelectizeOptions: function(select_options) {
var selectized = this.selectize[0].selectize,
self = this;
selectized.off();
selectized.clearOptions();
for(var n in select_options) {
selectized.addOption({value:select_options[n],text:select_options[n]});
}
selectized.addItem(this.value);
selectized.on('change',function() {
self.onInputChange();
});
},
enable: function() {
if(!this.always_disabled) {
this.input.disabled = false;
if(this.selectize) {
this.selectize[0].selectize.unlock();
}
}
this._super();
},
disable: function() {
this.input.disabled = true;
if(this.selectize) {
this.selectize[0].selectize.lock();
}
this._super();
},
destroy: function() {
if(this.label && this.label.parentNode) this.label.parentNode.removeChild(this.label);
if(this.description && this.description.parentNode) this.description.parentNode.removeChild(this.description);
if(this.input && this.input.parentNode) this.input.parentNode.removeChild(this.input);
if(this.selectize) {
this.selectize[0].selectize.destroy();
this.selectize = null;
}
this._super();
}
});
JSONEditor.defaults.editors.multiselect = JSONEditor.AbstractEditor.extend({
preBuild: function() {
this._super();
var i;
this.select_options = {};
this.select_values = {};
var items_schema = this.jsoneditor.expandRefs(this.schema.items || {});
var e = items_schema["enum"] || [];
var t = items_schema.options? items_schema.options.enum_titles || [] : [];
this.option_keys = [];
this.option_titles = [];
for(i=0; i<e.length; i++) {
// If the sanitized value is different from the enum value, don't include it
if(this.sanitize(e[i]) !== e[i]) continue;
this.option_keys.push(e[i]+"");
this.option_titles.push((t[i]||e[i])+"");
this.select_values[e[i]+""] = e[i];
}
},
build: function() {
var self = this, i;
if(!this.options.compact) this.header = this.label = this.theme.getFormInputLabel(this.getTitle());
if(this.schema.description) this.description = this.theme.getFormInputDescription(this.schema.description);
if(this.schema.append) this.append = this.theme.getFormInputAppend(this.getAppend());
if((!this.schema.format && this.option_keys.length < 8) || this.schema.format === "checkbox") {
this.input_type = 'checkboxes';
this.inputs = {};
this.controls = {};
for(i=0; i<this.option_keys.length; i++) {
this.inputs[this.option_keys[i]] = this.theme.getCheckbox();
this.select_options[this.option_keys[i]] = this.inputs[this.option_keys[i]];
var label = this.theme.getCheckboxLabel(this.option_titles[i]);
this.controls[this.option_keys[i]] = this.theme.getFormControl(label, this.inputs[this.option_keys[i]]);
}
this.control = this.theme.getMultiCheckboxHolder(this.controls,this.label,this.description);
}
else {
this.input_type = 'select';
this.input = this.theme.getSelectInput(this.option_keys);
this.theme.setSelectOptions(this.input,this.option_keys,this.option_titles);
this.input.multiple = true;
this.input.size = Math.min(10,this.option_keys.length);
for(i=0; i<this.option_keys.length; i++) {
this.select_options[this.option_keys[i]] = this.input.children[i];
}
if(this.schema.readOnly || this.schema.readonly) {
this.always_disabled = true;
this.input.disabled = true;
}
this.control = this.theme.getFormControl(this.label, this.input, this.description);
}
this.container.appendChild(this.control);
this.control.addEventListener('change',function(e) {
e.preventDefault();
e.stopPropagation();
var new_value = [];
for(i = 0; i<self.option_keys.length; i++) {
if(self.select_options[self.option_keys[i]].selected || self.select_options[self.option_keys[i]].checked) new_value.push(self.select_values[self.option_keys[i]]);
}
self.updateValue(new_value);
self.onChange(true);
});
},
setValue: function(value, initial) {
var i;
value = value || [];
if(typeof value !== "object") value = [value];
else if(!(Array.isArray(value))) value = [];
// Make sure we are dealing with an array of strings so we can check for strict equality
for(i=0; i<value.length; i++) {
if(typeof value[i] !== "string") value[i] += "";
}
// Update selected status of options
for(i in this.select_options) {
if(!this.select_options.hasOwnProperty(i)) continue;
this.select_options[i][this.input_type === "select"? "selected" : "checked"] = (value.indexOf(i) !== -1);
}
this.updateValue(value);
this.onChange();
},
setupSelect2: function() {
if(window.jQuery && window.jQuery.fn && window.jQuery.fn.select2) {
var options = window.jQuery.extend({},JSONEditor.plugins.select2);
if(this.schema.options && this.schema.options.select2_options) options = $extend(options,this.schema.options.select2_options);
this.select2 = window.jQuery(this.input).select2(options);
var self = this;
this.select2.on('select2-blur',function() {
var val =self.select2.select2('val');
self.value = val;
self.onChange(true);
});
}
else {
this.select2 = null;
}
},
onInputChange: function() {
this.value = this.input.value;
this.onChange(true);
},
postBuild: function() {
this._super();
this.setupSelect2();
},
register: function() {
this._super();
if(!this.input) return;
this.input.setAttribute('name',this.formname);
},
unregister: function() {
this._super();
if(!this.input) return;
this.input.removeAttribute('name');
},
getNumColumns: function() {
var longest_text = this.getTitle().length;
for(var i in this.select_values) {
if(!this.select_values.hasOwnProperty(i)) continue;
longest_text = Math.max(longest_text,(this.select_values[i]+"").length+4);
}
return Math.min(12,Math.max(longest_text/7,2));
},
updateValue: function(value) {
var changed = false;
var new_value = [];
for(var i=0; i<value.length; i++) {
if(!this.select_options[value[i]+""]) {
changed = true;
continue;
}
var sanitized = this.sanitize(this.select_values[value[i]]);
new_value.push(sanitized);
if(sanitized !== value[i]) changed = true;
}
this.value = new_value;
if(this.select2) this.select2.select2('val',this.value);
return changed;
},
sanitize: function(value) {
if(this.schema.items.type === "number") {
return 1*value;
}
else if(this.schema.items.type === "integer") {
return Math.floor(value*1);
}
else {
return ""+value;
}
},
enable: function() {
if(!this.always_disabled) {
if(this.input) {
this.input.disabled = false;
}
else if(this.inputs) {
for(var i in this.inputs) {
if(!this.inputs.hasOwnProperty(i)) continue;
this.inputs[i].disabled = false;
}
}
if(this.select2) this.select2.select2("enable",true);
}
this._super();
},
disable: function() {
if(this.input) {
this.input.disabled = true;
}
else if(this.inputs) {
for(var i in this.inputs) {
if(!this.inputs.hasOwnProperty(i)) continue;
this.inputs[i].disabled = true;
}
}
if(this.select2) this.select2.select2("enable",false);
this._super();
},
destroy: function() {
if(this.select2) {
this.select2.select2('destroy');
this.select2 = null;
}
this._super();
}
});
JSONEditor.defaults.editors.base64 = JSONEditor.AbstractEditor.extend({
getNumColumns: function() {
return 4;
},
build: function() {
var self = this;
this.title = this.header = this.label = this.theme.getFormInputLabel(this.getTitle());
// Input that holds the base64 string
this.input = this.theme.getFormInputField('hidden');
this.container.appendChild(this.input);
// Don't show uploader if this is readonly
if(!this.schema.readOnly && !this.schema.readonly) {
if(!window.FileReader) throw "FileReader required for base64 editor";
// File uploader
this.uploader = this.theme.getFormInputField('file');
this.uploader.addEventListener('change',function(e) {
e.preventDefault();
e.stopPropagation();
if(this.files && this.files.length) {
var fr = new FileReader();
fr.onload = function(evt) {
self.value = evt.target.result;
self.refreshPreview();
self.onChange(true);
fr = null;
};
fr.readAsDataURL(this.files[0]);
}
});
}
this.preview = this.theme.getFormInputDescription(this.schema.description);
this.container.appendChild(this.preview);
this.control = this.theme.getFormControl(this.label, this.uploader||this.input, this.preview);
this.container.appendChild(this.control);
},
refreshPreview: function() {
if(this.last_preview === this.value) return;
this.last_preview = this.value;
this.preview.innerHTML = '';
if(!this.value) return;
var mime = this.value.match(/^data:([^;,]+)[;,]/);
if(mime) mime = mime[1];
if(!mime) {
this.preview.innerHTML = '<em>Invalid data URI</em>';
}
else {
this.preview.innerHTML = '<strong>Type:</strong> '+mime+', <strong>Size:</strong> '+Math.floor((this.value.length-this.value.split(',')[0].length-1)/1.33333)+' bytes';
if(mime.substr(0,5)==="image") {
this.preview.innerHTML += '<br>';
var img = document.createElement('img');
img.style.maxWidth = '100%';
img.style.maxHeight = '100px';
img.src = this.value;
this.preview.appendChild(img);
}
}
},
enable: function() {
if(this.uploader) this.uploader.disabled = false;
this._super();
},
disable: function() {
if(this.uploader) this.uploader.disabled = true;
this._super();
},
setValue: function(val) {
if(this.value !== val) {
this.value = val;
this.input.value = this.value;
this.refreshPreview();
this.onChange();
}
},
destroy: function() {
if(this.preview && this.preview.parentNode) this.preview.parentNode.removeChild(this.preview);
if(this.title && this.title.parentNode) this.title.parentNode.removeChild(this.title);
if(this.input && this.input.parentNode) this.input.parentNode.removeChild(this.input);
if(this.uploader && this.uploader.parentNode) this.uploader.parentNode.removeChild(this.uploader);
this._super();
}
});
JSONEditor.defaults.editors.upload = JSONEditor.AbstractEditor.extend({
getNumColumns: function() {
return 4;
},
build: function() {
var self = this;
this.title = this.header = this.label = this.theme.getFormInputLabel(this.getTitle());
// Input that holds the base64 string
this.input = this.theme.getFormInputField('hidden');
this.container.appendChild(this.input);
// Don't show uploader if this is readonly
if(!this.schema.readOnly && !this.schema.readonly) {
if(!this.jsoneditor.options.upload) throw "Upload handler required for upload editor";
// File uploader
this.uploader = this.theme.getFormInputField('file');
this.uploader.addEventListener('change',function(e) {
e.preventDefault();
e.stopPropagation();
if(this.files && this.files.length) {
var fr = new FileReader();
fr.onload = function(evt) {
self.preview_value = evt.target.result;
self.refreshPreview();
self.onChange(true);
fr = null;
};
fr.readAsDataURL(this.files[0]);
}
});
}
var description = this.schema.description;
if (!description) description = '';
this.preview = this.theme.getFormInputDescription(description);
this.container.appendChild(this.preview);
this.control = this.theme.getFormControl(this.label, this.uploader||this.input, this.preview);
this.container.appendChild(this.control);
},
refreshPreview: function() {
if(this.last_preview === this.preview_value) return;
this.last_preview = this.preview_value;
this.preview.innerHTML = '';
if(!this.preview_value) return;
var self = this;
var file = this.uploader.files[0];
var uploadButton = this.getButton('Upload', 'upload', 'Upload');
this.preview.appendChild(uploadButton);
uploadButton.addEventListener('click',function(event) {
event.preventDefault();
uploadButton.setAttribute("disabled", "disabled");
self.theme.removeInputError(self.uploader);
if (self.theme.getProgressBar) {
self.progressBar = self.theme.getProgressBar();
self.preview.appendChild(self.progressBar);
}
self.jsoneditor.options.upload(self.path, file, {
success: function(url) {
self.setValue(url);
if(self.parent) self.parent.onChildEditorChange(self);
else self.jsoneditor.onChange();
if (self.progressBar) self.preview.removeChild(self.progressBar);
uploadButton.removeAttribute("disabled");
},
failure: function(error) {
self.theme.addInputError(self.uploader, error);
if (self.progressBar) self.preview.removeChild(self.progressBar);
uploadButton.removeAttribute("disabled");
},
updateProgress: function(progress) {
if (self.progressBar) {
if (progress) self.theme.updateProgressBar(self.progressBar, progress);
else self.theme.updateProgressBarUnknown(self.progressBar);
}
}
});
});
if(this.jsoneditor.options.auto_upload || this.schema.options.auto_upload) {
uploadButton.dispatchEvent(new MouseEvent('click'));
this.preview.removeChild(uploadButton);
}
},
enable: function() {
if(this.uploader) this.uploader.disabled = false;
this._super();
},
disable: function() {
if(this.uploader) this.uploader.disabled = true;
this._super();
},
setValue: function(val) {
if(this.value !== val) {
this.value = val;
this.input.value = this.value;
this.onChange();
}
},
destroy: function() {
if(this.preview && this.preview.parentNode) this.preview.parentNode.removeChild(this.preview);
if(this.title && this.title.parentNode) this.title.parentNode.removeChild(this.title);
if(this.input && this.input.parentNode) this.input.parentNode.removeChild(this.input);
if(this.uploader && this.uploader.parentNode) this.uploader.parentNode.removeChild(this.uploader);
this._super();
}
});
JSONEditor.defaults.editors.checkbox = JSONEditor.AbstractEditor.extend({
setValue: function(value,initial) {
this.value = !!value;
this.input.checked = this.value;
this.onChange();
},
register: function() {
this._super();
if(!this.input) return;
this.input.setAttribute('name',this.formname);
},
unregister: function() {
this._super();
if(!this.input) return;
this.input.removeAttribute('name');
},
getNumColumns: function() {
return Math.min(12,Math.max(this.getTitle().length/7,2));
},
build: function() {
var self = this;
if(!this.options.compact) {
this.label = this.header = this.theme.getCheckboxLabel(this.getTitle());
}
if(this.schema.description) this.description = this.theme.getFormInputDescription(this.schema.description);
if(this.options.compact) this.container.className += ' compact';
this.input = this.theme.getCheckbox();
if(this.formname)this.label.setAttribute('for',this.formname);
if(this.formname)this.input.setAttribute('id',this.formname);
this.control = this.theme.getFormControl(this.label, this.input, this.description);
if(this.schema.readOnly || this.schema.readonly) {
this.always_disabled = true;
this.input.disabled = true;
}
this.input.addEventListener('change',function(e) {
e.preventDefault();
e.stopPropagation();
self.value = this.checked;
self.onChange(true);
});
this.container.appendChild(this.control);
if (this.input.id.endsWith('_enable'))
this.container.appendChild(document.createElement('hr'));
},
enable: function() {
if(!this.always_disabled) {
this.input.disabled = false;
}
this._super();
},
disable: function() {
this.input.disabled = true;
this._super();
},
destroy: function() {
if(this.label && this.label.parentNode) this.label.parentNode.removeChild(this.label);
if(this.description && this.description.parentNode) this.description.parentNode.removeChild(this.description);
if(this.input && this.input.parentNode) this.input.parentNode.removeChild(this.input);
this._super();
}
});
JSONEditor.defaults.editors.arraySelectize = JSONEditor.AbstractEditor.extend({
build: function() {
this.title = this.theme.getFormInputLabel(this.getTitle());
this.title_controls = this.theme.getHeaderButtonHolder();
this.title.appendChild(this.title_controls);
this.error_holder = document.createElement('div');
if(this.schema.description) {
this.description = this.theme.getDescription(this.schema.description);
}
if(this.schema.append) {
this.append = this.theme.getAppend(this.getAppend());
}
this.input = document.createElement('select');
this.input.setAttribute('multiple', 'multiple');
var group = this.theme.getFormControl(this.title, this.input, this.description);
this.container.appendChild(group);
this.container.appendChild(this.error_holder);
window.jQuery(this.input).selectize({
delimiter: false,
createOnBlur: true,
create: true
});
},
postBuild: function() {
var self = this;
this.input.selectize.on('change', function(event) {
self.refreshValue();
self.onChange(true);
});
},
destroy: function() {
this.empty(true);
if(this.title && this.title.parentNode) this.title.parentNode.removeChild(this.title);
if(this.description && this.description.parentNode) this.description.parentNode.removeChild(this.description);
if(this.input && this.input.parentNode) this.input.parentNode.removeChild(this.input);
this._super();
},
empty: function(hard) {},
setValue: function(value, initial) {
var self = this;
// Update the array's value, adding/removing rows when necessary
value = value || [];
if(!(Array.isArray(value))) value = [value];
this.input.selectize.clearOptions();
this.input.selectize.clear(true);
value.forEach(function(item) {
self.input.selectize.addOption({text: item, value: item});
});
this.input.selectize.setValue(value);
this.refreshValue(initial);
},
refreshValue: function(force) {
this.value = this.input.selectize.getValue();
},
showValidationErrors: function(errors) {
var self = this;
// Get all the errors that pertain to this editor
var my_errors = [];
var other_errors = [];
$each(errors, function(i,error) {
if(error.path === self.path) {
my_errors.push(error);
}
else {
other_errors.push(error);
}
});
// Show errors for this editor
if(this.error_holder) {
if(my_errors.length) {
var message = [];
this.error_holder.innerHTML = '';
this.error_holder.style.display = '';
$each(my_errors, function(i,error) {
self.error_holder.appendChild(self.theme.getErrorMessage(error.message));
});
}
// Hide error area
else {
this.error_holder.style.display = 'none';
}
}
}
});
// colorpicker creation and handling, build on top of strings editor
JSONEditor.defaults.editors.colorPicker = JSONEditor.defaults.editors.string.extend({
getValue: function() {
if ($(this.input).data("colorpicker") !== undefined) {
var color = $(this.input).data('colorpicker').color.toRGB();
return [color.r,color.g, color.b];
}
else {
return [0,0,0];
}
},
setValue: function(val) {
function rgb2hex(rgb)
{
return "#" +
("0" + parseInt(rgb[0],10).toString(16)).slice(-2) +
("0" + parseInt(rgb[1],10).toString(16)).slice(-2) +
("0" + parseInt(rgb[2],10).toString(16)).slice(-2);
}
$(this.input).colorpicker('updateInput', 'rgb('+val+')');
$(this.input).colorpicker('updateData', val);
$(this.input).colorpicker('updatePicker', rgb2hex(val));
$(this.input).colorpicker('updateComponent', 'rgb('+val+')');
},
build: function() {
this._super();
var myinput = this;
$(myinput.input).parent().attr("class", $(myinput.input).parent().attr('class') + " colorpicker-element input-group");
$(myinput.input).append("<span class='input-group-addon' id='event_catcher'><i></i></span>");
$(myinput.input).colorpicker({
format: 'rgb',
customClass: 'colorpicker-2x',
sliders: {
saturation: {
maxLeft: 200,
maxTop: 200
},
hue: {
maxTop: 200
},
},
})
$("#event_catcher").detach().insertAfter(myinput.input);
$("#event_catcher").attr("id", "selector");
$(this.input).colorpicker().on('changeColor', function(e) {
$(myinput).val(e.color.toRGB()).change();
});
},
destroy: function() {
$(this.input).colorpicker('destroy');
}
});
// colorpickerRGBA creation and handling, build on top of strings editor
JSONEditor.defaults.editors.colorPickerRGBA = JSONEditor.defaults.editors.string.extend({
getValue: function() {
if ($(this.input).data("colorpicker") !== undefined) {
var color = $(this.input).data('colorpicker').color.toRGB();
return [color.r,color.g, color.b, color.a];
}
else {
return [0,0,0,1];
}
},
setValue: function(val) {
$(this.input).colorpicker('updateInput', 'rgba('+val+')');
$(this.input).colorpicker('updateData', val);
// $(this.input).colorpicker('updatePicker', rgb2hex(val));
$(this.input).colorpicker('updateComponent', 'rgba('+val+')');
},
build: function() {
this._super();
var myinput = this;
$(myinput.input).parent().attr("class", $(myinput.input).parent().attr('class') + " colorpicker-element input-group");
$(myinput.input).append("<span class='input-group-addon' id='event_catcher'><i></i></span>");
$(myinput.input).colorpicker({
format: 'rgba',
customClass: 'colorpicker-2x',
sliders: {
saturation: {
maxLeft: 200,
maxTop: 200
},
hue: {
maxTop: 200
},
alpha: {
maxTop: 200
}
},
})
$("#event_catcher").detach().insertAfter(myinput.input);
$("#event_catcher").attr("id", "selector");
$(this.input).colorpicker().on('changeColor', function(e) {
$(myinput).val(e.color.toRGB()).change();
});
},
destroy: function() {
$(this.input).colorpicker('destroy');
}
});
var matchKey = (function () {
var elem = document.documentElement;
if (elem.matches) return 'matches';
else if (elem.webkitMatchesSelector) return 'webkitMatchesSelector';
else if (elem.mozMatchesSelector) return 'mozMatchesSelector';
else if (elem.msMatchesSelector) return 'msMatchesSelector';
else if (elem.oMatchesSelector) return 'oMatchesSelector';
})();
JSONEditor.AbstractTheme = Class.extend({
getContainer: function() {
return document.createElement('div');
},
getFloatRightLinkHolder: function() {
var el = document.createElement('div');
el.style = el.style || {};
el.style.cssFloat = 'right';
el.style.marginLeft = '10px';
return el;
},
getModal: function() {
var el = document.createElement('div');
el.style.backgroundColor = 'white';
el.style.border = '1px solid black';
el.style.boxShadow = '3px 3px black';
el.style.position = 'absolute';
el.style.zIndex = '10';
el.style.display = 'none';
return el;
},
getGridContainer: function() {
var el = document.createElement('div');
return el;
},
getGridRow: function() {
var el = document.createElement('div');
el.className = 'row';
return el;
},
getGridColumn: function() {
var el = document.createElement('div');
return el;
},
setGridColumnSize: function(el,size) {
},
getLink: function(text) {
var el = document.createElement('a');
el.setAttribute('href','#');
el.appendChild(document.createTextNode(text));
return el;
},
disableHeader: function(header) {
header.style.color = '#ccc';
},
disableLabel: function(label) {
label.style.color = '#ccc';
},
enableHeader: function(header) {
header.style.color = '';
},
enableLabel: function(label) {
label.style.color = '';
},
getFormInputLabel: function(text) {
var el = document.createElement('label');
el.appendChild(document.createTextNode(text));
return el;
},
getCheckboxLabel: function(text) {
var el = this.getFormInputLabel(text);
el.style.fontWeight = 'bold';
return el;
},
getHeader: function(text) {
var el = document.createElement('h4');
if(text.innerHTML == ''){
text.style.display = 'none';
return text;
}
else if(typeof text === "string") {
el.textContent = text;
}
else {
el.appendChild(text);
}
return el;
},
getCheckbox: function() {
var el = this.getFormInputField('checkbox');
el.style.display = 'inline-block';
el.style.width = 'auto';
return el;
},
getMultiCheckboxHolder: function(controls,label,description) {
var el = document.createElement('div');
if(label) {
label.style.display = 'block';
el.appendChild(label);
}
for(var i in controls) {
if(!controls.hasOwnProperty(i)) continue;
controls[i].style.display = 'inline-block';
controls[i].style.marginRight = '20px';
el.appendChild(controls[i]);
}
if(description) el.appendChild(description);
return el;
},
getSelectInput: function(options) {
var select = document.createElement('select');
if(options) this.setSelectOptions(select, options);
return select;
},
getSwitcher: function(options) {
var switcher = this.getSelectInput(options);
switcher.style.backgroundColor = 'transparent';
switcher.style.display = 'inline-block';
switcher.style.fontStyle = 'italic';
switcher.style.fontWeight = 'normal';
switcher.style.height = 'auto';
switcher.style.marginBottom = 0;
switcher.style.marginLeft = '5px';
switcher.style.padding = '0 0 0 3px';
switcher.style.width = 'auto';
return switcher;
},
getSwitcherOptions: function(switcher) {
return switcher.getElementsByTagName('option');
},
setSwitcherOptions: function(switcher, options, titles) {
this.setSelectOptions(switcher, options, titles);
},
setSelectOptions: function(select, options, titles) {
if (typeof options != "undefined")
{
titles = titles || [];
select.innerHTML = '';
for(var i=0; i<options.length; i++) {
var option = document.createElement('option');
option.setAttribute('value',options[i]);
if (typeof titles[i] != 'undefined' && titles[i].startsWith('edt_'))
option.textContent = $.i18n(titles[i]);
else
option.textContent = titles[i] || options[i];
select.appendChild(option);
}
}
},
getTextareaInput: function() {
var el = document.createElement('textarea');
el.style = el.style || {};
el.style.width = '100%';
el.style.height = '300px';
el.style.boxSizing = 'border-box';
return el;
},
getRangeInput: function(min,max,step) {
if (typeof step == "undefined") step = 1;
var el = this.getFormInputField('number');
if (typeof min != "undefined") el.setAttribute('min',min);
if (typeof max != "undefined") el.setAttribute('max',max);
el.setAttribute('step',step);
return el;
},
getFormInputField: function(type) {
var el = document.createElement('input');
el.setAttribute('type',type);
return el;
},
afterInputReady: function(input) {
},
getFormControl: function(label, input, description) {
var el = document.createElement('div');
el.className = 'form-control';
if(label) el.appendChild(label);
if(input.type === 'checkbox') {
label.insertBefore(input,label.firstChild);
}
else {
el.appendChild(input);
}
if(description) el.appendChild(description);
return el;
},
getIndentedPanel: function() {
var el = document.createElement('div');
el.style = el.style || {};
el.style.paddingLeft = '10px';
el.style.marginLeft = '10px';
el.style.borderLeft = '1px solid #ccc';
return el;
},
getChildEditorHolder: function() {
return document.createElement('div');
},
getDescription: function(text) {
var el = document.createElement('p');
el.innerHTML = text;
return el;
},
getAppend: function(text) {
var el = document.createElement('p');
el.innerHTML = text;
return el;
},
getCheckboxDescription: function(text) {
return this.getDescription(text);
},
getFormInputDescription: function(text) {
return this.getDescription(text);
},
getHeaderButtonHolder: function() {
return this.getButtonHolder();
},
getButtonHolder: function() {
return document.createElement('div');
},
getButton: function(text, icon, title) {
var el = document.createElement('button');
el.type = 'button';
this.setButtonText(el,text,icon,title);
return el;
},
setButtonText: function(button, text, icon, title) {
button.innerHTML = '';
if(icon) {
button.appendChild(icon);
button.innerHTML += ' ';
}
button.appendChild(document.createTextNode(text));
if(title) button.setAttribute('title',title);
},
getTable: function() {
return document.createElement('table');
},
getTableRow: function() {
return document.createElement('tr');
},
getTableHead: function() {
return document.createElement('thead');
},
getTableBody: function() {
return document.createElement('tbody');
},
getTableHeaderCell: function(text) {
var el = document.createElement('th');
el.textContent = text;
return el;
},
getTableCell: function() {
var el = document.createElement('td');
return el;
},
getErrorMessage: function(text) {
var el = document.createElement('p');
el.style = el.style || {};
el.style.color = 'red';
el.appendChild(document.createTextNode(text));
return el;
},
addInputError: function(input, text) {
},
removeInputError: function(input) {
},
addTableRowError: function(row) {
},
removeTableRowError: function(row) {
},
getTabHolder: function() {
var el = document.createElement('div');
el.innerHTML = "<div style='float: left; width: 130px;' class='tabs'></div><div class='content' style='margin-left: 130px;'></div><div style='clear:both;'></div>";
return el;
},
applyStyles: function(el,styles) {
el.style = el.style || {};
for(var i in styles) {
if(!styles.hasOwnProperty(i)) continue;
el.style[i] = styles[i];
}
},
closest: function(elem, selector) {
while (elem && elem !== document) {
if (elem[matchKey]) {
if (elem[matchKey](selector)) {
return elem;
} else {
elem = elem.parentNode;
}
}
else {
return false;
}
}
return false;
},
getTab: function(span) {
var el = document.createElement('div');
el.appendChild(span);
el.style = el.style || {};
this.applyStyles(el,{
border: '1px solid #ccc',
borderWidth: '1px 0 1px 1px',
textAlign: 'center',
lineHeight: '30px',
borderRadius: '5px',
borderBottomRightRadius: 0,
borderTopRightRadius: 0,
fontWeight: 'bold',
cursor: 'pointer'
});
return el;
},
getTabContentHolder: function(tab_holder) {
return tab_holder.children[1];
},
getTabContent: function() {
return this.getIndentedPanel();
},
markTabActive: function(tab) {
this.applyStyles(tab,{
opacity: 1,
background: 'white'
});
},
markTabInactive: function(tab) {
this.applyStyles(tab,{
opacity:0.5,
background: ''
});
},
addTab: function(holder, tab) {
holder.children[0].appendChild(tab);
},
getBlockLink: function() {
var link = document.createElement('a');
link.style.display = 'block';
return link;
},
getBlockLinkHolder: function() {
var el = document.createElement('div');
return el;
},
getLinksHolder: function() {
var el = document.createElement('div');
return el;
},
createMediaLink: function(holder,link,media) {
holder.appendChild(link);
media.style.width='100%';
holder.appendChild(media);
},
createImageLink: function(holder,link,image) {
holder.appendChild(link);
link.appendChild(image);
}
});
JSONEditor.defaults.themes.bootstrap3 = JSONEditor.AbstractTheme.extend({
getSelectInput: function(options) {
var el = this._super(options);
el.className += 'form-control';
//el.style.width = 'auto';
return el;
},
setGridColumnSize: function(el,size) {
el.className = 'col-md-'+size;
},
afterInputReady: function(input) {
if(input.controlgroup) return;
input.controlgroup = this.closest(input,'.form-group');
if(this.closest(input,'.compact')) {
input.controlgroup.style.marginBottom = 0;
}
// TODO: use bootstrap slider
},
getTextareaInput: function() {
var el = document.createElement('textarea');
el.className = 'form-control';
return el;
},
getRangeInput: function(min, max, step) {
// TODO: use better slider
return this._super(min, max, step);
},
getFormInputField: function(type) {
var el = this._super(type);
if(type !== 'checkbox') {
el.className += 'form-control';
}
return el;
},
getFormControl: function(label, input, description, append, placeholder) {
var group = document.createElement('div');
var subgroup = document.createElement('div');
if(placeholder)
input.setAttribute('placeholder',placeholder);
if (input.type === 'checkbox'){
var helplabel = document.createElement("label")
group.className += ' form-group';
group.style.minHeight = "30px";
label.className += ' col-form-label col-sm-5 col-md-3 col-lg-5 col-xxl-4';
label.style.fontWeight = "bold";
group.appendChild(label);
group.appendChild(subgroup);
subgroup.className += 'checkbox col-sm-7 col-md-9 col-lg-7 col-xxl-8';
subgroup.style.marginTop = "0px";
subgroup.appendChild(input);
subgroup.appendChild(helplabel);
if (input.id.endsWith('_enable'))
subgroup.className += ' checkbox-success';
}
else if (append){
group.className += ' form-group';
if(label) {
label.className += ' col-form-label col-sm-5 col-md-3 col-lg-5 col-xxl-4';
group.appendChild(label);
}
group.appendChild(subgroup);
subgroup.className += ' col-sm-7 col-md-9 col-lg-7 input-group col-xxl-8';
subgroup.appendChild(input);
subgroup.appendChild(append);
}
else {
group.className += ' form-group';
if(label) {
label.className += ' col-form-label col-sm-5 col-md-3 col-lg-5 col-xxl-4';
group.appendChild(label);
}
group.appendChild(subgroup);
subgroup.className += ' input-group col-sm-7 col-md-9 col-lg-7 col-xxl-8';
subgroup.appendChild(input);
}
if(description) group.appendChild(description);
return group;
},
getIndentedPanel: function() {
var el = document.createElement('div');
el.className = 'well well-sm';
el.style.paddingBottom = 0;
return el;
},
getFormInputDescription: function(text) {
var el = document.createElement('p');
el.className = 'help-block';
el.innerHTML = text;
return el;
},
getFormInputAppend: function(text) {
var el = document.createElement('div');
el.className = 'input-group-addon';
el.textContent = text;
return el;
},
getHeaderButtonHolder: function() {
var el = this.getButtonHolder();
el.style.marginLeft = '10px';
return el;
},
getButtonHolder: function() {
var el = document.createElement('div');
el.className = 'btn-group';
return el;
},
getButton: function(text, icon, title) {
var el = this._super(text, icon, title);
el.className += 'btn btn-default';
return el;
},
getTable: function() {
var el = document.createElement('table');
el.className = 'table table-bordered';
el.style.width = 'auto';
el.style.maxWidth = 'none';
return el;
},
addInputError: function(input,text) {
if(!input.controlgroup) return;
input.controlgroup.className += ' has-error';
if(!input.errmsg) {
input.errmsg = document.createElement('p');
input.errmsg.className = 'help-block errormsg';
input.controlgroup.appendChild(input.errmsg);
}
else {
input.errmsg.style.display = '';
}
input.errmsg.textContent = text;
},
removeInputError: function(input) {
if(!input.errmsg) return;
input.errmsg.style.display = 'none';
input.controlgroup.className = input.controlgroup.className.replace(/\s?has-error/g,'');
},
getTabHolder: function() {
var el = document.createElement('div');
el.innerHTML = "<div class='tabs list-group col-md-2'></div><div class='col-md-10'></div>";
el.className = 'rows';
return el;
},
getTab: function(text) {
var el = document.createElement('a');
el.className = 'list-group-item';
el.setAttribute('href','#');
el.appendChild(text);
return el;
},
markTabActive: function(tab) {
tab.className += ' active';
},
markTabInactive: function(tab) {
tab.className = tab.className.replace(/\s?active/g,'');
},
getProgressBar: function() {
var min = 0, max = 100, start = 0;
var container = document.createElement('div');
container.className = 'progress';
var bar = document.createElement('div');
bar.className = 'progress-bar';
bar.setAttribute('role', 'progressbar');
bar.setAttribute('aria-valuenow', start);
bar.setAttribute('aria-valuemin', min);
bar.setAttribute('aria-valuenax', max);
bar.innerHTML = start + "%";
container.appendChild(bar);
return container;
},
updateProgressBar: function(progressBar, progress) {
if (!progressBar) return;
var bar = progressBar.firstChild;
var percentage = progress + "%";
bar.setAttribute('aria-valuenow', progress);
bar.style.width = percentage;
bar.innerHTML = percentage;
},
updateProgressBarUnknown: function(progressBar) {
if (!progressBar) return;
var bar = progressBar.firstChild;
progressBar.className = 'progress progress-striped active';
bar.removeAttribute('aria-valuenow');
bar.style.width = '100%';
bar.innerHTML = '';
}
});
JSONEditor.AbstractIconLib = Class.extend({
mapping: {
collapse: '',
expand: '',
"delete": '',
edit: '',
add: '',
cancel: '',
save: '',
moveup: '',
movedown: ''
},
icon_prefix: '',
getIconClass: function(key) {
if(this.mapping[key]) return this.icon_prefix+this.mapping[key];
else return null;
},
getIcon: function(key) {
var iconclass = this.getIconClass(key);
if(!iconclass) return null;
var i = document.createElement('i');
i.className = iconclass;
return i;
}
});
JSONEditor.defaults.iconlibs.fontawesome4 = JSONEditor.AbstractIconLib.extend({
mapping: {
collapse: 'caret-square-o-down',
expand: 'caret-square-o-right',
"delete": 'times',
edit: 'pencil',
add: 'plus',
cancel: 'ban',
save: 'save',
moveup: 'arrow-up',
movedown: 'arrow-down'
},
icon_prefix: 'fa fa-'
});
JSONEditor.defaults.templates["default"] = function() {
return {
compile: function(template) {
var matches = template.match(/{{\s*([a-zA-Z0-9\-_ \.]+)\s*}}/g);
var l = matches && matches.length;
// Shortcut if the template contains no variables
if(!l) return function() { return template; };
// Pre-compute the search/replace functions
// This drastically speeds up template execution
var replacements = [];
var get_replacement = function(i) {
var p = matches[i].replace(/[{}]+/g,'').trim().split('.');
var n = p.length;
var func;
if(n > 1) {
var cur;
func = function(vars) {
cur = vars;
for(i=0; i<n; i++) {
cur = cur[p[i]];
if(!cur) break;
}
return cur;
};
}
else {
p = p[0];
func = function(vars) {
return vars[p];
};
}
replacements.push({
s: matches[i],
r: func
});
};
for(var i=0; i<l; i++) {
get_replacement(i);
}
// The compiled function
return function(vars) {
var ret = template+"";
var r;
for(i=0; i<l; i++) {
r = replacements[i];
ret = ret.replace(r.s, r.r(vars));
}
return ret;
};
}
};
};
JSONEditor.defaults.templates.ejs = function() {
if(!window.EJS) return false;
return {
compile: function(template) {
var compiled = new window.EJS({
text: template
});
return function(context) {
return compiled.render(context);
};
}
};
};
JSONEditor.defaults.templates.handlebars = function() {
return window.Handlebars;
};
JSONEditor.defaults.templates.hogan = function() {
if(!window.Hogan) return false;
return {
compile: function(template) {
var compiled = window.Hogan.compile(template);
return function(context) {
return compiled.render(context);
};
}
};
};
JSONEditor.defaults.templates.markup = function() {
if(!window.Mark || !window.Mark.up) return false;
return {
compile: function(template) {
return function(context) {
return window.Mark.up(template,context);
};
}
};
};
JSONEditor.defaults.templates.mustache = function() {
if(!window.Mustache) return false;
return {
compile: function(template) {
return function(view) {
return window.Mustache.render(template, view);
};
}
};
};
JSONEditor.defaults.templates.swig = function() {
return window.swig;
};
JSONEditor.defaults.templates.underscore = function() {
if(!window._) return false;
return {
compile: function(template) {
return function(context) {
return window._.template(template, context);
};
}
};
};
// Set the default theme
JSONEditor.defaults.theme = 'html';
// Set the default template engine
JSONEditor.defaults.template = 'default';
// Default options when initializing JSON Editor
JSONEditor.defaults.options = {};
// String translate function
JSONEditor.defaults.translate = function(key, variables) {
return $.i18n(key, variables);
};
// Miscellaneous Plugin Settings
JSONEditor.plugins = {
ace: {
theme: ''
},
epiceditor: {
},
sceditor: {
},
select2: {
},
selectize: {
}
};
// Default per-editor options
$each(JSONEditor.defaults.editors, function(i,editor) {
JSONEditor.defaults.editors[i].options = editor.options || {};
});
// Set the default resolvers
// Use "multiple" as a fall back for everything
JSONEditor.defaults.resolvers.unshift(function(schema) {
if(typeof schema.type !== "string") return "multiple";
});
// If the type is not set but properties are defined, we can infer the type is actually object
JSONEditor.defaults.resolvers.unshift(function(schema) {
// If the schema is a simple type
if(!schema.type && schema.properties ) return "object";
});
// If the type is set and it's a basic type, use the primitive editor
JSONEditor.defaults.resolvers.unshift(function(schema) {
// If the schema is a simple type
if(typeof schema.type === "string") return schema.type;
});
// Boolean editors
JSONEditor.defaults.resolvers.unshift(function(schema) {
if(schema.type === 'boolean') {
// convert all boolean to checkbox
return "checkbox";
}
});
// Use the multiple editor for schemas where the `type` is set to "any"
JSONEditor.defaults.resolvers.unshift(function(schema) {
// If the schema can be of any type
if(schema.type === "any") return "multiple";
});
// Editor for base64 encoded files
JSONEditor.defaults.resolvers.unshift(function(schema) {
// If the schema can be of any type
if(schema.type === "string" && schema.media && schema.media.binaryEncoding==="base64") {
return "base64";
}
});
// Editor for uploading files
JSONEditor.defaults.resolvers.unshift(function(schema) {
if(schema.type === "string" && schema.format === "url" && schema.options && schema.options.upload === true) {
if(window.FileReader) return "upload";
}
});
// Use the table editor for arrays with the format set to `table`
JSONEditor.defaults.resolvers.unshift(function(schema) {
// Type `array` with format set to `table`
if(schema.type == "array" && schema.format == "table") {
return "table";
}
});
// Use the `select` editor for dynamic enumSource enums
JSONEditor.defaults.resolvers.unshift(function(schema) {
if(schema.enumSource) return (JSONEditor.plugins.selectize.enable) ? 'selectize' : 'select';
});
// Use the `enum` or `select` editors for schemas with enumerated properties
JSONEditor.defaults.resolvers.unshift(function(schema) {
if(schema["enum"]) {
if(schema.type === "array" || schema.type === "object") {
return "enum";
}
else if(schema.type === "number" || schema.type === "integer" || schema.type === "string") {
return (JSONEditor.plugins.selectize.enable) ? 'selectize' : 'select';
}
}
});
// Use the `select` editor if a string has format "select"
JSONEditor.defaults.resolvers.unshift(function(schema) {
if(schema.type === "string" && schema.format === "select")
return "select";
});
// Specialized editors for arrays of strings
JSONEditor.defaults.resolvers.unshift(function(schema) {
if(schema.type === "array" && schema.items && !(Array.isArray(schema.items)) && schema.uniqueItems && ['string','number','integer'].indexOf(schema.items.type) >= 0) {
// For enumerated strings, number, or integers
if(schema.items.enum) {
return 'multiselect';
}
// For non-enumerated strings (tag editor)
else if(JSONEditor.plugins.selectize.enable && schema.items.type === "string") {
return 'arraySelectize';
}
}
});
// Use the multiple editor for schemas with `oneOf` set
JSONEditor.defaults.resolvers.unshift(function(schema) {
// If this schema uses `oneOf` or `anyOf`
if(schema.oneOf || schema.anyOf) return "multiple";
});
// colorpicker extend for strings
JSONEditor.defaults.resolvers.unshift(function(schema) {
if(schema.type === "array" && schema.format === "colorpicker") {
return "colorPicker";
}
});
// colorpickerRGBA extend for strings
JSONEditor.defaults.resolvers.unshift(function(schema) {
if(schema.type === "array" && schema.format === "colorpickerRGBA") {
return "colorPickerRGBA";
}
});
/**
* This is a small wrapper for using JSON Editor like a typical jQuery plugin.
*/
(function() {
if(window.jQuery || window.Zepto) {
var $ = window.jQuery || window.Zepto;
$.jsoneditor = JSONEditor.defaults;
$.fn.jsoneditor = function(options) {
var self = this;
var editor = this.data('jsoneditor');
if(options === 'value') {
if(!editor) throw "Must initialize jsoneditor before getting/setting the value";
// Set value
if(arguments.length > 1) {
editor.setValue(arguments[1]);
}
// Get value
else {
return editor.getValue();
}
}
else if(options === 'validate') {
if(!editor) throw "Must initialize jsoneditor before validating";
// Validate a specific value
if(arguments.length > 1) {
return editor.validate(arguments[1]);
}
// Validate current value
else {
return editor.validate();
}
}
else if(options === 'destroy') {
if(editor) {
editor.destroy();
this.data('jsoneditor',null);
}
}
else {
// Destroy first
if(editor) {
editor.destroy();
}
// Create editor
editor = new JSONEditor(this.get(0),options);
this.data('jsoneditor',editor);
// Setup event listeners
editor.on('change',function() {
self.trigger('change');
});
editor.on('ready',function() {
self.trigger('ready');
});
}
return this;
};
}
})();
window.JSONEditor = JSONEditor;
})();
//# sourceMappingURL=jsoneditor.js.map