| /*! JSON Editor v0.7.25 - JSON Schema -> HTML Editor |
| * By Jeremy Dorn - https://github.com/jdorn/json-editor/ |
| * Released under the MIT license |
| * |
| * Date: 2016-04-12 |
| */ |
| |
| /** |
| * 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(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 = arguments.callee; |
| |
| 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 arrays should be unioned together |
| if(prop === 'required' && typeof val === "object" && Array.isArray(val)) { |
| // Union arrays and unique |
| extended.required = 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]; |
| |
| |
| 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]; |
| } |
| } |
| // 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)) { |
| 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)); |
| }); |
| } |
| } |
| |
| 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); |
| }); |
| |
| if(this.options.input_height) this.input.style.height = this.options.input_height; |
| if(this.options.expand_height) { |
| this.adjust_height = function(el) { |
| if(!el) return; |
| var i, ch=el.offsetHeight; |
| // Input too short |
| if(el.offsetHeight < el.scrollHeight) { |
| i=0; |
| while(el.offsetHeight < el.scrollHeight+3) { |
| if(i>100) break; |
| i++; |
| ch++; |
| el.style.height = ch+'px'; |
| } |
| } |
| else { |
| i=0; |
| while(el.offsetHeight >= el.scrollHeight+3) { |
| if(i>100) break; |
| i++; |
| ch--; |
| el.style.height = ch+'px'; |
| } |
| el.style.height = (ch+1)+'px'; |
| } |
| }; |
| |
| this.input.addEventListener('keyup',function(e) { |
| self.adjust_height(this); |
| }); |
| this.input.addEventListener('change',function(e) { |
| self.adjust_height(this); |
| }); |
| this.adjust_height(); |
| } |
| |
| if(this.format) this.input.setAttribute('data-schemaformat',this.format); |
| |
| 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 { |
| this.defaultProperties = this.schema.defaultProperties || Object.keys(this.schema.properties); |
| |
| // Increase the grid width to account for padding |
| self.maxwidth += 1; |
| |
| $each(this.defaultProperties, function(i,key) { |
| self.addObjectProperty(key, true); |
| |
| if(self.editors[key]) { |
| self.minwidth = Math.max(self.minwidth,(self.editors[key].options.grid_columns || self.editors[key].getNumColumns())); |
| self.maxwidth += (self.editors[key].options.grid_columns || self.editors[key].getNumColumns()); |
| } |
| }); |
| } |
| |
| // Sort editors by propertyOrder |
| this.property_order = Object.keys(this.editors); |
| this.property_order = this.property_order.sort(function(a,b) { |
| var ordera = self.editors[a].schema.propertyOrder; |
| var orderb = self.editors[b].schema.propertyOrder; |
| if(typeof ordera !== "number") ordera = 1000; |
| if(typeof orderb !== "number") orderb = 1000; |
| |
| return ordera - orderb; |
| }); |
| }, |
| build: function() { |
| var self = this; |
| |
| // If the object should be rendered as a table row |
| if(this.options.table_row) { |
| this.editor_holder = this.container; |
| $each(this.editors, function(key,editor) { |
| var holder = self.theme.getTableCell(); |
| self.editor_holder.appendChild(holder); |
| |
| editor.setContainer(holder); |
| editor.build(); |
| editor.postBuild(); |
| |
| if(self.editors[key].options.hidden) { |
| holder.style.display = 'none'; |
| } |
| if(self.editors[key].options.input_width) { |
| holder.style.width = self.editors[key].options.input_width; |
| } |
| }); |
| } |
| // If the object should be rendered as a table |
| else if(this.options.table) { |
| // TODO: table display format |
| throw "Not supported yet"; |
| } |
| // If the object should be rendered as a div |
| else { |
| this.header = document.createElement('span'); |
| this.header.textContent = this.getTitle(); |
| this.title = this.theme.getHeader(this.header); |
| this.container.appendChild(this.title); |
| this.container.style.position = 'relative'; |
| |
| // Edit JSON modal |
| this.editjson_holder = this.theme.getModal(); |
| this.editjson_textarea = this.theme.getTextareaInput(); |
| this.editjson_textarea.style.height = '170px'; |
| this.editjson_textarea.style.width = '300px'; |
| this.editjson_textarea.style.display = 'block'; |
| this.editjson_save = this.getButton('Save','save','Save'); |
| this.editjson_save.addEventListener('click',function(e) { |
| e.preventDefault(); |
| e.stopPropagation(); |
| self.saveJSON(); |
| }); |
| this.editjson_cancel = this.getButton('Cancel','cancel','Cancel'); |
| this.editjson_cancel.addEventListener('click',function(e) { |
| e.preventDefault(); |
| e.stopPropagation(); |
| self.hideEditJSON(); |
| }); |
| this.editjson_holder.appendChild(this.editjson_textarea); |
| this.editjson_holder.appendChild(this.editjson_save); |
| this.editjson_holder.appendChild(this.editjson_cancel); |
| |
| // Manage Properties modal |
| this.addproperty_holder = this.theme.getModal(); |
| this.addproperty_list = document.createElement('div'); |
| this.addproperty_list.style.width = '295px'; |
| this.addproperty_list.style.maxHeight = '160px'; |
| this.addproperty_list.style.padding = '5px 0'; |
| this.addproperty_list.style.overflowY = 'auto'; |
| this.addproperty_list.style.overflowX = 'hidden'; |
| this.addproperty_list.style.paddingLeft = '5px'; |
| this.addproperty_list.setAttribute('class', 'property-selector'); |
| this.addproperty_add = this.getButton('add','add','add'); |
| this.addproperty_input = this.theme.getFormInputField('text'); |
| this.addproperty_input.setAttribute('placeholder','Property name...'); |
| this.addproperty_input.style.width = '220px'; |
| this.addproperty_input.style.marginBottom = '0'; |
| this.addproperty_input.style.display = 'inline-block'; |
| this.addproperty_add.addEventListener('click',function(e) { |
| e.preventDefault(); |
| e.stopPropagation(); |
| if(self.addproperty_input.value) { |
| if(self.editors[self.addproperty_input.value]) { |
| window.alert('there is already a property with that name'); |
| return; |
| } |
| |
| self.addObjectProperty(self.addproperty_input.value); |
| if(self.editors[self.addproperty_input.value]) { |
| self.editors[self.addproperty_input.value].disable(); |
| } |
| self.onChange(true); |
| } |
| }); |
| this.addproperty_holder.appendChild(this.addproperty_list); |
| this.addproperty_holder.appendChild(this.addproperty_input); |
| this.addproperty_holder.appendChild(this.addproperty_add); |
| var spacer = document.createElement('div'); |
| spacer.style.clear = 'both'; |
| this.addproperty_holder.appendChild(spacer); |
| |
| |
| // Description |
| if(this.schema.description) { |
| this.description = this.theme.getDescription(this.schema.description); |
| this.container.appendChild(this.description); |
| } |
| |
| // Validation error placeholder area |
| this.error_holder = document.createElement('div'); |
| this.container.appendChild(this.error_holder); |
| |
| // Container for child editor area |
| this.editor_holder = this.theme.getIndentedPanel(); |
| this.container.appendChild(this.editor_holder); |
| |
| // Container for rows of child editors |
| this.row_container = this.theme.getGridContainer(); |
| this.editor_holder.appendChild(this.row_container); |
| |
| $each(this.editors, function(key,editor) { |
| var holder = self.theme.getGridColumn(); |
| self.row_container.appendChild(holder); |
| |
| editor.setContainer(holder); |
| editor.build(); |
| editor.postBuild(); |
| }); |
| |
| // Control buttons |
| this.title_controls = this.theme.getHeaderButtonHolder(); |
| this.editjson_controls = this.theme.getHeaderButtonHolder(); |
| this.addproperty_controls = this.theme.getHeaderButtonHolder(); |
| this.title.appendChild(this.title_controls); |
| this.title.appendChild(this.editjson_controls); |
| this.title.appendChild(this.addproperty_controls); |
| |
| // Show/Hide button |
| this.collapsed = false; |
| this.toggle_button = this.getButton('','collapse','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','Collapse'); |
| } |
| else { |
| self.editor_holder.style.display = 'none'; |
| self.collapsed = true; |
| self.setButtonText(self.toggle_button,'','expand','Expand'); |
| } |
| }); |
| |
| // If it should start collapsed |
| if(this.options.collapsed) { |
| $trigger(this.toggle_button,'click'); |
| } |
| |
| // Collapse button disabled |
| if(this.schema.options && typeof this.schema.options.disable_collapse !== "undefined") { |
| if(this.schema.options.disable_collapse) this.toggle_button.style.display = 'none'; |
| } |
| else if(this.jsoneditor.options.disable_collapse) { |
| this.toggle_button.style.display = 'none'; |
| } |
| |
| // Edit JSON Button |
| this.editjson_button = this.getButton('JSON','edit','Edit JSON'); |
| this.editjson_button.addEventListener('click',function(e) { |
| e.preventDefault(); |
| e.stopPropagation(); |
| self.toggleEditJSON(); |
| }); |
| this.editjson_controls.appendChild(this.editjson_button); |
| this.editjson_controls.appendChild(this.editjson_holder); |
| |
| // Edit JSON Buttton disabled |
| if(this.schema.options && typeof this.schema.options.disable_edit_json !== "undefined") { |
| if(this.schema.options.disable_edit_json) this.editjson_button.style.display = 'none'; |
| } |
| else if(this.jsoneditor.options.disable_edit_json) { |
| this.editjson_button.style.display = 'none'; |
| } |
| |
| // Object Properties Button |
| this.addproperty_button = this.getButton('Properties','edit','Object Properties'); |
| this.addproperty_button.addEventListener('click',function(e) { |
| e.preventDefault(); |
| e.stopPropagation(); |
| self.toggleAddProperty(); |
| }); |
| this.addproperty_controls.appendChild(this.addproperty_button); |
| this.addproperty_controls.appendChild(this.addproperty_holder); |
| this.refreshAddProperties(); |
| } |
| |
| // Fix table cell ordering |
| if(this.options.table_row) { |
| this.editor_holder = this.container; |
| $each(this.property_order,function(i,key) { |
| self.editor_holder.appendChild(self.editors[key].container); |
| }); |
| } |
| // Layout object editors in grid if needed |
| else { |
| // Initial layout |
| this.layoutEditors(); |
| // Do it again now that we know the approximate heights of elements |
| this.layoutEditors(); |
| } |
| }, |
| showEditJSON: function() { |
| if(!this.editjson_holder) return; |
| this.hideAddProperty(); |
| |
| // Position the form directly beneath the button |
| // TODO: edge detection |
| this.editjson_holder.style.left = this.editjson_button.offsetLeft+"px"; |
| this.editjson_holder.style.top = this.editjson_button.offsetTop + this.editjson_button.offsetHeight+"px"; |
| |
| // Start the textarea with the current value |
| this.editjson_textarea.value = JSON.stringify(this.getValue(),null,2); |
| |
| // Disable the rest of the form while editing JSON |
| this.disable(); |
| |
| this.editjson_holder.style.display = ''; |
| this.editjson_button.disabled = false; |
| this.editing_json = true; |
| }, |
| hideEditJSON: function() { |
| if(!this.editjson_holder) return; |
| if(!this.editing_json) return; |
| |
| this.editjson_holder.style.display = 'none'; |
| this.enable(); |
| this.editing_json = false; |
| }, |
| saveJSON: function() { |
| if(!this.editjson_holder) return; |
| |
| try { |
| var json = JSON.parse(this.editjson_textarea.value); |
| this.setValue(json); |
| this.hideEditJSON(); |
| } |
| catch(e) { |
| window.alert('invalid JSON'); |
| throw e; |
| } |
| }, |
| toggleEditJSON: function() { |
| if(this.editing_json) this.hideEditJSON(); |
| else this.showEditJSON(); |
| }, |
| insertPropertyControlUsingPropertyOrder: function (property, control, container) { |
| var propertyOrder; |
| if (this.schema.properties[property]) |
| propertyOrder = this.schema.properties[property].propertyOrder; |
| if (typeof propertyOrder !== "number") propertyOrder = 1000; |
| control.propertyOrder = propertyOrder; |
| |
| for (var i = 0; i < container.childNodes.length; i++) { |
| var child = container.childNodes[i]; |
| if (control.propertyOrder < child.propertyOrder) { |
| this.addproperty_list.insertBefore(control, child); |
| control = null; |
| break; |
| } |
| } |
| if (control) { |
| this.addproperty_list.appendChild(control); |
| } |
| }, |
| addPropertyCheckbox: function(key) { |
| var self = this; |
| var checkbox, label, labelText, control; |
| |
| checkbox = self.theme.getCheckbox(); |
| checkbox.style.width = 'auto'; |
| |
| if (this.schema.properties[key] && this.schema.properties[key].title) |
| labelText = this.schema.properties[key].title; |
| else |
| labelText = key; |
| |
| label = self.theme.getCheckboxLabel(labelText); |
| |
| control = self.theme.getFormControl(label,checkbox); |
| control.style.paddingBottom = control.style.marginBottom = control.style.paddingTop = control.style.marginTop = 0; |
| control.style.height = 'auto'; |
| //control.style.overflowY = 'hidden'; |
| |
| this.insertPropertyControlUsingPropertyOrder(key, control, this.addproperty_list); |
| |
| checkbox.checked = key in this.editors; |
| checkbox.addEventListener('change',function() { |
| if(checkbox.checked) { |
| self.addObjectProperty(key); |
| } |
| else { |
| self.removeObjectProperty(key); |
| } |
| self.onChange(true); |
| }); |
| self.addproperty_checkboxes[key] = checkbox; |
| |
| return checkbox; |
| }, |
| showAddProperty: function() { |
| if(!this.addproperty_holder) return; |
| this.hideEditJSON(); |
| |
| // Position the form directly beneath the button |
| // TODO: edge detection |
| this.addproperty_holder.style.left = this.addproperty_button.offsetLeft+"px"; |
| this.addproperty_holder.style.top = this.addproperty_button.offsetTop + this.addproperty_button.offsetHeight+"px"; |
| |
| // Disable the rest of the form while editing JSON |
| this.disable(); |
| |
| this.adding_property = true; |
| this.addproperty_button.disabled = false; |
| this.addproperty_holder.style.display = ''; |
| this.refreshAddProperties(); |
| }, |
| hideAddProperty: function() { |
| if(!this.addproperty_holder) return; |
| if(!this.adding_property) return; |
| |
| this.addproperty_holder.style.display = 'none'; |
| this.enable(); |
| |
| this.adding_property = false; |
| }, |
| toggleAddProperty: function() { |
| if(this.adding_property) this.hideAddProperty(); |
| else this.showAddProperty(); |
| }, |
| removeObjectProperty: function(property) { |
| if(this.editors[property]) { |
| this.editors[property].unregister(); |
| delete this.editors[property]; |
| |
| this.refreshValue(); |
| this.layoutEditors(); |
| } |
| }, |
| addObjectProperty: function(name, prebuild_only) { |
| var self = this; |
| |
| // Property is already added |
| if(this.editors[name]) return; |
| |
| // Property was added before and is cached |
| if(this.cached_editors[name]) { |
| this.editors[name] = this.cached_editors[name]; |
| if(prebuild_only) return; |
| this.editors[name].register(); |
| } |
| // New property |
| else { |
| if(!this.canHaveAdditionalProperties() && (!this.schema.properties || !this.schema.properties[name])) { |
| return; |
| } |
| |
| var schema = self.getPropertySchema(name); |
| |
| |
| // Add the property |
| var editor = self.jsoneditor.getEditorClass(schema); |
| |
| self.editors[name] = self.jsoneditor.createEditor(editor,{ |
| jsoneditor: self.jsoneditor, |
| schema: schema, |
| path: self.path+'.'+name, |
| parent: self |
| }); |
| self.editors[name].preBuild(); |
| |
| if(!prebuild_only) { |
| var holder = self.theme.getChildEditorHolder(); |
| self.editor_holder.appendChild(holder); |
| self.editors[name].setContainer(holder); |
| self.editors[name].build(); |
| self.editors[name].postBuild(); |
| } |
| |
| self.cached_editors[name] = self.editors[name]; |
| } |
| |
| // If we're only prebuilding the editors, don't refresh values |
| if(!prebuild_only) { |
| self.refreshValue(); |
| self.layoutEditors(); |
| } |
| }, |
| onChildEditorChange: function(editor) { |
| this.refreshValue(); |
| this._super(editor); |
| }, |
| canHaveAdditionalProperties: function() { |
| if (typeof this.schema.additionalProperties === "boolean") { |
| return this.schema.additionalProperties; |
| } |
| return !this.jsoneditor.options.no_additional_properties; |
| }, |
| destroy: function() { |
| $each(this.cached_editors, function(i,el) { |
| el.destroy(); |
| }); |
| if(this.editor_holder) this.editor_holder.innerHTML = ''; |
| if(this.title && this.title.parentNode) this.title.parentNode.removeChild(this.title); |
| if(this.error_holder && this.error_holder.parentNode) this.error_holder.parentNode.removeChild(this.error_holder); |
| |
| this.editors = null; |
| this.cached_editors = null; |
| if(this.editor_holder && this.editor_holder.parentNode) this.editor_holder.parentNode.removeChild(this.editor_holder); |
| this.editor_holder = null; |
| |
| this._super(); |
| }, |
| getValue: function() { |
| var result = this._super(); |
| if(this.jsoneditor.options.remove_empty_properties || this.options.remove_empty_properties) { |
| for(var i in result) { |
| if(result.hasOwnProperty(i)) { |
| if(!result[i]) delete result[i]; |
| } |
| } |
| } |
| return result; |
| }, |
| refreshValue: function() { |
| this.value = {}; |
| var self = this; |
| |
| for(var i in this.editors) { |
| if(!this.editors.hasOwnProperty(i)) continue; |
| this.value[i] = this.editors[i].getValue(); |
| } |
| |
| if(this.adding_property) this.refreshAddProperties(); |
| }, |
| refreshAddProperties: function() { |
| if(this.options.disable_properties || (this.options.disable_properties !== false && this.jsoneditor.options.disable_properties)) { |
| this.addproperty_controls.style.display = 'none'; |
| return; |
| } |
| |
| var can_add = false, can_remove = false, num_props = 0, i, show_modal = false; |
| |
| // Get number of editors |
| for(i in this.editors) { |
| if(!this.editors.hasOwnProperty(i)) continue; |
| num_props++; |
| } |
| |
| // Determine if we can add back removed properties |
| can_add = this.canHaveAdditionalProperties() && !(typeof this.schema.maxProperties !== "undefined" && num_props >= this.schema.maxProperties); |
| |
| if(this.addproperty_checkboxes) { |
| this.addproperty_list.innerHTML = ''; |
| } |
| this.addproperty_checkboxes = {}; |
| |
| // Check for which editors can't be removed or added back |
| for(i in this.cached_editors) { |
| if(!this.cached_editors.hasOwnProperty(i)) continue; |
| |
| this.addPropertyCheckbox(i); |
| |
| if(this.isRequired(this.cached_editors[i]) && i in this.editors) { |
| this.addproperty_checkboxes[i].disabled = true; |
| } |
| |
| if(typeof this.schema.minProperties !== "undefined" && num_props <= this.schema.minProperties) { |
| this.addproperty_checkboxes[i].disabled = this.addproperty_checkboxes[i].checked; |
| if(!this.addproperty_checkboxes[i].checked) show_modal = true; |
| } |
| else if(!(i in this.editors)) { |
| if(!can_add && !this.schema.properties.hasOwnProperty(i)) { |
| this.addproperty_checkboxes[i].disabled = true; |
| } |
| else { |
| this.addproperty_checkboxes[i].disabled = false; |
| show_modal = true; |
| } |
| } |
| else { |
| show_modal = true; |
| can_remove = true; |
| } |
| } |
| |
| if(this.canHaveAdditionalProperties()) { |
| show_modal = true; |
| } |
| |
| // Additional addproperty checkboxes not tied to a current editor |
| for(i in this.schema.properties) { |
| if(!this.schema.properties.hasOwnProperty(i)) continue; |
| if(this.cached_editors[i]) continue; |
| show_modal = true; |
| this.addPropertyCheckbox(i); |
| } |
| |
| // If no editors can be added or removed, hide the modal button |
| if(!show_modal) { |
| this.hideAddProperty(); |
| this.addproperty_controls.style.display = 'none'; |
| } |
| // If additional properties are disabled |
| else if(!this.canHaveAdditionalProperties()) { |
| this.addproperty_add.style.display = 'none'; |
| this.addproperty_input.style.display = 'none'; |
| } |
| // If no new properties can be added |
| else if(!can_add) { |
| this.addproperty_add.disabled = true; |
| } |
| // If new properties can be added |
| else { |
| this.addproperty_add.disabled = false; |
| } |
| }, |
| isRequired: function(editor) { |
| if(typeof editor.schema.required === "boolean") return editor.schema.required; |
| else if(Array.isArray(this.schema.required)) return this.schema.required.indexOf(editor.key) > -1; |
| else if(this.jsoneditor.options.required_by_default) return true; |
| else return false; |
| }, |
| setValue: function(value, initial) { |
| var self = this; |
| value = value || {}; |
| |
| if(typeof value !== "object" || Array.isArray(value)) value = {}; |
| |
| // First, set the values for all of the defined properties |
| $each(this.cached_editors, function(i,editor) { |
| // Value explicitly set |
| if(typeof value[i] !== "undefined") { |
| self.addObjectProperty(i); |
| editor.setValue(value[i],initial); |
| } |
| // Otherwise, remove value unless this is the initial set or it's required |
| else if(!initial && !self.isRequired(editor)) { |
| self.removeObjectProperty(i); |
| } |
| // Otherwise, set the value to the default |
| else { |
| editor.setValue(editor.getDefault(),initial); |
| } |
| }); |
| |
| $each(value, function(i,val) { |
| if(!self.cached_editors[i]) { |
| self.addObjectProperty(i); |
| if(self.editors[i]) self.editors[i].setValue(val,initial); |
| } |
| }); |
| |
| this.refreshValue(); |
| this.layoutEditors(); |
| this.onChange(); |
| }, |
| showValidationErrors: function(errors) { |
| var self = this; |
| |
| // Get all the errors that pertain to this editor |
| var my_errors = []; |
| var other_errors = []; |
| $each(errors, function(i,error) { |
| if(error.path === self.path) { |
| my_errors.push(error); |
| } |
| else { |
| other_errors.push(error); |
| } |
| }); |
| |
| // Show errors for this editor |
| if(this.error_holder) { |
| if(my_errors.length) { |
| var message = []; |
| this.error_holder.innerHTML = ''; |
| this.error_holder.style.display = ''; |
| $each(my_errors, function(i,error) { |
| self.error_holder.appendChild(self.theme.getErrorMessage(error.message)); |
| }); |
| } |
| // Hide error area |
| else { |
| this.error_holder.style.display = 'none'; |
| } |
| } |
| |
| // Show error for the table row if this is inside a table |
| if(this.options.table_row) { |
| if(my_errors.length) { |
| this.theme.addTableRowError(this.container); |
| } |
| else { |
| this.theme.removeTableRowError(this.container); |
| } |
| } |
| |
| // Show errors for child editors |
| $each(this.editors, function(i,editor) { |
| editor.showValidationErrors(other_errors); |
| }); |
| } |
| }); |
| |
| JSONEditor.defaults.editors.array = JSONEditor.AbstractEditor.extend({ |
| getDefault: function() { |
| return this.schema["default"] || []; |
| }, |
| register: function() { |
| this._super(); |
| if(this.rows) { |
| for(var i=0; i<this.rows.length; i++) { |
| this.rows[i].register(); |
| } |
| } |
| }, |
| unregister: function() { |
| this._super(); |
| if(this.rows) { |
| for(var i=0; i<this.rows.length; i++) { |
| this.rows[i].unregister(); |
| } |
| } |
| }, |
| getNumColumns: function() { |
| var info = this.getItemInfo(0); |
| // Tabs require extra horizontal space |
| if(this.tabs_holder) { |
| return Math.max(Math.min(12,info.width+2),4); |
| } |
| else { |
| return info.width; |
| } |
| }, |
| enable: function() { |
| if(this.add_row_button) this.add_row_button.disabled = false; |
| if(this.remove_all_rows_button) this.remove_all_rows_button.disabled = false; |
| if(this.delete_last_row_button) this.delete_last_row_button.disabled = false; |
| |
| if(this.rows) { |
| for(var i=0; i<this.rows.length; i++) { |
| this.rows[i].enable(); |
| |
| if(this.rows[i].moveup_button) this.rows[i].moveup_button.disabled = false; |
| if(this.rows[i].movedown_button) this.rows[i].movedown_button.disabled = false; |
| if(this.rows[i].delete_button) this.rows[i].delete_button.disabled = false; |
| } |
| } |
| this._super(); |
| }, |
| disable: function() { |
| if(this.add_row_button) this.add_row_button.disabled = true; |
| if(this.remove_all_rows_button) this.remove_all_rows_button.disabled = true; |
| if(this.delete_last_row_button) this.delete_last_row_button.disabled = true; |
| |
| if(this.rows) { |
| for(var i=0; i<this.rows.length; i++) { |
| this.rows[i].disable(); |
| |
| if(this.rows[i].moveup_button) this.rows[i].moveup_button.disabled = true; |
| if(this.rows[i].movedown_button) this.rows[i].movedown_button.disabled = true; |
| if(this.rows[i].delete_button) this.rows[i].delete_button.disabled = true; |
| } |
| } |
| this._super(); |
| }, |
| preBuild: function() { |
| this._super(); |
| |
| this.rows = []; |
| this.row_cache = []; |
| |
| this.hide_delete_buttons = this.options.disable_array_delete || this.jsoneditor.options.disable_array_delete; |
| this.hide_delete_all_rows_buttons = this.hide_delete_buttons || this.options.disable_array_delete_all_rows || this.jsoneditor.options.disable_array_delete_all_rows; |
| this.hide_delete_last_row_buttons = this.hide_delete_buttons || this.options.disable_array_delete_last_row || this.jsoneditor.options.disable_array_delete_last_row; |
| this.hide_move_buttons = this.options.disable_array_reorder || this.jsoneditor.options.disable_array_reorder; |
| this.hide_add_button = this.options.disable_array_add || this.jsoneditor.options.disable_array_add; |
| }, |
| build: function() { |
| var self = this; |
| |
| if(!this.options.compact) { |
| this.header = document.createElement('span'); |
| this.header.textContent = this.getTitle(); |
| this.title = this.theme.getHeader(this.header); |
| this.container.appendChild(this.title); |
| this.title_controls = this.theme.getHeaderButtonHolder(); |
| this.title.appendChild(this.title_controls); |
| if(this.schema.description) { |
| this.description = this.theme.getDescription(this.schema.description); |
| this.container.appendChild(this.description); |
| } |
| 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(); |
| }); |
| |
| 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); |
| }); |
| |
| 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); |
| }); |
| |
| 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); |
| }); |
| |
| 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','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','Expand'); |
| } |
| }); |
| |
| // If it should start collapsed |
| if(this.options.collapsed) { |
| $trigger(this.toggle_button,'click'); |
| } |
| |
| // Collapse button disabled |
| if(this.schema.options && typeof this.schema.options.disable_collapse !== "undefined") { |
| if(this.schema.options.disable_collapse) this.toggle_button.style.display = 'none'; |
| } |
| else if(this.jsoneditor.options.disable_collapse) { |
| this.toggle_button.style.display = 'none'; |
| } |
| |
| // Add "new row" and "delete last" buttons below editor |
| this.add_row_button = this.getButton(this.getItemTitle(),'add',this.translate('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()); |
| self.rows[i].container.style.display = ''; |
| if(self.rows[i].tab) self.rows[i].tab.style.display = ''; |
| self.rows[i].register(); |
| } |
| else { |
| self.addRow(); |
| } |
| self.active_tab = self.rows[i].tab; |
| self.refreshTabs(); |
| self.refreshValue(); |
| self.onChange(true); |
| }); |
| self.controls.appendChild(this.add_row_button); |
| |
| this.delete_last_row_button = this.getButton(this.translate('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); |
| }); |
| 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); |
| }); |
| 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); |
| }); |
| 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); |
| }); |
| 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); |
| }); |
| 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','Collapse'); |
| } |
| else { |
| self.collapsed = true; |
| self.panel.style.display = 'none'; |
| self.setButtonText(this,'','expand','Expand'); |
| } |
| }); |
| |
| // If it should start collapsed |
| if(this.options.collapsed) { |
| $trigger(this.toggle_button,'click'); |
| } |
| |
| // Collapse button disabled |
| if(this.schema.options && typeof this.schema.options.disable_collapse !== "undefined") { |
| if(this.schema.options.disable_collapse) this.toggle_button.style.display = 'none'; |
| } |
| else if(this.jsoneditor.options.disable_collapse) { |
| this.toggle_button.style.display = 'none'; |
| } |
| } |
| |
| // Add "new row" and "delete last" buttons below editor |
| this.add_row_button = this.getButton(this.getItemTitle(),'add',this.translate('button_add_row_title',[this.getItemTitle()])); |
| this.add_row_button.addEventListener('click',function(e) { |
| e.preventDefault(); |
| e.stopPropagation(); |
| |
| self.addRow(); |
| self.refreshValue(); |
| self.refreshRowButtons(); |
| self.onChange(true); |
| }); |
| self.controls.appendChild(this.add_row_button); |
| |
| this.delete_last_row_button = this.getButton(this.translate('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); |
| }); |
| 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); |
| }); |
| 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); |
| } |
| |
| self.type = i; |
| |
| self.register(); |
| |
| var current_value = self.getValue(); |
| |
| $each(self.editors,function(type,editor) { |
| if(!editor) return; |
| if(self.type === type) { |
| if(self.keep_values) editor.setValue(current_value,true); |
| editor.container.style.display = ''; |
| } |
| else editor.container.style.display = 'none'; |
| }); |
| self.refreshValue(); |
| self.refreshHeaderText(); |
| }, |
| buildChildEditor: function(i) { |
| var self = this; |
| var type = this.types[i]; |
| var holder = self.theme.getChildEditorHolder(); |
| self.editor_holder.appendChild(holder); |
| |
| var schema; |
| |
| if(typeof type === "string") { |
| schema = $extend({},self.schema); |
| schema.type = type; |
| } |
| else { |
| schema = $extend({},self.schema,type); |
| schema = self.jsoneditor.expandRefs(schema); |
| |
| // If we need to merge `required` arrays |
| if(type.required && Array.isArray(type.required) && self.schema.required && Array.isArray(self.schema.required)) { |
| schema.required = self.schema.required.concat(type.required); |
| } |
| } |
| |
| var editor = self.jsoneditor.getEditorClass(schema); |
| |
| self.editors[i] = self.jsoneditor.createEditor(editor,{ |
| jsoneditor: self.jsoneditor, |
| schema: schema, |
| container: holder, |
| path: self.path, |
| parent: self, |
| required: true |
| }); |
| self.editors[i].preBuild(); |
| self.editors[i].build(); |
| self.editors[i].postBuild(); |
| |
| if(self.editors[i].header) self.editors[i].header.style.display = 'none'; |
| |
| self.editors[i].option = self.switcher_options[i]; |
| |
| holder.addEventListener('change_header_text',function() { |
| self.refreshHeaderText(); |
| }); |
| |
| if(i !== self.type) holder.style.display = 'none'; |
| }, |
| preBuild: function() { |
| var self = this; |
| |
| this.types = []; |
| this.type = 0; |
| this.editors = []; |
| this.validators = []; |
| |
| this.keep_values = true; |
| if(typeof this.jsoneditor.options.keep_oneof_values !== "undefined") this.keep_values = this.jsoneditor.options.keep_oneof_values; |
| if(typeof this.options.keep_oneof_values !== "undefined") this.keep_values = this.options.keep_oneof_values; |
| |
| if(this.schema.oneOf) { |
| this.oneOf = true; |
| this.types = this.schema.oneOf; |
| $each(this.types,function(i,oneof) { |
| //self.types[i] = self.jsoneditor.expandSchema(oneof); |
| }); |
| delete this.schema.oneOf; |
| } |
| else { |
| if(!this.schema.type || this.schema.type === "any") { |
| this.types = ['string','number','integer','boolean','object','array','null']; |
| |
| // If any of these primitive types are disallowed |
| if(this.schema.disallow) { |
| var disallow = this.schema.disallow; |
| if(typeof disallow !== 'object' || !(Array.isArray(disallow))) { |
| disallow = [disallow]; |
| } |
| var allowed_types = []; |
| $each(this.types,function(i,type) { |
| if(disallow.indexOf(type) === -1) allowed_types.push(type); |
| }); |
| this.types = allowed_types; |
| } |
| } |
| else if(Array.isArray(this.schema.type)) { |
| this.types = this.schema.type; |
| } |
| else { |
| this.types = [this.schema.type]; |
| } |
| delete this.schema.type; |
| } |
| |
| this.display_text = this.getDisplayText(this.types); |
| }, |
| build: function() { |
| var self = this; |
| var container = this.container; |
| |
| this.header = this.label = this.theme.getFormInputLabel(this.getTitle()); |
| this.container.appendChild(this.header); |
| |
| this.switcher = this.theme.getSwitcher(this.display_text); |
| container.appendChild(this.switcher); |
| this.switcher.addEventListener('change',function(e) { |
| e.preventDefault(); |
| e.stopPropagation(); |
| |
| self.switchEditor(self.display_text.indexOf(this.value)); |
| self.onChange(true); |
| }); |
| |
| this.editor_holder = document.createElement('div'); |
| container.appendChild(this.editor_holder); |
| |
| |
| var validator_options = {}; |
| if(self.jsoneditor.options.custom_validators) { |
| validator_options.custom_validators = self.jsoneditor.options.custom_validators; |
| } |
| |
| this.switcher_options = this.theme.getSwitcherOptions(this.switcher); |
| $each(this.types,function(i,type) { |
| self.editors[i] = false; |
| |
| var schema; |
| |
| if(typeof type === "string") { |
| schema = $extend({},self.schema); |
| schema.type = type; |
| } |
| else { |
| schema = $extend({},self.schema,type); |
| |
| // If we need to merge `required` arrays |
| if(type.required && Array.isArray(type.required) && self.schema.required && Array.isArray(self.schema.required)) { |
| schema.required = self.schema.required.concat(type.required); |
| } |
| } |
| |
| self.validators[i] = new JSONEditor.Validator(self.jsoneditor,schema,validator_options); |
| }); |
| |
| this.switchEditor(0); |
| }, |
| onChildEditorChange: function(editor) { |
| if(this.editors[this.type]) { |
| this.refreshValue(); |
| this.refreshHeaderText(); |
| } |
| |
| this._super(); |
| }, |
| refreshHeaderText: function() { |
| var display_text = this.getDisplayText(this.types); |
| $each(this.switcher_options, function(i,option) { |
| option.textContent = display_text[i]; |
| }); |
| }, |
| refreshValue: function() { |
| this.value = this.editors[this.type].getValue(); |
| }, |
| setValue: function(val,initial) { |
| // Determine type by getting the first one that validates |
| var self = this; |
| $each(this.validators, function(i,validator) { |
| if(!validator.validate(val).length) { |
| self.type = i; |
| self.switcher.value = self.display_text[i]; |
| return false; |
| } |
| }); |
| |
| this.switchEditor(this.type); |
| |
| this.editors[this.type].setValue(val,initial); |
| |
| this.refreshValue(); |
| self.onChange(); |
| }, |
| destroy: function() { |
| $each(this.editors, function(type,editor) { |
| if(editor) editor.destroy(); |
| }); |
| if(this.editor_holder && this.editor_holder.parentNode) this.editor_holder.parentNode.removeChild(this.editor_holder); |
| if(this.switcher && this.switcher.parentNode) this.switcher.parentNode.removeChild(this.switcher); |
| this._super(); |
| }, |
| showValidationErrors: function(errors) { |
| var self = this; |
| |
| // oneOf error paths need to remove the oneOf[i] part before passing to child editors |
| if(this.oneOf) { |
| $each(this.editors,function(i,editor) { |
| if(!editor) return; |
| var check = self.path+'.oneOf['+i+']'; |
| var new_errors = []; |
| $each(errors, function(j,error) { |
| if(error.path.substr(0,check.length)===check) { |
| var new_error = $extend({},error); |
| new_error.path = self.path+new_error.path.substr(check.length); |
| new_errors.push(new_error); |
| } |
| }); |
| |
| editor.showValidationErrors(new_errors); |
| }); |
| } |
| else { |
| $each(this.editors,function(type,editor) { |
| if(!editor) return; |
| editor.showValidationErrors(errors); |
| }); |
| } |
| } |
| }); |
| |
| // Enum Editor (used for objects and arrays with enumerated values) |
| JSONEditor.defaults.editors["enum"] = JSONEditor.AbstractEditor.extend({ |
| getNumColumns: function() { |
| return 4; |
| }, |
| build: function() { |
| var container = this.container; |
| this.title = this.header = this.label = this.theme.getFormInputLabel(this.getTitle()); |
| this.container.appendChild(this.title); |
| |
| this.options.enum_titles = this.options.enum_titles || []; |
| |
| this["enum"] = this.schema["enum"]; |
| this.selected = 0; |
| this.select_options = []; |
| this.html_values = []; |
| |
| var self = this; |
| for(var i=0; i<this["enum"].length; i++) { |
| this.select_options[i] = this.options.enum_titles[i] || "Value "+(i+1); |
| this.html_values[i] = this.getHTML(this["enum"][i]); |
| } |
| |
| // Switcher |
| this.switcher = this.theme.getSwitcher(this.select_options); |
| this.container.appendChild(this.switcher); |
| |
| // Display area |
| this.display_area = this.theme.getIndentedPanel(); |
| this.container.appendChild(this.display_area); |
| |
| if(this.options.hide_display) this.display_area.style.display = "none"; |
| |
| this.switcher.addEventListener('change',function() { |
| self.selected = self.select_options.indexOf(this.value); |
| self.value = self["enum"][self.selected]; |
| self.refreshValue(); |
| self.onChange(true); |
| }); |
| this.value = this["enum"][0]; |
| this.refreshValue(); |
| |
| if(this["enum"].length === 1) this.switcher.style.display = 'none'; |
| }, |
| refreshValue: function() { |
| var self = this; |
| self.selected = -1; |
| var stringified = JSON.stringify(this.value); |
| $each(this["enum"], function(i, el) { |
| if(stringified === JSON.stringify(el)) { |
| self.selected = i; |
| return false; |
| } |
| }); |
| |
| if(self.selected<0) { |
| self.setValue(self["enum"][0]); |
| return; |
| } |
| |
| this.switcher.value = this.select_options[this.selected]; |
| this.display_area.innerHTML = this.html_values[this.selected]; |
| }, |
| enable: function() { |
| if(!this.always_disabled) this.switcher.disabled = false; |
| this._super(); |
| }, |
| disable: function() { |
| this.switcher.disabled = true; |
| this._super(); |
| }, |
| getHTML: function(el) { |
| var self = this; |
| |
| if(el === null) { |
| return '<em>null</em>'; |
| } |
| // Array or Object |
| else if(typeof el === "object") { |
| // TODO: use theme |
| var ret = ''; |
| |
| $each(el,function(i,child) { |
| var html = self.getHTML(child); |
| |
| // Add the keys to object children |
| if(!(Array.isArray(el))) { |
| // TODO: use theme |
| html = '<div><em>'+i+'</em>: '+html+'</div>'; |
| } |
| |
| // TODO: use theme |
| ret += '<li>'+html+'</li>'; |
| }); |
| |
| if(Array.isArray(el)) ret = '<ol>'+ret+'</ol>'; |
| else ret = "<ul style='margin-top:0;margin-bottom:0;padding-top:0;padding-bottom:0;'>"+ret+'</ul>'; |
| |
| return ret; |
| } |
| // Boolean |
| else if(typeof el === "boolean") { |
| return el? 'true' : 'false'; |
| } |
| // String |
| else if(typeof el === "string") { |
| return el.replace(/&/g,'&').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 = []; |
| |
| // 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(); |
| }); |
| |
| 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 = []; |
| |
| // 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(); |
| |
| this.select_options = {}; |
| this.select_values = {}; |
| |
| var items_schema = this.jsoneditor.expandRefs(this.schema.items || {}); |
| |
| var e = items_schema["enum"] || []; |
| this.option_keys = []; |
| 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.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_keys[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.input.multiple = true; |
| this.input.size = Math.min(10,this.option_keys.length); |
| |
| for(i=0; i<this.option_keys.length; i++) { |
| this.select_options[this.option_keys[i]] = this.input.children[i]; |
| } |
| |
| if(this.schema.readOnly || this.schema.readonly) { |
| this.always_disabled = true; |
| this.input.disabled = true; |
| } |
| |
| this.control = this.theme.getFormControl(this.label, this.input, this.description); |
| } |
| |
| this.container.appendChild(this.control); |
| this.control.addEventListener('change',function(e) { |
| e.preventDefault(); |
| e.stopPropagation(); |
| |
| var new_value = []; |
| for(i = 0; i<self.option_keys.length; i++) { |
| if(self.select_options[self.option_keys[i]].selected || self.select_options[self.option_keys[i]].checked) new_value.push(self.select_values[self.option_keys[i]]); |
| } |
| |
| self.updateValue(new_value); |
| self.onChange(true); |
| }); |
| }, |
| setValue: function(value, initial) { |
| var i; |
| value = value || []; |
| if(typeof value !== "object") value = [value]; |
| else if(!(Array.isArray(value))) value = []; |
| |
| // Make sure we are dealing with an array of strings so we can check for strict equality |
| for(i=0; i<value.length; i++) { |
| if(typeof value[i] !== "string") value[i] += ""; |
| } |
| |
| // Update selected status of options |
| for(i in this.select_options) { |
| if(!this.select_options.hasOwnProperty(i)) continue; |
| |
| this.select_options[i][this.input_type === "select"? "selected" : "checked"] = (value.indexOf(i) !== -1); |
| } |
| |
| this.updateValue(value); |
| this.onChange(); |
| }, |
| setupSelect2: function() { |
| if(window.jQuery && window.jQuery.fn && window.jQuery.fn.select2) { |
| var options = window.jQuery.extend({},JSONEditor.plugins.select2); |
| if(this.schema.options && this.schema.options.select2_options) options = $extend(options,this.schema.options.select2_options); |
| this.select2 = window.jQuery(this.input).select2(options); |
| var self = this; |
| this.select2.on('select2-blur',function() { |
| var val =self.select2.select2('val'); |
| self.value = val; |
| self.onChange(true); |
| }); |
| } |
| else { |
| this.select2 = null; |
| } |
| }, |
| onInputChange: function() { |
| this.value = this.input.value; |
| this.onChange(true); |
| }, |
| postBuild: function() { |
| this._super(); |
| this.setupSelect2(); |
| }, |
| register: function() { |
| this._super(); |
| if(!this.input) return; |
| this.input.setAttribute('name',this.formname); |
| }, |
| unregister: function() { |
| this._super(); |
| if(!this.input) return; |
| this.input.removeAttribute('name'); |
| }, |
| getNumColumns: function() { |
| var longest_text = this.getTitle().length; |
| for(var i in this.select_values) { |
| if(!this.select_values.hasOwnProperty(i)) continue; |
| longest_text = Math.max(longest_text,(this.select_values[i]+"").length+4); |
| } |
| |
| return Math.min(12,Math.max(longest_text/7,2)); |
| }, |
| updateValue: function(value) { |
| var changed = false; |
| var new_value = []; |
| for(var i=0; i<value.length; i++) { |
| if(!this.select_options[value[i]+""]) { |
| changed = true; |
| continue; |
| } |
| var sanitized = this.sanitize(this.select_values[value[i]]); |
| new_value.push(sanitized); |
| if(sanitized !== value[i]) changed = true; |
| } |
| this.value = new_value; |
| if(this.select2) this.select2.select2('val',this.value); |
| return changed; |
| }, |
| sanitize: function(value) { |
| if(this.schema.items.type === "number") { |
| return 1*value; |
| } |
| else if(this.schema.items.type === "integer") { |
| return Math.floor(value*1); |
| } |
| else { |
| return ""+value; |
| } |
| }, |
| enable: function() { |
| if(!this.always_disabled) { |
| if(this.input) { |
| this.input.disabled = false; |
| } |
| else if(this.inputs) { |
| for(var i in this.inputs) { |
| if(!this.inputs.hasOwnProperty(i)) continue; |
| this.inputs[i].disabled = false; |
| } |
| } |
| if(this.select2) this.select2.select2("enable",true); |
| } |
| this._super(); |
| }, |
| disable: function() { |
| if(this.input) { |
| this.input.disabled = true; |
| } |
| else if(this.inputs) { |
| for(var i in this.inputs) { |
| if(!this.inputs.hasOwnProperty(i)) continue; |
| this.inputs[i].disabled = true; |
| } |
| } |
| if(this.select2) this.select2.select2("enable",false); |
| this._super(); |
| }, |
| destroy: function() { |
| if(this.select2) { |
| this.select2.select2('destroy'); |
| this.select2 = null; |
| } |
| this._super(); |
| } |
| }); |
| |
| JSONEditor.defaults.editors.base64 = JSONEditor.AbstractEditor.extend({ |
| getNumColumns: function() { |
| return 4; |
| }, |
| build: function() { |
| var self = this; |
| this.title = this.header = this.label = this.theme.getFormInputLabel(this.getTitle()); |
| |
| // Input that holds the base64 string |
| this.input = this.theme.getFormInputField('hidden'); |
| this.container.appendChild(this.input); |
| |
| // Don't show uploader if this is readonly |
| if(!this.schema.readOnly && !this.schema.readonly) { |
| if(!window.FileReader) throw "FileReader required for base64 editor"; |
| |
| // File uploader |
| this.uploader = this.theme.getFormInputField('file'); |
| |
| this.uploader.addEventListener('change',function(e) { |
| e.preventDefault(); |
| e.stopPropagation(); |
| |
| if(this.files && this.files.length) { |
| var fr = new FileReader(); |
| fr.onload = function(evt) { |
| self.value = evt.target.result; |
| self.refreshPreview(); |
| self.onChange(true); |
| fr = null; |
| }; |
| fr.readAsDataURL(this.files[0]); |
| } |
| }); |
| } |
| |
| this.preview = this.theme.getFormInputDescription(this.schema.description); |
| this.container.appendChild(this.preview); |
| |
| this.control = this.theme.getFormControl(this.label, this.uploader||this.input, this.preview); |
| this.container.appendChild(this.control); |
| }, |
| refreshPreview: function() { |
| if(this.last_preview === this.value) return; |
| this.last_preview = this.value; |
| |
| this.preview.innerHTML = ''; |
| |
| if(!this.value) return; |
| |
| var mime = this.value.match(/^data:([^;,]+)[;,]/); |
| if(mime) mime = mime[1]; |
| |
| if(!mime) { |
| this.preview.innerHTML = '<em>Invalid data URI</em>'; |
| } |
| else { |
| this.preview.innerHTML = '<strong>Type:</strong> '+mime+', <strong>Size:</strong> '+Math.floor((this.value.length-this.value.split(',')[0].length-1)/1.33333)+' bytes'; |
| if(mime.substr(0,5)==="image") { |
| this.preview.innerHTML += '<br>'; |
| var img = document.createElement('img'); |
| img.style.maxWidth = '100%'; |
| img.style.maxHeight = '100px'; |
| img.src = this.value; |
| this.preview.appendChild(img); |
| } |
| } |
| }, |
| enable: function() { |
| if(this.uploader) this.uploader.disabled = false; |
| this._super(); |
| }, |
| disable: function() { |
| if(this.uploader) this.uploader.disabled = true; |
| this._super(); |
| }, |
| setValue: function(val) { |
| if(this.value !== val) { |
| this.value = val; |
| this.input.value = this.value; |
| this.refreshPreview(); |
| this.onChange(); |
| } |
| }, |
| destroy: function() { |
| if(this.preview && this.preview.parentNode) this.preview.parentNode.removeChild(this.preview); |
| if(this.title && this.title.parentNode) this.title.parentNode.removeChild(this.title); |
| if(this.input && this.input.parentNode) this.input.parentNode.removeChild(this.input); |
| if(this.uploader && this.uploader.parentNode) this.uploader.parentNode.removeChild(this.uploader); |
| |
| this._super(); |
| } |
| }); |
| |
| JSONEditor.defaults.editors.upload = JSONEditor.AbstractEditor.extend({ |
| getNumColumns: function() { |
| return 4; |
| }, |
| build: function() { |
| var self = this; |
| this.title = this.header = this.label = this.theme.getFormInputLabel(this.getTitle()); |
| |
| // Input that holds the base64 string |
| this.input = this.theme.getFormInputField('hidden'); |
| this.container.appendChild(this.input); |
| |
| // Don't show uploader if this is readonly |
| if(!this.schema.readOnly && !this.schema.readonly) { |
| |
| if(!this.jsoneditor.options.upload) throw "Upload handler required for upload editor"; |
| |
| // File uploader |
| this.uploader = this.theme.getFormInputField('file'); |
| |
| this.uploader.addEventListener('change',function(e) { |
| e.preventDefault(); |
| e.stopPropagation(); |
| |
| if(this.files && this.files.length) { |
| var fr = new FileReader(); |
| fr.onload = function(evt) { |
| self.preview_value = evt.target.result; |
| self.refreshPreview(); |
| self.onChange(true); |
| fr = null; |
| }; |
| fr.readAsDataURL(this.files[0]); |
| } |
| }); |
| } |
| |
| var description = this.schema.description; |
| if (!description) description = ''; |
| |
| this.preview = this.theme.getFormInputDescription(description); |
| this.container.appendChild(this.preview); |
| |
| this.control = this.theme.getFormControl(this.label, this.uploader||this.input, this.preview); |
| this.container.appendChild(this.control); |
| }, |
| refreshPreview: function() { |
| if(this.last_preview === this.preview_value) return; |
| this.last_preview = this.preview_value; |
| |
| this.preview.innerHTML = ''; |
| |
| if(!this.preview_value) return; |
| |
| var self = this; |
| |
| var 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); |
| }); |
| |
| 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 (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 col-md-2'></div><div class='col-md-10'></div>"; |
| el.className = 'rows'; |
| return el; |
| }, |
| getTab: function(text) { |
| var el = document.createElement('a'); |
| el.className = 'list-group-item'; |
| el.setAttribute('href','#'); |
| el.appendChild(text); |
| return el; |
| }, |
| markTabActive: function(tab) { |
| tab.className += ' active'; |
| }, |
| markTabInactive: function(tab) { |
| tab.className = tab.className.replace(/\s?active/g,''); |
| }, |
| getProgressBar: function() { |
| var min = 0, max = 100, start = 0; |
| |
| var container = document.createElement('div'); |
| container.className = 'progress'; |
| |
| var bar = document.createElement('div'); |
| bar.className = 'progress-bar'; |
| bar.setAttribute('role', 'progressbar'); |
| bar.setAttribute('aria-valuenow', start); |
| bar.setAttribute('aria-valuemin', min); |
| bar.setAttribute('aria-valuenax', max); |
| bar.innerHTML = start + "%"; |
| container.appendChild(bar); |
| |
| return container; |
| }, |
| updateProgressBar: function(progressBar, progress) { |
| if (!progressBar) return; |
| |
| var bar = progressBar.firstChild; |
| var percentage = progress + "%"; |
| bar.setAttribute('aria-valuenow', progress); |
| bar.style.width = percentage; |
| bar.innerHTML = percentage; |
| }, |
| updateProgressBarUnknown: function(progressBar) { |
| if (!progressBar) return; |
| |
| var bar = progressBar.firstChild; |
| progressBar.className = 'progress progress-striped active'; |
| bar.removeAttribute('aria-valuenow'); |
| bar.style.width = '100%'; |
| bar.innerHTML = ''; |
| } |
| }); |
| |
| // 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" |
| }; |
| |
| // Miscellaneous Plugin Settings |
| JSONEditor.plugins = { |
| ace: { |
| theme: '' |
| }, |
| epiceditor: { |
| |
| }, |
| sceditor: { |
| |
| }, |
| select2: { |
| |
| }, |
| selectize: { |
| } |
| }; |
| |
| // Default per-editor options |
| for(var i in JSONEditor.defaults.editors) { |
| if(!JSONEditor.defaults.editors.hasOwnProperty(i)) continue; |
| JSONEditor.defaults.editors[i].options = JSONEditor.defaults.editors.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` |
| if(schema.oneOf) 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 |