UNPKG

json-editor

Version:
472 lines (410 loc) 14.1 kB
/** * All editors should extend from this class */ JSONEditor.AbstractEditor = Class.extend({ onChildEditorChange: function(editor) { this.onChange(true); }, notify: function() { this.jsoneditor.notifyWatchers(this.path); }, change: function() { if(this.parent) this.parent.onChildEditorChange(this); else this.jsoneditor.onChange(); }, onChange: function(bubble) { this.notify(); if(this.watch_listener) this.watch_listener(); if(bubble) this.change(); }, register: function() { this.jsoneditor.registerEditor(this); this.onChange(); }, unregister: function() { if(!this.jsoneditor) return; this.jsoneditor.unregisterEditor(this); }, getNumColumns: function() { return 12; }, init: function(options) { this.jsoneditor = options.jsoneditor; this.theme = this.jsoneditor.theme; this.template_engine = this.jsoneditor.template; this.iconlib = this.jsoneditor.iconlib; this.translate = this.jsoneditor.translate || JSONEditor.defaults.translate; this.original_schema = options.schema; this.schema = this.jsoneditor.expandSchema(this.original_schema); this.options = $extend({}, (this.options || {}), (options.schema.options || {}), options); if(!options.path && !this.schema.id) this.schema.id = 'root'; this.path = options.path || 'root'; this.formname = options.formname || this.path.replace(/\.([^.]+)/g,'[$1]'); if(this.jsoneditor.options.form_name_root) this.formname = this.formname.replace(/^root\[/,this.jsoneditor.options.form_name_root+'['); this.key = this.path.split('.').pop(); this.parent = options.parent; this.link_watchers = []; if(options.container) this.setContainer(options.container); }, setContainer: function(container) { this.container = container; if(this.schema.id) this.container.setAttribute('data-schemaid',this.schema.id); if(this.schema.type && typeof this.schema.type === "string") this.container.setAttribute('data-schematype',this.schema.type); this.container.setAttribute('data-schemapath',this.path); }, preBuild: function() { }, build: function() { }, postBuild: function() { this.setupWatchListeners(); this.addLinks(); this.setValue(this.getDefault(), true); this.updateHeaderText(); this.register(); this.onWatchedFieldChange(); }, setupWatchListeners: function() { var self = this; // Watched fields this.watched = {}; if(this.schema.vars) this.schema.watch = this.schema.vars; this.watched_values = {}; this.watch_listener = function() { if(self.refreshWatchedFieldValues()) { self.onWatchedFieldChange(); } }; this.register(); if(this.schema.hasOwnProperty('watch')) { var path,path_parts,first,root,adjusted_path; for(var name in this.schema.watch) { if(!this.schema.watch.hasOwnProperty(name)) continue; path = this.schema.watch[name]; if(Array.isArray(path)) { if(path.length<2) continue; path_parts = [path[0]].concat(path[1].split('.')); } else { path_parts = path.split('.'); if(!self.theme.closest(self.container,'[data-schemaid="'+path_parts[0]+'"]')) path_parts.unshift('#'); } first = path_parts.shift(); if(first === '#') first = self.jsoneditor.schema.id || 'root'; // Find the root node for this template variable root = self.theme.closest(self.container,'[data-schemaid="'+first+'"]'); if(!root) throw "Could not find ancestor node with id "+first; // Keep track of the root node and path for use when rendering the template adjusted_path = root.getAttribute('data-schemapath') + '.' + path_parts.join('.'); self.jsoneditor.watch(adjusted_path,self.watch_listener); self.watched[name] = adjusted_path; } } // Dynamic header if(this.schema.headerTemplate) { this.header_template = this.jsoneditor.compileTemplate(this.schema.headerTemplate, this.template_engine); } }, addLinks: function() { // Add links if(!this.no_link_holder) { this.link_holder = this.theme.getLinksHolder(); this.container.appendChild(this.link_holder); if(this.schema.links) { for(var i=0; i<this.schema.links.length; i++) { this.addLink(this.getLink(this.schema.links[i])); } } } }, getButton: function(text, icon, title) { var btnClass = 'json-editor-btn-'+icon; if(!this.iconlib) icon = null; else icon = this.iconlib.getIcon(icon); if(!icon && title) { text = title; title = null; } var btn = this.theme.getButton(text, icon, title); btn.className += ' ' + btnClass + ' '; return btn; }, setButtonText: function(button, text, icon, title) { if(!this.iconlib) icon = null; else icon = this.iconlib.getIcon(icon); if(!icon && title) { text = title; title = null; } return this.theme.setButtonText(button, text, icon, title); }, addLink: function(link) { if(this.link_holder) this.link_holder.appendChild(link); }, getLink: function(data) { var holder, link; // Get mime type of the link var mime = data.mediaType || 'application/javascript'; var type = mime.split('/')[0]; // Template to generate the link href var href = this.jsoneditor.compileTemplate(data.href,this.template_engine); // Template to generate the link's download attribute var download = null; if(data.download) download = data.download; if(download && download !== true) { download = this.jsoneditor.compileTemplate(download, this.template_engine); } // Image links if(type === 'image') { holder = this.theme.getBlockLinkHolder(); link = document.createElement('a'); link.setAttribute('target','_blank'); var image = document.createElement('img'); this.theme.createImageLink(holder,link,image); // When a watched field changes, update the url this.link_watchers.push(function(vars) { var url = href(vars); link.setAttribute('href',url); link.setAttribute('title',data.rel || url); image.setAttribute('src',url); }); } // Audio/Video links else if(['audio','video'].indexOf(type) >=0) { holder = this.theme.getBlockLinkHolder(); link = this.theme.getBlockLink(); link.setAttribute('target','_blank'); var media = document.createElement(type); media.setAttribute('controls','controls'); this.theme.createMediaLink(holder,link,media); // When a watched field changes, update the url this.link_watchers.push(function(vars) { var url = href(vars); link.setAttribute('href',url); link.textContent = data.rel || url; media.setAttribute('src',url); }); } // Text links else { link = holder = this.theme.getBlockLink(); holder.setAttribute('target','_blank'); holder.textContent = data.rel; // When a watched field changes, update the url this.link_watchers.push(function(vars) { var url = href(vars); holder.setAttribute('href',url); holder.textContent = data.rel || url; }); } if(download && link) { if(download === true) { link.setAttribute('download',''); } else { this.link_watchers.push(function(vars) { link.setAttribute('download',download(vars)); }); } } if(data.class) link.className = link.className + ' ' + data.class; return holder; }, refreshWatchedFieldValues: function() { if(!this.watched_values) return; var watched = {}; var changed = false; var self = this; if(this.watched) { var val,editor; for(var name in this.watched) { if(!this.watched.hasOwnProperty(name)) continue; editor = self.jsoneditor.getEditor(this.watched[name]); val = editor? editor.getValue() : null; if(self.watched_values[name] !== val) changed = true; watched[name] = val; } } watched.self = this.getValue(); if(this.watched_values.self !== watched.self) changed = true; this.watched_values = watched; return changed; }, getWatchedFieldValues: function() { return this.watched_values; }, updateHeaderText: function() { if(this.header) { // If the header has children, only update the text node's value if(this.header.children.length) { for(var i=0; i<this.header.childNodes.length; i++) { if(this.header.childNodes[i].nodeType===3) { this.header.childNodes[i].nodeValue = this.getHeaderText(); break; } } } // Otherwise, just update the entire node else { this.header.textContent = this.getHeaderText(); } } }, getHeaderText: function(title_only) { if(this.header_text) return this.header_text; else if(title_only) return this.schema.title; else return this.getTitle(); }, onWatchedFieldChange: function() { var vars; if(this.header_template) { vars = $extend(this.getWatchedFieldValues(),{ key: this.key, i: this.key, i0: (this.key*1), i1: (this.key*1+1), title: this.getTitle() }); var header_text = this.header_template(vars); if(header_text !== this.header_text) { this.header_text = header_text; this.updateHeaderText(); this.notify(); //this.fireChangeHeaderEvent(); } } if(this.link_watchers.length) { vars = this.getWatchedFieldValues(); for(var i=0; i<this.link_watchers.length; i++) { this.link_watchers[i](vars); } } }, setValue: function(value) { this.value = value; }, getValue: function() { return this.value; }, refreshValue: function() { }, getChildEditors: function() { return false; }, destroy: function() { var self = this; this.unregister(this); $each(this.watched,function(name,adjusted_path) { self.jsoneditor.unwatch(adjusted_path,self.watch_listener); }); this.watched = null; this.watched_values = null; this.watch_listener = null; this.header_text = null; this.header_template = null; this.value = null; if(this.container && this.container.parentNode) this.container.parentNode.removeChild(this.container); this.container = null; this.jsoneditor = null; this.schema = null; this.path = null; this.key = null; this.parent = null; }, getDefault: function() { if(this.schema["default"]) return this.schema["default"]; if(this.schema["enum"]) return this.schema["enum"][0]; var type = this.schema.type || this.schema.oneOf; if(type && Array.isArray(type)) type = type[0]; if(type && typeof type === "object") type = type.type; if(type && Array.isArray(type)) type = type[0]; if(typeof type === "string") { if(type === "number") return 0.0; if(type === "boolean") return false; if(type === "integer") return 0; if(type === "string") return ""; if(type === "object") return {}; if(type === "array") return []; } return null; }, getTitle: function() { return this.schema.title || this.key; }, enable: function() { this.disabled = false; }, disable: function() { this.disabled = true; }, isEnabled: function() { return !this.disabled; }, isRequired: function() { if(typeof this.schema.required === "boolean") return this.schema.required; else if(this.parent && this.parent.schema && Array.isArray(this.parent.schema.required)) return this.parent.schema.required.indexOf(this.key) > -1; else if(this.jsoneditor.options.required_by_default) return true; else return false; }, getDisplayText: function(arr) { var disp = []; var used = {}; // Determine how many times each attribute name is used. // This helps us pick the most distinct display text for the schemas. $each(arr,function(i,el) { if(el.title) { used[el.title] = used[el.title] || 0; used[el.title]++; } if(el.description) { used[el.description] = used[el.description] || 0; used[el.description]++; } if(el.format) { used[el.format] = used[el.format] || 0; used[el.format]++; } if(el.type) { used[el.type] = used[el.type] || 0; used[el.type]++; } }); // Determine display text for each element of the array $each(arr,function(i,el) { var name; // If it's a simple string if(typeof el === "string") name = el; // Object else if(el.title && used[el.title]<=1) name = el.title; else if(el.format && used[el.format]<=1) name = el.format; else if(el.type && used[el.type]<=1) name = el.type; else if(el.description && used[el.description]<=1) name = el.descripton; else if(el.title) name = el.title; else if(el.format) name = el.format; else if(el.type) name = el.type; else if(el.description) name = el.description; else if(JSON.stringify(el).length < 50) name = JSON.stringify(el); else name = "type"; disp.push(name); }); // Replace identical display text with "text 1", "text 2", etc. var inc = {}; $each(disp,function(i,name) { inc[name] = inc[name] || 0; inc[name]++; if(used[name] > 1) disp[i] = name + " " + inc[name]; }); return disp; }, getOption: function(key) { try { throw "getOption is deprecated"; } catch(e) { window.console.error(e); } return this.options[key]; }, showValidationErrors: function(errors) { } });