json-editor
Version:
JSON Schema based editor
449 lines (393 loc) • 13.8 kB
JavaScript
JSONEditor.defaults.editors.string = JSONEditor.AbstractEditor.extend({
register: function() {
this._super();
if(!this.input) return;
this.input.setAttribute('name',this.formname);
},
unregister: function() {
this._super();
if(!this.input) return;
this.input.removeAttribute('name');
},
setValue: function(value,initial,from_template) {
var self = this;
if(this.template && !from_template) {
return;
}
if(value === null || typeof value === 'undefined') value = "";
else if(typeof value === "object") value = JSON.stringify(value);
else if(typeof value !== "string") value = ""+value;
if(value === this.serialized) return;
// Sanitize value before setting it
var sanitized = this.sanitize(value);
if(this.input.value === sanitized) {
return;
}
this.input.value = sanitized;
// If using SCEditor, update the WYSIWYG
if(this.sceditor_instance) {
this.sceditor_instance.val(sanitized);
}
else if(this.epiceditor) {
this.epiceditor.importFile(null,sanitized);
}
else if(this.ace_editor) {
this.ace_editor.setValue(sanitized);
}
var changed = from_template || this.getValue() !== value;
this.refreshValue();
if(initial) this.is_dirty = false;
else if(this.jsoneditor.options.show_errors === "change") this.is_dirty = true;
if(this.adjust_height) this.adjust_height(this.input);
// Bubble this setValue to parents if the value changed
this.onChange(changed);
},
getNumColumns: function() {
var min = Math.ceil(Math.max(this.getTitle().length,this.schema.maxLength||0,this.schema.minLength||0)/5);
var num;
if(this.input_type === 'textarea') num = 6;
else if(['text','email'].indexOf(this.input_type) >= 0) num = 4;
else num = 2;
return Math.min(12,Math.max(min,num));
},
build: function() {
var self = this, i;
if(!this.options.compact) this.header = this.label = this.theme.getFormInputLabel(this.getTitle());
if(this.schema.description) this.description = this.theme.getFormInputDescription(this.schema.description);
this.format = this.schema.format;
if(!this.format && this.schema.media && this.schema.media.type) {
this.format = this.schema.media.type.replace(/(^(application|text)\/(x-)?(script\.)?)|(-source$)/g,'');
}
if(!this.format && this.options.default_format) {
this.format = this.options.default_format;
}
if(this.options.format) {
this.format = this.options.format;
}
// Specific format
if(this.format) {
// Text Area
if(this.format === 'textarea') {
this.input_type = 'textarea';
this.input = this.theme.getTextareaInput();
}
// Range Input
else if(this.format === 'range') {
this.input_type = 'range';
var min = this.schema.minimum || 0;
var max = this.schema.maximum || Math.max(100,min+1);
var step = 1;
if(this.schema.multipleOf) {
if(min%this.schema.multipleOf) min = Math.ceil(min/this.schema.multipleOf)*this.schema.multipleOf;
if(max%this.schema.multipleOf) max = Math.floor(max/this.schema.multipleOf)*this.schema.multipleOf;
step = this.schema.multipleOf;
}
this.input = this.theme.getRangeInput(min,max,step);
}
// Source Code
else if([
'actionscript',
'batchfile',
'bbcode',
'c',
'c++',
'cpp',
'coffee',
'csharp',
'css',
'dart',
'django',
'ejs',
'erlang',
'golang',
'groovy',
'handlebars',
'haskell',
'haxe',
'html',
'ini',
'jade',
'java',
'javascript',
'json',
'less',
'lisp',
'lua',
'makefile',
'markdown',
'matlab',
'mysql',
'objectivec',
'pascal',
'perl',
'pgsql',
'php',
'python',
'r',
'ruby',
'sass',
'scala',
'scss',
'smarty',
'sql',
'stylus',
'svg',
'twig',
'vbscript',
'xml',
'yaml'
].indexOf(this.format) >= 0
) {
this.input_type = this.format;
this.source_code = true;
this.input = this.theme.getTextareaInput();
}
// HTML5 Input type
else {
this.input_type = this.format;
this.input = this.theme.getFormInputField(this.input_type);
}
}
// Normal text input
else {
this.input_type = 'text';
this.input = this.theme.getFormInputField(this.input_type);
}
// minLength, maxLength, and pattern
if(typeof this.schema.maxLength !== "undefined") this.input.setAttribute('maxlength',this.schema.maxLength);
if(typeof this.schema.pattern !== "undefined") this.input.setAttribute('pattern',this.schema.pattern);
else if(typeof this.schema.minLength !== "undefined") this.input.setAttribute('pattern','.{'+this.schema.minLength+',}');
if(this.options.compact) {
this.container.className += ' compact';
}
else {
if(this.options.input_width) this.input.style.width = this.options.input_width;
}
if(this.schema.readOnly || this.schema.readonly || this.schema.template) {
this.always_disabled = true;
this.input.disabled = true;
}
this.input
.addEventListener('change',function(e) {
e.preventDefault();
e.stopPropagation();
// Don't allow changing if this field is a template
if(self.schema.template) {
this.value = self.value;
return;
}
var val = this.value;
// sanitize value
var sanitized = self.sanitize(val);
if(val !== sanitized) {
this.value = sanitized;
}
self.is_dirty = true;
self.refreshValue();
self.onChange(true);
});
if(this.options.input_height) this.input.style.height = this.options.input_height;
if(this.options.expand_height) {
this.adjust_height = function(el) {
if(!el) return;
var i, ch=el.offsetHeight;
// Input too short
if(el.offsetHeight < el.scrollHeight) {
i=0;
while(el.offsetHeight < el.scrollHeight+3) {
if(i>100) break;
i++;
ch++;
el.style.height = ch+'px';
}
}
else {
i=0;
while(el.offsetHeight >= el.scrollHeight+3) {
if(i>100) break;
i++;
ch--;
el.style.height = ch+'px';
}
el.style.height = (ch+1)+'px';
}
};
this.input.addEventListener('keyup',function(e) {
self.adjust_height(this);
});
this.input.addEventListener('change',function(e) {
self.adjust_height(this);
});
this.adjust_height();
}
if(this.format) this.input.setAttribute('data-schemaformat',this.format);
this.control = this.theme.getFormControl(this.label, this.input, this.description);
this.container.appendChild(this.control);
// Any special formatting that needs to happen after the input is added to the dom
window.requestAnimationFrame(function() {
// Skip in case the input is only a temporary editor,
// otherwise, in the case of an ace_editor creation,
// it will generate an error trying to append it to the missing parentNode
if(self.input.parentNode) self.afterInputReady();
if(self.adjust_height) self.adjust_height(self.input);
});
// Compile and store the template
if(this.schema.template) {
this.template = this.jsoneditor.compileTemplate(this.schema.template, this.template_engine);
this.refreshValue();
}
else {
this.refreshValue();
}
},
enable: function() {
if(!this.always_disabled) {
this.input.disabled = false;
// TODO: WYSIWYG and Markdown editors
}
this._super();
},
disable: function() {
this.input.disabled = true;
// TODO: WYSIWYG and Markdown editors
this._super();
},
afterInputReady: function() {
var self = this, options;
// Code editor
if(this.source_code) {
// WYSIWYG html and bbcode editor
if(this.options.wysiwyg &&
['html','bbcode'].indexOf(this.input_type) >= 0 &&
window.jQuery && window.jQuery.fn && window.jQuery.fn.sceditor
) {
options = $extend({},{
plugins: self.input_type==='html'? 'xhtml' : 'bbcode',
emoticonsEnabled: false,
width: '100%',
height: 300
},JSONEditor.plugins.sceditor,self.options.sceditor_options||{});
window.jQuery(self.input).sceditor(options);
self.sceditor_instance = window.jQuery(self.input).sceditor('instance');
self.sceditor_instance.blur(function() {
// Get editor's value
var val = window.jQuery("<div>"+self.sceditor_instance.val()+"</div>");
// Remove sceditor spans/divs
window.jQuery('#sceditor-start-marker,#sceditor-end-marker,.sceditor-nlf',val).remove();
// Set the value and update
self.input.value = val.html();
self.value = self.input.value;
self.is_dirty = true;
self.onChange(true);
});
}
// EpicEditor for markdown (if it's loaded)
else if (this.input_type === 'markdown' && window.EpicEditor) {
this.epiceditor_container = document.createElement('div');
this.input.parentNode.insertBefore(this.epiceditor_container,this.input);
this.input.style.display = 'none';
options = $extend({},JSONEditor.plugins.epiceditor,{
container: this.epiceditor_container,
clientSideStorage: false
});
this.epiceditor = new window.EpicEditor(options).load();
this.epiceditor.importFile(null,this.getValue());
this.epiceditor.on('update',function() {
var val = self.epiceditor.exportFile();
self.input.value = val;
self.value = val;
self.is_dirty = true;
self.onChange(true);
});
}
// ACE editor for everything else
else if(window.ace) {
var mode = this.input_type;
// aliases for c/cpp
if(mode === 'cpp' || mode === 'c++' || mode === 'c') {
mode = 'c_cpp';
}
this.ace_container = document.createElement('div');
this.ace_container.style.width = '100%';
this.ace_container.style.position = 'relative';
this.ace_container.style.height = '400px';
this.input.parentNode.insertBefore(this.ace_container,this.input);
this.input.style.display = 'none';
this.ace_editor = window.ace.edit(this.ace_container);
this.ace_editor.setValue(this.getValue());
// The theme
if(JSONEditor.plugins.ace.theme) this.ace_editor.setTheme('ace/theme/'+JSONEditor.plugins.ace.theme);
// The mode
mode = window.ace.require("ace/mode/"+mode);
if(mode) this.ace_editor.getSession().setMode(new mode.Mode());
// Listen for changes
this.ace_editor.on('change',function() {
var val = self.ace_editor.getValue();
self.input.value = val;
self.refreshValue();
self.is_dirty = true;
self.onChange(true);
});
}
}
self.theme.afterInputReady(self.input);
},
refreshValue: function() {
this.value = this.input.value;
if(typeof this.value !== "string") this.value = '';
this.serialized = this.value;
},
destroy: function() {
// If using SCEditor, destroy the editor instance
if(this.sceditor_instance) {
this.sceditor_instance.destroy();
}
else if(this.epiceditor) {
this.epiceditor.unload();
}
else if(this.ace_editor) {
this.ace_editor.destroy();
}
this.template = null;
if(this.input && this.input.parentNode) this.input.parentNode.removeChild(this.input);
if(this.label && this.label.parentNode) this.label.parentNode.removeChild(this.label);
if(this.description && this.description.parentNode) this.description.parentNode.removeChild(this.description);
this._super();
},
/**
* This is overridden in derivative editors
*/
sanitize: function(value) {
return value;
},
/**
* Re-calculates the value if needed
*/
onWatchedFieldChange: function() {
var self = this, vars, j;
// If this editor needs to be rendered by a macro template
if(this.template) {
vars = this.getWatchedFieldValues();
this.setValue(this.template(vars),false,true);
}
this._super();
},
showValidationErrors: function(errors) {
var self = this;
if(this.jsoneditor.options.show_errors === "always") {}
else if(!this.is_dirty && this.previous_error_setting===this.jsoneditor.options.show_errors) return;
this.previous_error_setting = this.jsoneditor.options.show_errors;
var messages = [];
$each(errors,function(i,error) {
if(error.path === self.path) {
messages.push(error.message);
}
});
if(messages.length) {
this.theme.addInputError(this.input, messages.join('. ')+'.');
}
else {
this.theme.removeInputError(this.input);
}
}
});