Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Recursively resolve definitions in remote schemas #733

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 38 additions & 37 deletions src/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,38 +13,38 @@ JSONEditor.prototype = {
constructor: JSONEditor,
init: function() {
var self = this;

this.ready = false;

var theme_class = JSONEditor.defaults.themes[this.options.theme || JSONEditor.defaults.theme];
if(!theme_class) throw "Unknown theme " + (this.options.theme || JSONEditor.defaults.theme);

this.schema = this.options.schema;
this.theme = new theme_class();
this.template = this.options.template;
this.refs = this.options.refs || {};
this.uuid = 0;
this.__data = {};

var icon_class = JSONEditor.defaults.iconlibs[this.options.iconlib || JSONEditor.defaults.iconlib];
if(icon_class) this.iconlib = new icon_class();

this.root_container = this.theme.getContainer();
this.element.appendChild(this.root_container);

this.translate = this.options.translate || JSONEditor.defaults.translate;

// Fetch all external refs via ajax
this._loadExternalRefs(this.schema, function() {
self._getDefinitions(self.schema);

// Validator options
var validator_options = {};
if(self.options.custom_validators) {
validator_options.custom_validators = self.options.custom_validators;
}
self.validator = new JSONEditor.Validator(self,null,validator_options);

// Create the root editor
var editor_class = self.getEditorClass(self.schema);
self.root = self.createEditor(editor_class, {
Expand All @@ -53,7 +53,7 @@ JSONEditor.prototype = {
required: true,
container: self.root_container
});

self.root.preBuild();
self.root.build();
self.root.postBuild();
Expand Down Expand Up @@ -88,7 +88,7 @@ JSONEditor.prototype = {
},
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);
Expand All @@ -101,7 +101,7 @@ JSONEditor.prototype = {
destroy: function() {
if(this.destroyed) return;
if(!this.ready) return;

this.schema = null;
this.options = null;
this.root.destroy();
Expand All @@ -115,14 +115,14 @@ JSONEditor.prototype = {
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) {
Expand All @@ -146,7 +146,7 @@ JSONEditor.prototype = {
else {
this.callbacks = {};
}

return this;
},
trigger: function(event) {
Expand All @@ -155,7 +155,7 @@ JSONEditor.prototype = {
this.callbacks[event][i]();
}
}

return this;
},
setOption: function(option, value) {
Expand All @@ -167,7 +167,7 @@ JSONEditor.prototype = {
else {
throw "Option "+option+" must be set during instantiation and cannot be changed later";
}

return this;
},
getEditorClass: function(schema) {
Expand Down Expand Up @@ -196,30 +196,30 @@ JSONEditor.prototype = {
},
onChange: function() {
if(!this.ready) return;

if(this.firing_change) return;
this.firing_change = true;

var self = this;

window.requestAnimationFrame(function() {
self.firing_change = false;
if(!self.ready) return;

// Validate and cache results
self.validation_results = self.validator.validate(self.root.getValue());

if(self.options.show_errors !== "never") {
self.root.showValidationErrors(self.validation_results);
}
else {
self.root.showValidationErrors([]);
}

// Fire change event
self.trigger('change');
});

return this;
},
compileTemplate: function(template, name) {
Expand Down Expand Up @@ -262,7 +262,7 @@ JSONEditor.prototype = {
else {
// No data stored
if(!el.hasAttribute('data-jsoneditor-'+key)) return null;

return this.__data[el.getAttribute('data-jsoneditor-'+key)];
}
},
Expand All @@ -284,7 +284,7 @@ JSONEditor.prototype = {
this.watchlist = this.watchlist || {};
this.watchlist[path] = this.watchlist[path] || [];
this.watchlist[path].push(callback);

return this;
},
unwatch: function(path,callback) {
Expand All @@ -294,7 +294,7 @@ JSONEditor.prototype = {
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;
Expand Down Expand Up @@ -339,11 +339,11 @@ JSONEditor.prototype = {
}
}
};

if(schema.$ref && typeof schema.$ref !== "object" && schema.$ref.substr(0,1) !== "#" && !this.refs[schema.$ref]) {
refs[schema.$ref] = true;
}

for(var i in schema) {
if(!schema.hasOwnProperty(i)) continue;
if(schema[i] && typeof schema[i] === "object" && Array.isArray(schema[i])) {
Expand All @@ -357,25 +357,25 @@ JSONEditor.prototype = {
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();
var r = new XMLHttpRequest();
r.open("GET", url, true);
r.onreadystatechange = function () {
if (r.readyState != 4) return;
if (r.readyState != 4) return;
// Request succeeded
if(r.status === 200) {
var response;
Expand All @@ -387,8 +387,9 @@ JSONEditor.prototype = {
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._getDefinitions(self.refs[url]);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For clarity in the summary, this is the only "real" code change

self._loadExternalRefs(response,function() {
done++;
if(done >= waiting && !callback_fired) {
Expand All @@ -405,20 +406,20 @@ JSONEditor.prototype = {
};
r.send();
});

if(!waiting) {
callback();
}
},
expandRefs: function(schema) {
schema = $extend({},schema);

while (schema.$ref) {
var ref = schema.$ref;
delete schema.$ref;

if(!this.refs[ref]) ref = decodeURIComponent(ref);

schema = this.extendSchemas(schema,this.refs[ref]);
}
return schema;
Expand Down Expand Up @@ -478,7 +479,7 @@ JSONEditor.prototype = {
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++) {
Expand Down Expand Up @@ -508,7 +509,7 @@ JSONEditor.prototype = {
extended.oneOf[i] = this.extendSchemas(this.expandSchema(schema.oneOf[i]),tmp);
}
}

return this.expandRefs(extended);
},
extendSchemas: function(obj1, obj2) {
Expand Down