|
|
/*! 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.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); this.translate = this.options.translate || JSONEditor.defaults.translate;
// 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.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("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("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("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('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('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('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('error_type_union') }); } } // Simple type
else { if(!this._checkType(schema.type, value)) { errors.push({ path: path, property: 'type', message: this.translate('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('error_disallow_union') }); } } // Simple type
else { if(this._checkType(schema.disallow, value)) { errors.push({ path: path, property: 'disallow', message: this.translate('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('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?'error_maximum_excl':'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?'error_minimum_excl':'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('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?'error_notempty':'error_minLength'), [schema.minLength]) }); } }
// `pattern`
if(schema.pattern) { if(!(new RegExp(schema.pattern)).test(value)) { errors.push({ path: path, property: 'pattern', message: this.translate('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('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('error_maxItems', [schema.maxItems]) }); } }
// `minItems`
if(schema.minItems) { if(value.length < schema.minItems) { errors.push({ path: path, property: 'minItems', message: this.translate('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('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('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('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('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('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('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.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); }, 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(); }, 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() { return this.schema.title || this.key; }, 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 false; }, 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('name',this.formname); }, unregister: function() { this._super(); if(!this.input) return; this.input.removeAttribute('name'); }, 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);
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); } } // 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); }, {passive: 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); }, {passive: true}); this.input.addEventListener('change',function(e) { self.adjust_height(this); }, {passive: true}); this.adjust_height(); }
if(this.format) this.input.setAttribute('data-schemaformat',this.format);
this.control = this.theme.getFormControl(this.label, this.input, this.description); 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(); },{passive: true}); this.editjson_cancel = this.getButton('Cancel','cancel','Cancel'); this.editjson_cancel.addEventListener('click',function(e) { e.preventDefault(); e.stopPropagation(); self.hideEditJSON(); },{ pasive:true }); 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); } },{passive: 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('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('button_collapse')); } else { self.editor_holder.style.display = 'none'; self.collapsed = true; self.setButtonText(self.toggle_button,'','expand',self.translate('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(); },{passive: true}); 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(); },{passive: true}); 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); }, {passive: 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); } 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); this.item_title = tmp.title || 'item'; } 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 = 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(); },{passive: true});
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('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); },{passive: 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('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); },{passive: 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('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); },{passive: 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('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('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('button_expand')); } },{passive: true});
// 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('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); },{passive: true}); self.controls.appendChild(this.add_row_button);
this.delete_last_row_button = this.getButton(this.translate('button_delete_last',[this.getItemTitle()]),'delete',this.translate('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); },{passive: true}); self.controls.appendChild(this.delete_last_row_button);
this.remove_all_rows_button = this.getButton(this.translate('button_delete_all'),'delete',this.translate('button_delete_all_title')); this.remove_all_rows_button.addEventListener('click',function(e) { e.preventDefault(); e.stopPropagation(); self.setValue([]); self.onChange(true); },{passive: 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); } 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('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); },{passive: 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('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); },{passive: true}); controls_holder.appendChild(self.rows[i].moveup_button); }
if(!this.hide_move_buttons) { self.rows[i].movedown_button = this.getButton('','movedown',this.translate('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); },{passive: 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('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('button_collapse')); } else { self.collapsed = true; self.panel.style.display = 'none'; self.setButtonText(this,'','expand',self.translate('button_expand')); } },{passive: true});
// 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('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); },{passive: true}); self.controls.appendChild(this.add_row_button);
this.delete_last_row_button = this.getButton(this.translate('button_delete_last',[this.getItemTitle()]),'delete',this.translate('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); },{passive: true}); self.controls.appendChild(this.delete_last_row_button);
this.remove_all_rows_button = this.getButton(this.translate('button_delete_all'),'delete',this.translate('button_delete_all_title')); this.remove_all_rows_button.addEventListener('click',function(e) { e.preventDefault(); e.stopPropagation();
self.setValue([]); self.onChange(true); },{passive: 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(); }, {passive: true});
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); }, {passive: 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); }, {passive: 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,'&').replace(/</g,'<').replace(/>/g,'>'); } // 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('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); });
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.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(); }, {passive: true});
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 new_val; // Invalid option, use first option instead
if(this.enum_options.indexOf(val) === -1) { new_val = this.enum_values[0]; } else { new_val = this.enum_values[this.enum_options.indexOf(val)]; }
// If valid hasn't changed
if(new_val === this.value) return;
// Store new value and propogate change event
this.value = new_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.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.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); }, {passive: 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]); } }, {passive: true}); }
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]); } }, {passive: true}); }
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 mime = this.preview_value.match(/^data:([^;,]+)[;,]/); if(mime) mime = mime[1]; if(!mime) mime = 'unknown';
var file = this.uploader.files[0];
this.preview.innerHTML = '<strong>Type:</strong> '+mime+', <strong>Size:</strong> '+file.size+' 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.preview_value; this.preview.appendChild(img); }
this.preview.innerHTML += '<br>'; 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); } } }); }); }, 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(); 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); }, {passive: true});
this.container.appendChild(this.control); }, 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); }
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'; } } }});
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 = 'normal'; return el; }, getHeader: function(text) { var el = document.createElement('h3'); 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) { titles = titles || []; select.innerHTML = ''; for(var i=0; i<options.length; i++) { var option = document.createElement('option'); option.setAttribute('value',options[i]); 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) { var el = this.getFormInputField('range'); el.setAttribute('min',min); 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; }, 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.bootstrap2 = JSONEditor.AbstractTheme.extend({ getRangeInput: function(min, max, step) { // TODO: use bootstrap slider
return this._super(min, max, step); }, getGridContainer: function() { var el = document.createElement('div'); el.className = 'container-fluid'; return el; }, getGridRow: function() { var el = document.createElement('div'); el.className = 'row-fluid'; return el; }, getFormInputLabel: function(text) { var el = this._super(text); el.style.display = 'inline-block'; el.style.fontWeight = 'bold'; return el; }, setGridColumnSize: function(el,size) { el.className = 'span'+size; }, getSelectInput: function(options) { var input = this._super(options); input.style.width = 'auto'; input.style.maxWidth = '98%'; return input; }, getFormInputField: function(type) { var el = this._super(type); el.style.width = '98%'; return el; }, afterInputReady: function(input) { if(input.controlgroup) return; input.controlgroup = this.closest(input,'.control-group'); input.controls = this.closest(input,'.controls'); if(this.closest(input,'.compact')) { input.controlgroup.className = input.controlgroup.className.replace(/control-group/g,'').replace(/[ ]{2,}/g,' '); input.controls.className = input.controlgroup.className.replace(/controls/g,'').replace(/[ ]{2,}/g,' '); input.style.marginBottom = 0; }
// TODO: use bootstrap slider
}, getIndentedPanel: function() { var el = document.createElement('div'); el.className = 'well well-small'; el.style.paddingBottom = 0; return el; }, getFormInputDescription: function(text) { var el = document.createElement('p'); el.className = 'help-inline'; el.textContent = text; return el; }, getFormControl: function(label, input, description) { var ret = document.createElement('div'); ret.className = 'control-group';
var controls = document.createElement('div'); controls.className = 'controls';
if(label && input.getAttribute('type') === 'checkbox') { ret.appendChild(controls); label.className += ' checkbox'; label.appendChild(input); controls.appendChild(label); controls.style.height = '30px'; } else { if(label) { label.className += ' control-label'; ret.appendChild(label); } controls.appendChild(input); ret.appendChild(controls); }
if(description) controls.appendChild(description);
return ret; }, 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 || !input.controls) return; input.controlgroup.className += ' error'; if(!input.errmsg) { input.errmsg = document.createElement('p'); input.errmsg.className = 'help-block errormsg'; input.controls.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?error/g,''); }, getTabHolder: function() { var el = document.createElement('div'); el.className = 'tabbable tabs-left'; el.innerHTML = "<ul class='nav nav-tabs span2' style='margin-right: 0;'></ul><div class='tab-content span10' style='overflow:visible;'></div>"; return el; }, getTab: function(text) { var el = document.createElement('li'); var a = document.createElement('a'); a.setAttribute('href','#'); a.appendChild(text); el.appendChild(a); return el; }, getTabContentHolder: function(tab_holder) { return tab_holder.children[1]; }, getTabContent: function() { var el = document.createElement('div'); el.className = 'tab-pane active'; return el; }, markTabActive: function(tab) { tab.className += ' active'; }, markTabInactive: function(tab) { tab.className = tab.className.replace(/\s?active/g,''); }, addTab: function(holder, tab) { holder.children[0].appendChild(tab); }, getProgressBar: function() { var container = document.createElement('div'); container.className = 'progress';
var bar = document.createElement('div'); bar.className = 'bar'; bar.style.width = '0%'; container.appendChild(bar);
return container; }, updateProgressBar: function(progressBar, progress) { if (!progressBar) return;
progressBar.firstChild.style.width = progress + "%"; }, updateProgressBarUnknown: function(progressBar) { if (!progressBar) return;
progressBar.className = 'progress progress-striped active'; progressBar.firstChild.style.width = '100%'; }});
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) { var group = document.createElement('div');
if(label && input.type === 'checkbox') { group.className += ' checkbox'; label.appendChild(input); label.style.fontSize = '14px'; group.style.marginTop = '0'; group.appendChild(label); input.style.position = 'relative'; input.style.cssFloat = 'left'; } else { group.className += ' form-group'; if(label) { label.className += ' control-label'; group.appendChild(label); } group.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; }, 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 list-group-horizontal col-md-12'></div><div class='col-md-12'></div>"; el.className = 'rows'; return el; }, getTab: function(text) { var el = document.createElement('a'); el.className = 'list-group-item col-md-2'; 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 = ''; }});
// Base Foundation theme
JSONEditor.defaults.themes.foundation = JSONEditor.AbstractTheme.extend({ getChildEditorHolder: function() { var el = document.createElement('div'); el.style.marginBottom = '15px'; return el; }, getSelectInput: function(options) { var el = this._super(options); el.style.minWidth = 'none'; el.style.padding = '5px'; el.style.marginTop = '3px'; return el; }, getSwitcher: function(options) { var el = this._super(options); el.style.paddingRight = '8px'; return el; }, afterInputReady: function(input) { if(this.closest(input,'.compact')) { input.style.marginBottom = 0; } input.group = this.closest(input,'.form-control'); }, getFormInputLabel: function(text) { var el = this._super(text); el.style.display = 'inline-block'; return el; }, getFormInputField: function(type) { var el = this._super(type); el.style.width = '100%'; el.style.marginBottom = type==='checkbox'? '0' : '12px'; return el; }, getFormInputDescription: function(text) { var el = document.createElement('p'); el.textContent = text; el.style.marginTop = '-10px'; el.style.fontStyle = 'italic'; return el; }, getIndentedPanel: function() { var el = document.createElement('div'); el.className = 'panel'; el.style.paddingBottom = 0; return el; }, getHeaderButtonHolder: function() { var el = this.getButtonHolder(); el.style.display = 'inline-block'; el.style.marginLeft = '10px'; el.style.verticalAlign = 'middle'; return el; }, getButtonHolder: function() { var el = document.createElement('div'); el.className = 'button-group'; return el; }, getButton: function(text, icon, title) { var el = this._super(text, icon, title); el.className += ' small button'; return el; }, addInputError: function(input,text) { if(!input.group) return; input.group.className += ' error';
if(!input.errmsg) { input.insertAdjacentHTML('afterend','<small class="error"></small>'); input.errmsg = input.parentNode.getElementsByClassName('error')[0]; } else { input.errmsg.style.display = ''; }
input.errmsg.textContent = text; }, removeInputError: function(input) { if(!input.errmsg) return; input.group.className = input.group.className.replace(/ error/g,''); input.errmsg.style.display = 'none'; }, getProgressBar: function() { var progressBar = document.createElement('div'); progressBar.className = 'progress';
var meter = document.createElement('span'); meter.className = 'meter'; meter.style.width = '0%'; progressBar.appendChild(meter); return progressBar; }, updateProgressBar: function(progressBar, progress) { if (!progressBar) return; progressBar.firstChild.style.width = progress + '%'; }, updateProgressBarUnknown: function(progressBar) { if (!progressBar) return; progressBar.firstChild.style.width = '100%'; }});
// Foundation 3 Specific Theme
JSONEditor.defaults.themes.foundation3 = JSONEditor.defaults.themes.foundation.extend({ getHeaderButtonHolder: function() { var el = this._super(); el.style.fontSize = '.6em'; return el; }, getFormInputLabel: function(text) { var el = this._super(text); el.style.fontWeight = 'bold'; return el; }, getTabHolder: function() { var el = document.createElement('div'); el.className = 'row'; el.innerHTML = "<dl class='tabs vertical two columns'></dl><div class='tabs-content ten columns'></div>"; return el; }, setGridColumnSize: function(el,size) { var sizes = ['zero','one','two','three','four','five','six','seven','eight','nine','ten','eleven','twelve']; el.className = 'columns '+sizes[size]; }, getTab: function(text) { var el = document.createElement('dd'); var a = document.createElement('a'); a.setAttribute('href','#'); a.appendChild(text); el.appendChild(a); return el; }, getTabContentHolder: function(tab_holder) { return tab_holder.children[1]; }, getTabContent: function() { var el = document.createElement('div'); el.className = 'content active'; el.style.paddingLeft = '5px'; return el; }, markTabActive: function(tab) { tab.className += ' active'; }, markTabInactive: function(tab) { tab.className = tab.className.replace(/\s*active/g,''); }, addTab: function(holder, tab) { holder.children[0].appendChild(tab); }});
// Foundation 4 Specific Theme
JSONEditor.defaults.themes.foundation4 = JSONEditor.defaults.themes.foundation.extend({ getHeaderButtonHolder: function() { var el = this._super(); el.style.fontSize = '.6em'; return el; }, setGridColumnSize: function(el,size) { el.className = 'columns large-'+size; }, getFormInputDescription: function(text) { var el = this._super(text); el.style.fontSize = '.8rem'; return el; }, getFormInputLabel: function(text) { var el = this._super(text); el.style.fontWeight = 'bold'; return el; }});
// Foundation 5 Specific Theme
JSONEditor.defaults.themes.foundation5 = JSONEditor.defaults.themes.foundation.extend({ getFormInputDescription: function(text) { var el = this._super(text); el.style.fontSize = '.8rem'; return el; }, setGridColumnSize: function(el,size) { el.className = 'columns medium-'+size; }, getButton: function(text, icon, title) { var el = this._super(text,icon,title); el.className = el.className.replace(/\s*small/g,'') + ' tiny'; return el; }, getTabHolder: function() { var el = document.createElement('div'); el.innerHTML = "<dl class='tabs vertical'></dl><div class='tabs-content vertical'></div>"; return el; }, getTab: function(text) { var el = document.createElement('dd'); var a = document.createElement('a'); a.setAttribute('href','#'); a.appendChild(text); el.appendChild(a); return el; }, getTabContentHolder: function(tab_holder) { return tab_holder.children[1]; }, getTabContent: function() { var el = document.createElement('div'); el.className = 'content active'; el.style.paddingLeft = '5px'; return el; }, markTabActive: function(tab) { tab.className += ' active'; }, markTabInactive: function(tab) { tab.className = tab.className.replace(/\s*active/g,''); }, addTab: function(holder, tab) { holder.children[0].appendChild(tab); }});
JSONEditor.defaults.themes.foundation6 = JSONEditor.defaults.themes.foundation5.extend({ getIndentedPanel: function() { var el = document.createElement('div'); el.className = 'callout secondary'; return el; }, getButtonHolder: function() { var el = document.createElement('div'); el.className = 'button-group tiny'; el.style.marginBottom = 0; return el; }, getFormInputLabel: function(text) { var el = this._super(text); el.style.display = 'block'; return el; }, 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 if (label) { label.appendChild(input); } else { el.appendChild(input); }
if(description) label.appendChild(description); return el; }, addInputError: function(input,text) { if(!input.group) return; input.group.className += ' error';
if(!input.errmsg) { var errorEl = document.createElement('span'); errorEl.className = 'form-error is-visible'; input.group.getElementsByTagName('label')[0].appendChild(errorEl);
input.className = input.className + ' is-invalid-input';
input.errmsg = errorEl; } else { input.errmsg.style.display = ''; input.className = ''; }
input.errmsg.textContent = text; }, removeInputError: function(input) { if(!input.errmsg) return; input.className = input.className.replace(/ is-invalid-input/g,''); if(input.errmsg.parentNode) { input.errmsg.parentNode.removeChild(input.errmsg); } },});
JSONEditor.defaults.themes.html = JSONEditor.AbstractTheme.extend({ getFormInputLabel: function(text) { var el = this._super(text); el.style.display = 'block'; el.style.marginBottom = '3px'; el.style.fontWeight = 'bold'; return el; }, getFormInputDescription: function(text) { var el = this._super(text); el.style.fontSize = '.8em'; el.style.margin = 0; el.style.display = 'inline-block'; el.style.fontStyle = 'italic'; return el; }, getIndentedPanel: function() { var el = this._super(); el.style.border = '1px solid #ddd'; el.style.padding = '5px'; el.style.margin = '5px'; el.style.borderRadius = '3px'; return el; }, getChildEditorHolder: function() { var el = this._super(); el.style.marginBottom = '8px'; return el; }, getHeaderButtonHolder: function() { var el = this.getButtonHolder(); el.style.display = 'inline-block'; el.style.marginLeft = '10px'; el.style.fontSize = '.8em'; el.style.verticalAlign = 'middle'; return el; }, getTable: function() { var el = this._super(); el.style.borderBottom = '1px solid #ccc'; el.style.marginBottom = '5px'; return el; }, addInputError: function(input, text) { input.style.borderColor = 'red'; if(!input.errmsg) { var group = this.closest(input,'.form-control'); input.errmsg = document.createElement('div'); input.errmsg.setAttribute('class','errmsg'); input.errmsg.style = input.errmsg.style || {}; input.errmsg.style.color = 'red'; group.appendChild(input.errmsg); } else { input.errmsg.style.display = 'block'; } input.errmsg.innerHTML = ''; input.errmsg.appendChild(document.createTextNode(text)); }, removeInputError: function(input) { input.style.borderColor = ''; if(input.errmsg) input.errmsg.style.display = 'none'; }, getProgressBar: function() { var max = 100, start = 0;
var progressBar = document.createElement('progress'); progressBar.setAttribute('max', max); progressBar.setAttribute('value', start); return progressBar; }, updateProgressBar: function(progressBar, progress) { if (!progressBar) return; progressBar.setAttribute('value', progress); }, updateProgressBarUnknown: function(progressBar) { if (!progressBar) return; progressBar.removeAttribute('value'); }});
JSONEditor.defaults.themes.jqueryui = JSONEditor.AbstractTheme.extend({ getTable: function() { var el = this._super(); el.setAttribute('cellpadding',5); el.setAttribute('cellspacing',0); return el; }, getTableHeaderCell: function(text) { var el = this._super(text); el.className = 'ui-state-active'; el.style.fontWeight = 'bold'; return el; }, getTableCell: function() { var el = this._super(); el.className = 'ui-widget-content'; return el; }, getHeaderButtonHolder: function() { var el = this.getButtonHolder(); el.style.marginLeft = '10px'; el.style.fontSize = '.6em'; el.style.display = 'inline-block'; return el; }, getFormInputDescription: function(text) { var el = this.getDescription(text); el.style.marginLeft = '10px'; el.style.display = 'inline-block'; return el; }, getFormControl: function(label, input, description) { var el = this._super(label,input,description); if(input.type === 'checkbox') { el.style.lineHeight = '25px'; el.style.padding = '3px 0'; } else { el.style.padding = '4px 0 8px 0'; } return el; }, getDescription: function(text) { var el = document.createElement('span'); el.style.fontSize = '.8em'; el.style.fontStyle = 'italic'; el.textContent = text; return el; }, getButtonHolder: function() { var el = document.createElement('div'); el.className = 'ui-buttonset'; el.style.fontSize = '.7em'; return el; }, getFormInputLabel: function(text) { var el = document.createElement('label'); el.style.fontWeight = 'bold'; el.style.display = 'block'; el.textContent = text; return el; }, getButton: function(text, icon, title) { var button = document.createElement("button"); button.className = 'ui-button ui-widget ui-state-default ui-corner-all';
// Icon only
if(icon && !text) { button.className += ' ui-button-icon-only'; icon.className += ' ui-button-icon-primary ui-icon-primary'; button.appendChild(icon); } // Icon and Text
else if(icon) { button.className += ' ui-button-text-icon-primary'; icon.className += ' ui-button-icon-primary ui-icon-primary'; button.appendChild(icon); } // Text only
else { button.className += ' ui-button-text-only'; }
var el = document.createElement('span'); el.className = 'ui-button-text'; el.textContent = text||title||"."; button.appendChild(el);
button.setAttribute('title',title); return button; }, setButtonText: function(button,text, icon, title) { button.innerHTML = ''; button.className = 'ui-button ui-widget ui-state-default ui-corner-all';
// Icon only
if(icon && !text) { button.className += ' ui-button-icon-only'; icon.className += ' ui-button-icon-primary ui-icon-primary'; button.appendChild(icon); } // Icon and Text
else if(icon) { button.className += ' ui-button-text-icon-primary'; icon.className += ' ui-button-icon-primary ui-icon-primary'; button.appendChild(icon); } // Text only
else { button.className += ' ui-button-text-only'; }
var el = document.createElement('span'); el.className = 'ui-button-text'; el.textContent = text||title||"."; button.appendChild(el);
button.setAttribute('title',title); }, getIndentedPanel: function() { var el = document.createElement('div'); el.className = 'ui-widget-content ui-corner-all'; el.style.padding = '1em 1.4em'; el.style.marginBottom = '20px'; return el; }, afterInputReady: function(input) { if(input.controls) return; input.controls = this.closest(input,'.form-control'); }, addInputError: function(input,text) { if(!input.controls) return; if(!input.errmsg) { input.errmsg = document.createElement('div'); input.errmsg.className = 'ui-state-error'; input.controls.appendChild(input.errmsg); } else { input.errmsg.style.display = ''; }
input.errmsg.textContent = text; }, removeInputError: function(input) { if(!input.errmsg) return; input.errmsg.style.display = 'none'; }, markTabActive: function(tab) { tab.className = tab.className.replace(/\s*ui-widget-header/g,'')+' ui-state-active'; }, markTabInactive: function(tab) { tab.className = tab.className.replace(/\s*ui-state-active/g,'')+' ui-widget-header'; }});
JSONEditor.defaults.themes.barebones = JSONEditor.AbstractTheme.extend({ getFormInputLabel: function (text) { var el = this._super(text); return el; }, getFormInputDescription: function (text) { var el = this._super(text); return el; }, getIndentedPanel: function () { var el = this._super(); return el; }, getChildEditorHolder: function () { var el = this._super(); return el; }, getHeaderButtonHolder: function () { var el = this.getButtonHolder(); return el; }, getTable: function () { var el = this._super(); return el; }, addInputError: function (input, text) { if (!input.errmsg) { var group = this.closest(input, '.form-control'); input.errmsg = document.createElement('div'); input.errmsg.setAttribute('class', 'errmsg'); group.appendChild(input.errmsg); } else { input.errmsg.style.display = 'block'; }
input.errmsg.innerHTML = ''; input.errmsg.appendChild(document.createTextNode(text)); }, removeInputError: function (input) { input.style.borderColor = ''; if (input.errmsg) input.errmsg.style.display = 'none'; }, getProgressBar: function () { var max = 100, start = 0;
var progressBar = document.createElement('progress'); progressBar.setAttribute('max', max); progressBar.setAttribute('value', start); return progressBar; }, updateProgressBar: function (progressBar, progress) { if (!progressBar) return; progressBar.setAttribute('value', progress); }, updateProgressBarUnknown: function (progressBar) { if (!progressBar) return; progressBar.removeAttribute('value'); }});
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.bootstrap2 = JSONEditor.AbstractIconLib.extend({ mapping: { collapse: 'chevron-down', expand: 'chevron-up', "delete": 'trash', edit: 'pencil', add: 'plus', cancel: 'ban-circle', save: 'ok', moveup: 'arrow-up', movedown: 'arrow-down' }, icon_prefix: 'icon-'});
JSONEditor.defaults.iconlibs.bootstrap3 = JSONEditor.AbstractIconLib.extend({ mapping: { collapse: 'chevron-down', expand: 'chevron-right', "delete": 'remove', edit: 'pencil', add: 'plus', cancel: 'floppy-remove', save: 'floppy-saved', moveup: 'arrow-up', movedown: 'arrow-down' }, icon_prefix: 'glyphicon glyphicon-'});
JSONEditor.defaults.iconlibs.fontawesome3 = JSONEditor.AbstractIconLib.extend({ mapping: { collapse: 'chevron-down', expand: 'chevron-right', "delete": 'remove', edit: 'pencil', add: 'plus', cancel: 'ban-circle', save: 'save', moveup: 'arrow-up', movedown: 'arrow-down' }, icon_prefix: 'icon-'});
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.iconlibs.foundation2 = JSONEditor.AbstractIconLib.extend({ mapping: { collapse: 'minus', expand: 'plus', "delete": 'remove', edit: 'edit', add: 'add-doc', cancel: 'error', save: 'checkmark', moveup: 'up-arrow', movedown: 'down-arrow' }, icon_prefix: 'foundicon-'});
JSONEditor.defaults.iconlibs.foundation3 = JSONEditor.AbstractIconLib.extend({ mapping: { collapse: 'minus', expand: 'plus', "delete": 'x', edit: 'pencil', add: 'page-add', cancel: 'x-circle', save: 'save', moveup: 'arrow-up', movedown: 'arrow-down' }, icon_prefix: 'fi-'});
JSONEditor.defaults.iconlibs.jqueryui = JSONEditor.AbstractIconLib.extend({ mapping: { collapse: 'triangle-1-s', expand: 'triangle-1-e', "delete": 'trash', edit: 'pencil', add: 'plusthick', cancel: 'closethick', save: 'disk', moveup: 'arrowthick-1-n', movedown: 'arrowthick-1-s' }, icon_prefix: 'ui-icon ui-icon-'});
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) { var lang = JSONEditor.defaults.languages[JSONEditor.defaults.language]; if(!lang) throw "Unknown language "+JSONEditor.defaults.language; var string = lang[key] || JSONEditor.defaults.languages[JSONEditor.defaults.default_language][key]; if(typeof string === "undefined") throw "Unknown translate string "+key; if(variables) { for(var i=0; i<variables.length; i++) { string = string.replace(new RegExp('\\{\\{'+i+'}}','g'),variables[i]); } } return string;};
// Translation strings and default languages
JSONEditor.defaults.default_language = 'en';JSONEditor.defaults.language = JSONEditor.defaults.default_language;JSONEditor.defaults.languages.en = { /** * When a property is not set */ error_notset: "Property must be set", /** * When a string must not be empty */ error_notempty: "Value required", /** * When a value is not one of the enumerated values */ error_enum: "Value must be one of the enumerated values", /** * When a value doesn't validate any schema of a 'anyOf' combination */ error_anyOf: "Value must validate against at least one of the provided schemas", /** * When a value doesn't validate * @variables This key takes one variable: The number of schemas the value does not validate */ error_oneOf: 'Value must validate against exactly one of the provided schemas. It currently validates against {{0}} of the schemas.', /** * When a value does not validate a 'not' schema */ error_not: "Value must not validate against the provided schema", /** * When a value does not match any of the provided types */ error_type_union: "Value must be one of the provided types", /** * When a value does not match the given type * @variables This key takes one variable: The type the value should be of */ error_type: "Value must be of type {{0}}", /** * When the value validates one of the disallowed types */ error_disallow_union: "Value must not be one of the provided disallowed types", /** * When the value validates a disallowed type * @variables This key takes one variable: The type the value should not be of */ error_disallow: "Value must not be of type {{0}}", /** * When a value is not a multiple of or divisible by a given number * @variables This key takes one variable: The number mentioned above */ error_multipleOf: "Value must be a multiple of {{0}}", /** * When a value is greater than it's supposed to be (exclusive) * @variables This key takes one variable: The maximum */ error_maximum_excl: "Value must be less than {{0}}", /** * When a value is greater than it's supposed to be (inclusive * @variables This key takes one variable: The maximum */ error_maximum_incl: "Value must be at most {{0}}", /** * When a value is lesser than it's supposed to be (exclusive) * @variables This key takes one variable: The minimum */ error_minimum_excl: "Value must be greater than {{0}}", /** * When a value is lesser than it's supposed to be (inclusive) * @variables This key takes one variable: The minimum */ error_minimum_incl: "Value must be at least {{0}}", /** * When a value have too many characters * @variables This key takes one variable: The maximum character count */ error_maxLength: "Value must be at most {{0}} characters long", /** * When a value does not have enough characters * @variables This key takes one variable: The minimum character count */ error_minLength: "Value must be at least {{0}} characters long", /** * When a value does not match a given pattern */ error_pattern: "Value must match the pattern {{0}}", /** * When an array has additional items whereas it is not supposed to */ error_additionalItems: "No additional items allowed in this array", /** * When there are to many items in an array * @variables This key takes one variable: The maximum item count */ error_maxItems: "Value must have at most {{0}} items", /** * When there are not enough items in an array * @variables This key takes one variable: The minimum item count */ error_minItems: "Value must have at least {{0}} items", /** * When an array is supposed to have unique items but has duplicates */ error_uniqueItems: "Array must have unique items", /** * When there are too many properties in an object * @variables This key takes one variable: The maximum property count */ error_maxProperties: "Object must have at most {{0}} properties", /** * When there are not enough properties in an object * @variables This key takes one variable: The minimum property count */ error_minProperties: "Object must have at least {{0}} properties", /** * When a required property is not defined * @variables This key takes one variable: The name of the missing property */ error_required: "Object is missing the required property '{{0}}'", /** * When there is an additional property is set whereas there should be none * @variables This key takes one variable: The name of the additional property */ error_additional_properties: "No additional properties allowed, but property {{0}} is set", /** * When a dependency is not resolved * @variables This key takes one variable: The name of the missing property for the dependency */ error_dependency: "Must have property {{0}}", /** * Text on Delete All buttons */ button_delete_all: "All", /** * Title on Delete All buttons */ button_delete_all_title: "Delete All", /** * Text on Delete Last buttons * @variable This key takes one variable: The title of object to delete */ button_delete_last: "Last {{0}}", /** * Title on Delete Last buttons * @variable This key takes one variable: The title of object to delete */ button_delete_last_title: "Delete Last {{0}}", /** * Title on Add Row buttons * @variable This key takes one variable: The title of object to add */ button_add_row_title: "Add {{0}}", /** * Title on Move Down buttons */ button_move_down_title: "Move down", /** * Title on Move Up buttons */ button_move_up_title: "Move up", /** * Title on Delete Row buttons * @variable This key takes one variable: The title of object to delete */ button_delete_row_title: "Delete {{0}}", /** * Title on Delete Row buttons, short version (no parameter with the object title) */ button_delete_row_title_short: "Delete", /** * Title on Collapse buttons */ button_collapse: "Collapse", /** * Title on Expand buttons */ button_expand: "Expand"};JSONEditor.defaults.languages.es = { /** * When a property is not set */ error_notset: "La propiedad debe estar asignada", /** * When a string must not be empty */ error_notempty: "Valor requerido", /** * When a value is not one of the enumerated values */ error_enum: "El valor debe se uno de los valores enumerados", /** * When a value doesn't validate any schema of a 'anyOf' combination */ error_anyOf: "El valor debe validarse contra al menos uno de los esquemás definidos", /** * When a value doesn't validate * @variables This key takes one variable: The number of schemas the value does not validate */ error_oneOf: 'El valor debe validarse contra exactamente uno de los esquemas definidos. Actualmente se valida contra {{0}} de los esquemas.', /** * When a value does not validate a 'not' schema */ error_not: "El valor no debe validarse contra el esquema definido", /** * When a value does not match any of the provided types */ error_type_union: "El valor debe ser de uno de los tipos definidos", /** * When a value does not match the given type * @variables This key takes one variable: The type the value should be of */ error_type: "El valor debe ser de tipo {{0}}", /** * When the value validates one of the disallowed types */ error_disallow_union: "El valor no debe ser uno de los tipos deshabilitados", /** * When the value validates a disallowed type * @variables This key takes one variable: The type the value should not be of */ error_disallow: "El valor no debe ser de tipo {{0}}", /** * When a value is not a multiple of or divisible by a given number * @variables This key takes one variable: The number mentioned above */ error_multipleOf: "El valor debe ser un multiplo de {{0}}", /** * When a value is greater than it's supposed to be (exclusive) * @variables This key takes one variable: The maximum */ error_maximum_excl: "El valor debe ser menor que {{0}}", /** * When a value is greater than it's supposed to be (inclusive * @variables This key takes one variable: The maximum */ error_maximum_incl: "El valor debe ser como máximo {{0}}", /** * When a value is lesser than it's supposed to be (exclusive) * @variables This key takes one variable: The minimum */ error_minimum_excl: "El valor debe ser mayor que {{0}}", /** * When a value is lesser than it's supposed to be (inclusive) * @variables This key takes one variable: The minimum */ error_minimum_incl: "El valor debe ser al menos {{0}}", /** * When a value have too many characters * @variables This key takes one variable: The maximum character count */ error_maxLength: "El valor debe tener como máximo {{0}} caracteres", /** * When a value does not have enough characters * @variables This key takes one variable: The minimum character count */ error_minLength: "El valor debe tener al menos {{0}} caracteres", /** * When a value does not match a given pattern */ error_pattern: "El valor debe tener el patrón {{0}}", /** * When an array has additional items whereas it is not supposed to */ error_additionalItems: "No se permiten elementos adicionales en este array", /** * When there are to many items in an array * @variables This key takes one variable: The maximum item count */ error_maxItems: "El valor debe tener como máximo {{0}} elementos", /** * When there are not enough items in an array * @variables This key takes one variable: The minimum item count */ error_minItems: "El valor debe tener al menos {{0}} elementos", /** * When an array is supposed to have unique items but has duplicates */ error_uniqueItems: "El Array debe tener elementos únicos", /** * When there are too many properties in an object * @variables This key takes one variable: The maximum property count */ error_maxProperties: "El objeto debe tener como máximo {{0}} propiedades", /** * When there are not enough properties in an object * @variables This key takes one variable: The minimum property count */ error_minProperties: "El objeto debe tener al menos {{0}} propiedades", /** * When a required property is not defined * @variables This key takes one variable: The name of the missing property */ error_required: "El objeto perdió la propiedad requerida '{{0}}'", /** * When there is an additional property is set whereas there should be none * @variables This key takes one variable: The name of the additional property */ error_additional_properties: "No se permiten propiedades adicionales, pero la propiedad {{0}} esta asignada", /** * When a dependency is not resolved * @variables This key takes one variable: The name of the missing property for the dependency */ error_dependency: "Debe tener la propiedad {{0}}", /** * Text on Delete All buttons */ button_delete_all: "Todos", /** * Title on Delete All buttons */ button_delete_all_title: "Eliminar Todos", /** * Text on Delete Last buttons * @variable This key takes one variable: The title of object to delete */ button_delete_last: "Último {{0}}", /** * Title on Delete Last buttons * @variable This key takes one variable: The title of object to delete */ button_delete_last_title: "Eliminar Último {{0}}", /** * Title on Add Row buttons * @variable This key takes one variable: The title of object to add */ button_add_row_title: "Agregar {{0}}", /** * Title on Move Down buttons */ button_move_down_title: "Bajar", /** * Title on Move Up buttons */ button_move_up_title: "Subir", /** * Title on Delete Row buttons * @variable This key takes one variable: The title of object to delete */ button_delete_row_title: "Eliminar {{0}}", /** * Title on Delete Row buttons, short version (no parameter with the object title) */ button_delete_row_title_short: "Eliminar", /** * Title on Collapse buttons */ button_collapse: "Contraer", /** * Title on Expand buttons */ button_expand: "Expandir"};// 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') { // If explicitly set to 'checkbox', use that
if(schema.format === "checkbox" || (schema.options && schema.options.checkbox)) { return "checkbox"; } // Otherwise, default to select menu
return (JSONEditor.plugins.selectize.enable) ? 'selectize' : 'select'; }});// 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'; } }});// 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";});
/** * 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
|