From 63a4094e4aba08def50b48012a4b7ae0e2f1fac0 Mon Sep 17 00:00:00 2001 From: Andre Kampert Date: Mon, 25 Aug 2014 17:52:02 +0200 Subject: [PATCH 1/5] Add editor for integer rating --- Gruntfile.js | 1 + examples/basic.html | 10 ++- src/defaults.js | 5 ++ src/editors/rating.js | 150 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 src/editors/rating.js diff --git a/Gruntfile.js b/Gruntfile.js index 59f5dbedd..d225e2ce3 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -27,6 +27,7 @@ module.exports = function(grunt) { 'src/editors/string.js', 'src/editors/number.js', 'src/editors/integer.js', + 'src/editors/rating.js', 'src/editors/object.js', 'src/editors/array.js', 'src/editors/table.js', diff --git a/examples/basic.html b/examples/basic.html index 6505025c2..1daadf5f9 100644 --- a/examples/basic.html +++ b/examples/basic.html @@ -41,11 +41,19 @@

Basic JSON Editor Example

2010,2011,2012,2013,2014 ], default: 2008 + }, + safety: { + type: "integer", + format: "rating", + maximum: "5", + exclusiveMaximum: false, + options: { + } } } } }); - + // Hook up the submit button to log to the console document.getElementById('submit').addEventListener('click',function() { // Get the value from the editor diff --git a/src/defaults.js b/src/defaults.js index 27cb2ff0c..182e85a86 100644 --- a/src/defaults.js +++ b/src/defaults.js @@ -185,6 +185,11 @@ JSONEditor.defaults.resolvers.unshift(function(schema) { // If the schema is a simple type if(typeof schema.type === "string") return schema.type; }); +// Use a specialized editor for ratings +JSONEditor.defaults.resolvers.unshift(function(schema) { + // If the schema is a simple type + if(schema.type === "integer" && schema.format === "rating") return "rating"; +}); // Use the select editor for all boolean values JSONEditor.defaults.resolvers.unshift(function(schema) { if(schema.type === 'boolean') { diff --git a/src/editors/rating.js b/src/editors/rating.js new file mode 100644 index 000000000..edc4502d2 --- /dev/null +++ b/src/editors/rating.js @@ -0,0 +1,150 @@ +JSONEditor.defaults.editors.rating = JSONEditor.defaults.editors.integer.extend({ + 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); + + // Dynamically add the required CSS the first time this editor is used + var styleId = 'json-editor-style-rating'; + var styles = document.getElementById(styleId); + if (!styles) { + var style = document.createElement('style'); + style.id = styleId; + style.type = 'text/css'; + style.innerHTML = '\ + .rating-container {\ + display: inline-block;\ + clear: both;\ + }\ + \ + .rating {\ + float:left;\ + }\ + \ + /* :not(:checked) is a filter, so that browsers that don’t support :checked don’t\ + follow these rules. Every browser that supports :checked also supports :not(), so\ + it doesn’t make the test unnecessarily selective */\ + .rating:not(:checked) > input {\ + position:absolute;\ + top:-9999px;\ + clip:rect(0,0,0,0);\ + }\ + \ + .rating:not(:checked) > label {\ + float:right;\ + width:1em;\ + padding:0 .1em;\ + overflow:hidden;\ + white-space:nowrap;\ + cursor:pointer;\ + color:#ddd;\ + }\ + \ + .rating:not(:checked) > label:before {\ + content: "★ ";\ + }\ + \ + .rating > input:checked ~ label {\ + color: #FF7700;\ + }\ + \ + .rating:not(:checked) > label:hover,\ + .rating:not(:checked) > label:hover ~ label {\ + color: #FFD308;\ + }\ + \ + .rating > input:checked + label:hover,\ + .rating > input:checked + label:hover ~ label,\ + .rating > input:checked ~ label:hover,\ + .rating > input:checked ~ label:hover ~ label,\ + .rating > label:hover ~ input:checked ~ label {\ + color: #ea0;\ + }\ + \ + .rating > label:active {\ + position:relative;\ + top:2px;\ + left:2px;\ + }\ + '; + document.getElementsByTagName('head')[0].appendChild(style); + } + + this.input = this.theme.getFormInputField('hidden'); + this.container.appendChild(this.input); + + // Required to keep height + var ratingContainer = document.createElement('div'); + ratingContainer.className = 'rating-container'; + + // Contains options for rating + var group = document.createElement('div'); + group.setAttribute('name', this.formname); + group.className = 'rating'; + ratingContainer.appendChild(group); + + if(this.options.compact) this.container.setAttribute('class',this.container.getAttribute('class')+' compact'); + + if(this.schema.readOnly || this.schema.readonly) { + this.always_disabled = true; + this.input.disabled = true; + } + + var max = this.schema.maximum ? this.schema.maximum : 5; + if (this.schema.exclusiveMaximum) max--; + + this.inputs = []; + for(i=max; i>0; i--) { + var id = this.formname + i; + var radioInput = this.theme.getFormInputField('radio'); + radioInput.setAttribute('id', id); + radioInput.setAttribute('value', i); + radioInput.setAttribute('name', this.formname); + group.appendChild(radioInput); + this.inputs.push(radioInput); + + var label = document.createElement('label'); + label.setAttribute('for', id); + label.appendChild(document.createTextNode(i + (i == 1 ? ' star' : ' stars'))); + group.appendChild(label); + } + + ratingContainer + .addEventListener('change',function(e) { + e.preventDefault(); + e.stopPropagation(); + + self.input.value = e.srcElement.value; + + self.is_dirty = true; + + self.refreshValue(); + self.watch_listener(); + self.jsoneditor.notifyWatchers(self.path); + if(self.parent) self.parent.onChildEditorChange(self); + else self.jsoneditor.onChange(); + }); + + this.control = this.theme.getFormControl(this.label, ratingContainer, this.description); + this.container.appendChild(this.control); + + this.refreshValue(); + }, + setValue: function(val) { + var sanitized = this.sanitize(val); + if(this.value === sanitized) { + return; + } + var self = this; + $each(this.inputs,function(i,input) { + if (input.value === sanitized) { + input.checked = true; + self.value = sanitized; + self.input.value = self.value; + self.watch_listener(); + self.jsoneditor.notifyWatchers(self.path); + return false; + } + }); + } +}); From 95a11664ca58a0c9200ead59cff94d8940856fbd Mon Sep 17 00:00:00 2001 From: Andre Kampert Date: Mon, 25 Aug 2014 18:06:29 +0200 Subject: [PATCH 2/5] Do not use multiline strings --- src/editors/rating.js | 111 +++++++++++++++++++++--------------------- 1 file changed, 55 insertions(+), 56 deletions(-) diff --git a/src/editors/rating.js b/src/editors/rating.js index edc4502d2..bc5120337 100644 --- a/src/editors/rating.js +++ b/src/editors/rating.js @@ -11,62 +11,61 @@ JSONEditor.defaults.editors.rating = JSONEditor.defaults.editors.integer.extend( var style = document.createElement('style'); style.id = styleId; style.type = 'text/css'; - style.innerHTML = '\ - .rating-container {\ - display: inline-block;\ - clear: both;\ - }\ - \ - .rating {\ - float:left;\ - }\ - \ - /* :not(:checked) is a filter, so that browsers that don’t support :checked don’t\ - follow these rules. Every browser that supports :checked also supports :not(), so\ - it doesn’t make the test unnecessarily selective */\ - .rating:not(:checked) > input {\ - position:absolute;\ - top:-9999px;\ - clip:rect(0,0,0,0);\ - }\ - \ - .rating:not(:checked) > label {\ - float:right;\ - width:1em;\ - padding:0 .1em;\ - overflow:hidden;\ - white-space:nowrap;\ - cursor:pointer;\ - color:#ddd;\ - }\ - \ - .rating:not(:checked) > label:before {\ - content: "★ ";\ - }\ - \ - .rating > input:checked ~ label {\ - color: #FF7700;\ - }\ - \ - .rating:not(:checked) > label:hover,\ - .rating:not(:checked) > label:hover ~ label {\ - color: #FFD308;\ - }\ - \ - .rating > input:checked + label:hover,\ - .rating > input:checked + label:hover ~ label,\ - .rating > input:checked ~ label:hover,\ - .rating > input:checked ~ label:hover ~ label,\ - .rating > label:hover ~ input:checked ~ label {\ - color: #ea0;\ - }\ - \ - .rating > label:active {\ - position:relative;\ - top:2px;\ - left:2px;\ - }\ - '; + style.innerHTML = + ' .rating-container {' + + ' display: inline-block;' + + ' clear: both;' + + ' }' + + ' ' + + ' .rating {' + + ' float:left;' + + ' }' + + ' ' + + ' /* :not(:checked) is a filter, so that browsers that don’t support :checked don’t' + + ' follow these rules. Every browser that supports :checked also supports :not(), so' + + ' it doesn’t make the test unnecessarily selective */' + + ' .rating:not(:checked) > input {' + + ' position:absolute;' + + ' top:-9999px;' + + ' clip:rect(0,0,0,0);' + + ' }' + + ' ' + + ' .rating:not(:checked) > label {' + + ' float:right;' + + ' width:1em;' + + ' padding:0 .1em;' + + ' overflow:hidden;' + + ' white-space:nowrap;' + + ' cursor:pointer;' + + ' color:#ddd;' + + ' }' + + ' ' + + ' .rating:not(:checked) > label:before {' + + ' content: \'★ \';' + + ' }' + + ' ' + + ' .rating > input:checked ~ label {' + + ' color: #FF7700;' + + ' }' + + ' ' + + ' .rating:not(:checked) > label:hover,' + + ' .rating:not(:checked) > label:hover ~ label {' + + ' color: #FFD308;' + + ' }' + + ' ' + + ' .rating > input:checked + label:hover,' + + ' .rating > input:checked + label:hover ~ label,' + + ' .rating > input:checked ~ label:hover,' + + ' .rating > input:checked ~ label:hover ~ label,' + + ' .rating > label:hover ~ input:checked ~ label {' + + ' color: #ea0;' + + ' }' + + ' ' + + ' .rating > label:active {' + + ' position:relative;' + + ' top:2px;' + + ' left:2px;' + + ' }'; document.getElementsByTagName('head')[0].appendChild(style); } From d962f5622e8c561abeff4c60217c8d5c56079dfd Mon Sep 17 00:00:00 2001 From: Andre Kampert Date: Mon, 25 Aug 2014 18:44:05 +0200 Subject: [PATCH 3/5] Support readonly option --- examples/basic.html | 3 +-- src/editors/rating.js | 35 +++++++++++++++++++---------------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/examples/basic.html b/examples/basic.html index 1daadf5f9..a094af8a7 100644 --- a/examples/basic.html +++ b/examples/basic.html @@ -47,8 +47,7 @@

