http://git-wip-us.apache.org/repos/asf/stratos/blob/c6a485f9/components/org.apache.stratos.manager.console/console/themes/theme0/js/JSONEditor-0.7.12/jsoneditor-0.7.12.js
----------------------------------------------------------------------
diff --git 
a/components/org.apache.stratos.manager.console/console/themes/theme0/js/JSONEditor-0.7.12/jsoneditor-0.7.12.js
 
b/components/org.apache.stratos.manager.console/console/themes/theme0/js/JSONEditor-0.7.12/jsoneditor-0.7.12.js
new file mode 100644
index 0000000..f216d6c
--- /dev/null
+++ 
b/components/org.apache.stratos.manager.console/console/themes/theme0/js/JSONEditor-0.7.12/jsoneditor-0.7.12.js
@@ -0,0 +1,7001 @@
+/*! JSON Editor v0.7.12 - JSON Schema -> HTML Editor
+ * By Jeremy Dorn - https://github.com/jdorn/json-editor/
+ * Released under the MIT license
+ *
+ * Date: 2014-10-05
+ */
+
+/**
+ * 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]';
+            };
+        }
+    }());
+    var $isplainobject = function( obj ) {
+        // Not own constructor property must be Object
+        if ( obj.constructor &&
+            !obj.hasOwnProperty('constructor') &&
+            !obj.constructor.prototype.hasOwnProperty('isPrototypeOf')) {
+            return false;
+        }
+
+        // Own properties are enumerated firstly, so to speed up,
+        // if last one is own, then all properties are own.
+
+        var key;
+        for ( key in obj ) {}
+
+        return key === undefined || obj.hasOwnProperty(key);
+    };
+
+    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) return;
+        var i;
+        if(typeof obj.length !== 'undefined') {
+            for(i=0; i<obj.length; i++) {
+                if(callback(i,obj[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) {
+        options = $extend({},JSONEditor.defaults.options,options||{});
+        this.element = element;
+        this.options = options;
+        this.init();
+    };
+    JSONEditor.prototype = {
+        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);
+                self.validator = new JSONEditor.Validator(self);
+
+                // 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() {
+                    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;
+
+                // 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 && 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;
+                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) {
+            this.jsoneditor = jsoneditor;
+            this.schema = schema || this.jsoneditor.schema;
+            this.options = {};
+            this.translate = this.jsoneditor.translate || 
JSONEditor.defaults.translate;
+        },
+        validate: function(value) {
+            return this._validateSchema(this.schema, value);
+        },
+        _validateSchema: function(schema,value,path) {
+            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) {
+                    valid = value / (schema.multipleOf || schema.divisibleBy);
+                    if(valid !== Math.floor(valid)) {
+                        errors.push({
+                            path: path,
+                            property: schema.multipleOf? 'multipleOf' : 
'divisibleBy',
+                            message: this.translate('error_multipleOf', 
[schema.multipleOf || schema.divisibleBy])
+                        });
+                    }
+                }
+
+                // `maximum`
+                if(schema.hasOwnProperty('maximum')) {
+                    if(schema.exclusiveMaximum && value >= schema.maximum) {
+                        errors.push({
+                            path: path,
+                            property: 'maximum',
+                            message: this.translate('error_maximum_excl', 
[schema.maximum])
+                        });
+                    }
+                    else if(!schema.exclusiveMaximum && value > 
schema.maximum) {
+                        errors.push({
+                            path: path,
+                            property: 'maximum',
+                            message: this.translate('error_maximum_incl', 
[schema.maximum])
+                        });
+                    }
+                }
+
+                // `minimum`
+                if(schema.hasOwnProperty('minimum')) {
+                    if(schema.exclusiveMinimum && value <= schema.minimum) {
+                        errors.push({
+                            path: path,
+                            property: 'minimum',
+                            message: this.translate('error_minimum_excl', 
[schema.minimum])
+                        });
+                    }
+                    else if(!schema.exclusiveMinimum && value < 
schema.minimum) {
+                        errors.push({
+                            path: path,
+                            property: 'minimum',
+                            message: this.translate('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')
+                        });
+                    }
+                }
+            }
+            // 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
+            $each(JSONEditor.defaults.custom_validators,function(i,validator) {
+                errors = errors.concat(validator(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.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);
+
+            // 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 {
+                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;
+                });
+            }
+
+            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) {
+                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;
+        },
+        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) 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;
+
+            // 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',
+                    '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.setAttribute('class',this.container.getAttribute('class')+' 
compact');
+
+            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.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();
+            });
+
+            // 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.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,
+                        //edito

<TRUNCATED>

Reply via email to