Basic JSON Editor Example

format: "rating", maximum: "5", exclusiveMaximum: false, - options: { - } + readonly: false } } } diff --git a/src/editors/rating.js b/src/editors/rating.js index bc5120337..e2d5807dc 100644 --- a/src/editors/rating.js +++ b/src/editors/rating.js @@ -45,23 +45,23 @@ JSONEditor.defaults.editors.rating = JSONEditor.defaults.editors.integer.extend( ' }' + ' ' + ' .rating > input:checked ~ label {' + - ' color: #FF7700;' + + ' color: #FFB200;' + ' }' + ' ' + - ' .rating:not(:checked) > label:hover,' + - ' .rating:not(:checked) > label:hover ~ label {' + - ' color: #FFD308;' + + ' .rating:not([readOnly]):not(:checked) > label:hover,' + + ' .rating:not([readOnly]):not(:checked) > label:hover ~ label {' + + ' color: #FFDA00;' + ' }' + ' ' + - ' .rating > input:checked + label:hover,' + - ' .rating > input:checked + label:hover ~ label,' + - ' .rating > input:checked ~ label:hover,' + - ' .rating > input:checked ~ label:hover ~ label,' + - ' .rating > label:hover ~ input:checked ~ label {' + - ' color: #ea0;' + + ' .rating:not([readOnly]) > input:checked + label:hover,' + + ' .rating:not([readOnly]) > input:checked + label:hover ~ label,' + + ' .rating:not([readOnly]) > input:checked ~ label:hover,' + + ' .rating:not([readOnly]) > input:checked ~ label:hover ~ label,' + + ' .rating:not([readOnly]) > label:hover ~ input:checked ~ label {' + + ' color: #FF8C0D;' + ' }' + ' ' + - ' .rating > label:active {' + + ' .rating:not([readOnly]) > label:active {' + ' position:relative;' + ' top:2px;' + ' left:2px;' + @@ -84,11 +84,6 @@ JSONEditor.defaults.editors.rating = JSONEditor.defaults.editors.integer.extend( if(this.options.compact) this.container.setAttribute('class',this.container.getAttribute('class')+' compact'); - if(this.schema.readOnly || this.schema.readonly) { - this.always_disabled = true; - this.input.disabled = true; - } - var max = this.schema.maximum ? this.schema.maximum : 5; if (this.schema.exclusiveMaximum) max--; @@ -108,6 +103,14 @@ JSONEditor.defaults.editors.rating = JSONEditor.defaults.editors.integer.extend( group.appendChild(label); } + if(this.schema.readOnly || this.schema.readonly) { + this.always_disabled = true; + $each(this.inputs,function(i,input) { + group.setAttribute("readOnly", "readOnly"); + input.disabled = true; + }); + } + ratingContainer .addEventListener('change',function(e) { e.preventDefault(); From 8d083e33bb8df5928997149023b9851562651872 Mon Sep 17 00:00:00 2001 From: Andre Kampert Date: Mon, 25 Aug 2014 18:46:06 +0200 Subject: [PATCH 4/5] Fix comment --- src/defaults.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/defaults.js b/src/defaults.js index 182e85a86..c62e6d823 100644 --- a/src/defaults.js +++ b/src/defaults.js @@ -187,7 +187,6 @@ JSONEditor.defaults.resolvers.unshift(function(schema) { }); // Use a specialized editor for ratings JSONEditor.defaults.resolvers.unshift(function(schema) { - // If the schema is a simple type if(schema.type === "integer" && schema.format === "rating") return "rating"; }); // Use the select editor for all boolean values From e4d681cf8f5c57d578393fc42cacc1217bb21c67 Mon Sep 17 00:00:00 2001 From: Andre Kampert Date: Wed, 17 Sep 2014 15:31:09 +0200 Subject: [PATCH 5/5] Merge branch master Squashed commit of the following: commit a97cc2a5ea1b368f941e6efbed3ab61d69fab5bc Author: Jeremy Dorn Date: Mon Sep 15 16:17:10 2014 -0700 Version bump to 0.7.10 commit b75eec9f08e35d810804dbde4e41d9453fa75d25 Author: Jeremy Dorn Date: Mon Sep 15 16:15:30 2014 -0700 Add option to remove empty object properties from returned JSON. Fixes #220 commit 5dd3627a8abdd8b2a206278bcd96894ce31a8797 Author: Jeremy Dorn Date: Mon Sep 15 16:07:07 2014 -0700 Fix array/minItems bug. Fixes #246 commit e7fbe178d188a13bcd94f202fe382e13d4e7ad3f Author: Jeremy Dorn Date: Mon Sep 15 14:49:29 2014 -0700 Standardize all notify/change/watch calls. Fixes #247 Fixes #248 Probably fixes a lot more bugs too commit 4a5bd8ad20bef03ccb2f673b8006184140d09ad6 Merge: ba381f5 941e096 Author: Jeremy Dorn Date: Mon Sep 15 14:39:29 2014 -0700 Merge branch 'keynslug-patch-1' commit 941e0963cecf4dfd1d4a4b2e7d41e6868b62c89e Author: Andrew Majorov Date: Tue Sep 16 01:03:10 2014 +0400 Properly handle cases when number extremum is 0 The condition does not hold when `schema.minimum` or `schema.maximum` equals 0, even if the property actually defined. commit ba381f5cc2cdd062c65fb16cd4a910fecf4eb852 Author: Jeremy Date: Mon Sep 1 07:40:12 2014 -0700 Register with npm commit 50f383106ba42ee54a4389cf8477bf3e3d0d2cfa Author: Jeremy Date: Sun Aug 31 19:55:32 2014 -0700 Fix Select2 bugs, enum_titles support for strings, add Select2 example. Fixes #240 Related to #231 --- bower.json | 2 +- examples/select2.html | 84 ++++++++++++++ package.json | 30 ++++- src/defaults.js | 7 ++ src/editor.js | 16 +-- src/editors/array.js | 35 +++--- src/editors/base64.js | 8 +- src/editors/enum.js | 6 +- src/editors/multiple.js | 14 +-- src/editors/multiselect.js | 13 +-- src/editors/null.js | 2 +- src/editors/object.js | 26 +++-- src/editors/select.js | 220 +++++++++++++++++++++++++++++++++---- src/editors/string.js | 183 ++---------------------------- src/editors/table.js | 35 ++---- src/editors/upload.js | 4 +- src/intro.js | 4 +- src/validator.js | 4 +- 18 files changed, 393 insertions(+), 300 deletions(-) create mode 100644 examples/select2.html diff --git a/bower.json b/bower.json index 883ad5265..bd2800d00 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "json-editor", - "version": "0.7.8", + "version": "0.7.10", "authors": [ "Jeremy Dorn " ], diff --git a/examples/select2.html b/examples/select2.html new file mode 100644 index 000000000..0912c462c --- /dev/null +++ b/examples/select2.html @@ -0,0 +1,84 @@ + + + + + JSON Editor Select2 Integration Example + + + + + + +

JSON Editor Select2 Integration Example

+ +

This example demonstrates JSONEditor's integration with Select2

+ +
+ + + + + diff --git a/package.json b/package.json index 6b09221c7..3cce35a89 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,28 @@ { - "name": "jquery-jsoneditor", + "name": "json-editor", + "title": "JSONEditor", + "description": "JSON Schema based editor", + "version": "0.7.10", + "main": "dist/jsoneditor.js", + "author": { + "name": "Jeremy Dorn", + "email": "jeremy@jeremydorn.com", + "url": "http://jeremydorn.com" + }, + "bugs": { + "url": "https://github.com/jdorn/json-editor/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/jdorn/json-editor.git" + }, + "keywords": [ + "json", + "schema", + "jsonschema", + "editor" + ], + "license": "MIT", "engines": { "node": ">= 0.8.0" }, @@ -9,5 +32,10 @@ "grunt-contrib-watch": "~0.5.3", "grunt-contrib-jshint": "^0.10.0", "grunt": "~0.4.2" + }, + "scripts": { + "build": "npm install && grunt", + "start": "grunt watch", + "test": "grunt" } } diff --git a/src/defaults.js b/src/defaults.js index c62e6d823..43d3add0c 100644 --- a/src/defaults.js +++ b/src/defaults.js @@ -166,6 +166,9 @@ JSONEditor.plugins = { }, sceditor: { + }, + select2: { + } }; @@ -220,6 +223,10 @@ JSONEditor.defaults.resolvers.unshift(function(schema) { return "table"; } }); +// Use the `select` editor for dynamic enumSource enums +JSONEditor.defaults.resolvers.unshift(function(schema) { + if(schema.enumSource) return "select"; +}); // Use the `enum` or `select` editors for schemas with enumerated properties JSONEditor.defaults.resolvers.unshift(function(schema) { if(schema.enum) { diff --git a/src/editor.js b/src/editor.js index 26892fcec..c25f33543 100644 --- a/src/editor.js +++ b/src/editor.js @@ -3,10 +3,7 @@ */ JSONEditor.AbstractEditor = Class.extend({ onChildEditorChange: function(editor) { - if(!this.watch_listener) return; - this.watch_listener(); - this.notify(); - this.change(); + this.onChange(true); }, notify: function() { this.jsoneditor.notifyWatchers(this.path); @@ -15,8 +12,14 @@ JSONEditor.AbstractEditor = Class.extend({ 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; @@ -62,13 +65,12 @@ JSONEditor.AbstractEditor = Class.extend({ }, postBuild: function() { - this.register(); this.setupWatchListeners(); this.addLinks(); this.setValue(this.getDefault(), true); this.updateHeaderText(); - this.jsoneditor.notifyWatchers(this.path); - this.watch_listener(); + this.register(); + this.onWatchedFieldChange(); }, setupWatchListeners: function() { diff --git a/src/editors/array.js b/src/editors/array.js index 89864c0fd..8c394bec7 100644 --- a/src/editors/array.js +++ b/src/editors/array.js @@ -305,17 +305,17 @@ JSONEditor.defaults.editors.array = JSONEditor.AbstractEditor.extend({ $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); + self.rows[i].setValue(val,initial); } else if(self.row_cache[i]) { self.rows[i] = self.row_cache[i]; - self.rows[i].setValue(val); + 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); + self.addRow(val,initial); } }); @@ -339,8 +339,8 @@ JSONEditor.defaults.editors.array = JSONEditor.AbstractEditor.extend({ self.refreshValue(initial); self.refreshTabs(); - - self.jsoneditor.notifyWatchers(self.path); + + self.onChange(); // TODO: sortable }, @@ -431,7 +431,7 @@ JSONEditor.defaults.editors.array = JSONEditor.AbstractEditor.extend({ } } }, - addRow: function(value) { + addRow: function(value, initial) { var self = this; var i = this.rows.length; @@ -488,9 +488,8 @@ JSONEditor.defaults.editors.array = JSONEditor.AbstractEditor.extend({ self.active_tab = new_active_tab; self.refreshTabs(); } - - if(self.parent) self.parent.onChildEditorChange(self); - else self.jsoneditor.onChange(); + + self.onChange(true); }); if(controls_holder) { @@ -517,8 +516,7 @@ JSONEditor.defaults.editors.array = JSONEditor.AbstractEditor.extend({ self.active_tab = self.rows[i-1].tab; self.refreshTabs(); - if(self.parent) self.parent.onChildEditorChange(self); - else self.jsoneditor.onChange(); + self.onChange(true); }); if(controls_holder) { @@ -544,8 +542,7 @@ JSONEditor.defaults.editors.array = JSONEditor.AbstractEditor.extend({ self.setValue(rows); self.active_tab = self.rows[i+1].tab; self.refreshTabs(); - if(self.parent) self.parent.onChildEditorChange(self); - else self.jsoneditor.onChange(); + self.onChange(true); }); if(controls_holder) { @@ -553,7 +550,7 @@ JSONEditor.defaults.editors.array = JSONEditor.AbstractEditor.extend({ } } - if(value) self.rows[i].setValue(value); + if(value) self.rows[i].setValue(value, initial); self.refreshTabs(); }, addControls: function() { @@ -617,9 +614,7 @@ JSONEditor.defaults.editors.array = JSONEditor.AbstractEditor.extend({ self.active_tab = self.rows[i].tab; self.refreshTabs(); self.refreshValue(); - if(self.parent) self.parent.onChildEditorChange(self); - else self.jsoneditor.onChange(); - self.jsoneditor.notifyWatchers(self.path); + self.onChange(true); }); self.controls.appendChild(this.add_row_button); @@ -638,8 +633,7 @@ JSONEditor.defaults.editors.array = JSONEditor.AbstractEditor.extend({ self.active_tab = new_active_tab; self.refreshTabs(); } - if(self.parent) self.parent.onChildEditorChange(self); - else self.jsoneditor.onChange(); + self.onChange(true); }); self.controls.appendChild(this.delete_last_row_button); @@ -648,8 +642,7 @@ JSONEditor.defaults.editors.array = JSONEditor.AbstractEditor.extend({ e.preventDefault(); e.stopPropagation(); self.setValue([]); - if(self.parent) self.parent.onChildEditorChange(self); - else self.jsoneditor.onChange(); + self.onChange(true); }); self.controls.appendChild(this.remove_all_rows_button); diff --git a/src/editors/base64.js b/src/editors/base64.js index c0b785423..70bc37ea5 100644 --- a/src/editors/base64.js +++ b/src/editors/base64.js @@ -26,10 +26,7 @@ JSONEditor.defaults.editors.base64 = JSONEditor.AbstractEditor.extend({ fr.onload = function(evt) { self.value = evt.target.result; self.refreshPreview(); - self.watch_listener(); - self.jsoneditor.notifyWatchers(self.path); - if(self.parent) self.parent.onChildEditorChange(self); - else self.jsoneditor.onChange(); + self.onChange(true); fr = null; }; fr.readAsDataURL(this.files[0]); @@ -82,8 +79,7 @@ JSONEditor.defaults.editors.base64 = JSONEditor.AbstractEditor.extend({ this.value = val; this.input.value = this.value; this.refreshPreview(); - this.watch_listener(); - this.jsoneditor.notifyWatchers(this.path); + this.onChange(); } }, destroy: function() { diff --git a/src/editors/enum.js b/src/editors/enum.js index 1d84005e0..be80f1dc6 100644 --- a/src/editors/enum.js +++ b/src/editors/enum.js @@ -41,9 +41,7 @@ JSONEditor.defaults.editors.enum = JSONEditor.AbstractEditor.extend({ self.selected = self.select_options.indexOf(this.value); self.value = self.enum[self.selected]; self.refreshValue(); - - if(self.parent) self.parent.onChildEditorChange(self); - else self.jsoneditor.onChange(); + self.onChange(true); }); this.value = this.enum[0]; this.refreshValue(); @@ -123,7 +121,7 @@ JSONEditor.defaults.editors.enum = JSONEditor.AbstractEditor.extend({ if(this.value !== val) { this.value = val; this.refreshValue(); - this.jsoneditor.notifyWatchers(this.path); + this.onChange(); } }, destroy: function() { diff --git a/src/editors/multiple.js b/src/editors/multiple.js index 52e373529..f3245e1bf 100644 --- a/src/editors/multiple.js +++ b/src/editors/multiple.js @@ -65,9 +65,7 @@ JSONEditor.defaults.editors.multiple = JSONEditor.AbstractEditor.extend({ else editor.container.style.display = 'none'; }); self.refreshValue(); - if(self.watch_listener) self.watch_listener(); - self.notify(); - self.change(); + self.refreshHeaderText(); }, buildChildEditor: function(i) { var self = this; @@ -173,6 +171,7 @@ JSONEditor.defaults.editors.multiple = JSONEditor.AbstractEditor.extend({ e.stopPropagation(); self.switchEditor(self.display_text.indexOf(this.value)); + self.onChange(true); }); this.switcher.style.marginBottom = 0; this.switcher.style.width = 'auto'; @@ -205,14 +204,11 @@ JSONEditor.defaults.editors.multiple = JSONEditor.AbstractEditor.extend({ }); this.switchEditor(0); - - this.refreshValue(); - this.refreshHeaderText(); }, onChildEditorChange: function(editor) { if(this.editors[this.type]) { - this.refreshHeaderText(); this.refreshValue(); + this.refreshHeaderText(); } this._super(); @@ -242,9 +238,7 @@ JSONEditor.defaults.editors.multiple = JSONEditor.AbstractEditor.extend({ this.editors[this.type].setValue(val,initial); this.refreshValue(); - this.watch_listener(); - this.notify(); - + self.onChange(); }, destroy: function() { $each(this.editors, function(type,editor) { diff --git a/src/editors/multiselect.js b/src/editors/multiselect.js index cd344f91a..ce479d507 100644 --- a/src/editors/multiselect.js +++ b/src/editors/multiselect.js @@ -65,10 +65,7 @@ JSONEditor.defaults.editors.multiselect = JSONEditor.AbstractEditor.extend({ } self.updateValue(new_value); - - if(self.parent) self.parent.onChildEditorChange(self); - else self.jsoneditor.onChange(); - self.jsoneditor.notifyWatchers(self.path); + self.onChange(true); }); }, setValue: function(value, initial) { @@ -89,12 +86,8 @@ JSONEditor.defaults.editors.multiselect = JSONEditor.AbstractEditor.extend({ this.select_options[i][this.input_type === "select"? "selected" : "checked"] = (value.indexOf(i) !== -1); } - if(this.updateValue(value)) { - if(this.parent) this.onChildEditorChange(this); - else this.jsoneditor.onChange(); - } - - this.jsoneditor.notifyWatchers(this.path); + this.updateValue(value); + this.onChange(); }, register: function() { this._super(); diff --git a/src/editors/null.js b/src/editors/null.js index 1d04525aa..0d88ba6ed 100644 --- a/src/editors/null.js +++ b/src/editors/null.js @@ -3,7 +3,7 @@ JSONEditor.defaults.editors.null = JSONEditor.AbstractEditor.extend({ return null; }, setValue: function() { - this.jsoneditor.notifyWatchers(this.path); + this.onChange(); }, getNumColumns: function() { return 2; diff --git a/src/editors/object.js b/src/editors/object.js index d871c1806..da672758d 100644 --- a/src/editors/object.js +++ b/src/editors/object.js @@ -236,7 +236,7 @@ JSONEditor.defaults.editors.object = JSONEditor.AbstractEditor.extend({ self.maxwidth += 1; $each(this.defaultProperties, function(i,key) { - self.addObjectProperty(key, true, true); + self.addObjectProperty(key, true); if(self.editors[key]) { self.minwidth = Math.max(self.minwidth,self.editors[key].getNumColumns()); @@ -338,6 +338,7 @@ JSONEditor.defaults.editors.object = JSONEditor.AbstractEditor.extend({ if(self.editors[self.addproperty_input.value]) { self.editors[self.addproperty_input.value].disable(); } + self.onChange(true); } }); this.addproperty_holder.appendChild(this.addproperty_list); @@ -526,6 +527,7 @@ JSONEditor.defaults.editors.object = JSONEditor.AbstractEditor.extend({ else { self.removeObjectProperty(key); } + self.onChange(true); }); self.addproperty_checkboxes[key] = checkbox; @@ -567,12 +569,10 @@ JSONEditor.defaults.editors.object = JSONEditor.AbstractEditor.extend({ delete this.editors[property]; this.refreshValue(); - this.notify(); - this.change(); this.layoutEditors(); } }, - addObjectProperty: function(name, prebuild_only, bulk) { + addObjectProperty: function(name, prebuild_only) { var self = this; // Property is already added @@ -583,7 +583,6 @@ JSONEditor.defaults.editors.object = JSONEditor.AbstractEditor.extend({ this.editors[name] = this.cached_editors[name]; if(prebuild_only) return; this.editors[name].register(); - this.jsoneditor.notifyWatchers(this.editors[name].path); } // New property else { @@ -617,10 +616,8 @@ JSONEditor.defaults.editors.object = JSONEditor.AbstractEditor.extend({ } // If we're only prebuilding the editors, don't refresh values - if(!prebuild_only && !bulk) { + if(!prebuild_only) { self.refreshValue(); - self.notify(); - self.change(); self.layoutEditors(); } }, @@ -646,6 +643,17 @@ JSONEditor.defaults.editors.object = JSONEditor.AbstractEditor.extend({ 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; @@ -777,8 +785,8 @@ JSONEditor.defaults.editors.object = JSONEditor.AbstractEditor.extend({ }); this.refreshValue(); - this.notify(); this.layoutEditors(); + this.onChange(); }, showValidationErrors: function(errors) { var self = this; diff --git a/src/editors/select.js b/src/editors/select.js index aef682cb1..7a0ee2074 100644 --- a/src/editors/select.js +++ b/src/editors/select.js @@ -13,8 +13,9 @@ JSONEditor.defaults.editors.select = JSONEditor.AbstractEditor.extend({ } 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.jsoneditor.notifyWatchers(this.path); + this.onChange(); }, register: function() { this._super(); @@ -60,9 +61,11 @@ JSONEditor.defaults.editors.select = JSONEditor.AbstractEditor.extend({ // 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] = ""+option; + self.enum_display[i] = ""+(display[i] || option); self.enum_values[i] = self.typecast(option); }); } @@ -72,6 +75,63 @@ JSONEditor.defaults.editors.select = JSONEditor.AbstractEditor.extend({ self.enum_options = ['1','']; 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 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(); + }); + } + else { + this.select2 = null; + } + }, postBuild: function() { this._super(); this.theme.afterInputReady(this.input); - - // 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) { - window.jQuery(this.input).select2(); + 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 HTML Editor +/*! JSON Editor v0.7.10 - JSON Schema -> HTML Editor * By Jeremy Dorn - https://github.com/jdorn/json-editor/ * Released under the MIT license * - * Date: 2014-08-28 + * Date: 2014-09-15 */ /** diff --git a/src/validator.js b/src/validator.js index 4167de250..9b5148cb9 100644 --- a/src/validator.js +++ b/src/validator.js @@ -218,7 +218,7 @@ JSONEditor.Validator = Class.extend({ } // `maximum` - if(schema.maximum) { + if(schema.hasOwnProperty('maximum')) { if(schema.exclusiveMaximum && value >= schema.maximum) { errors.push({ path: path, @@ -236,7 +236,7 @@ JSONEditor.Validator = Class.extend({ } // `minimum` - if(schema.minimum) { + if(schema.hasOwnProperty('minimum')) { if(schema.exclusiveMinimum && value <= schema.minimum) { errors.push({ path: path,