import env from '../core/env';
import dom from '../core/dom';
let CodeMirror;
Iif (env.hasCodeMirror) {
if (env.isSupportAmd) {
require(['codemirror'], function(cm) {
CodeMirror = cm;
});
} else {
CodeMirror = window.CodeMirror;
}
}
/**
* @class Codeview
*/
export default class CodeView {
constructor(context) {
this.context = context;
this.$editor = context.layoutInfo.editor;
this.$editable = context.layoutInfo.editable;
this.$codable = context.layoutInfo.codable;
this.options = context.options;
}
sync() {
const isCodeview = this.isActivated();
Iif (isCodeview && env.hasCodeMirror) {
this.$codable.data('cmEditor').save();
}
}
/**
* @return {Boolean}
*/
isActivated() {
return this.$editor.hasClass('codeview');
}
/**
* toggle codeview
*/
toggle() {
if (this.isActivated()) {
this.deactivate();
} else {
this.activate();
}
this.context.triggerEvent('codeview.toggled');
}
/**
* purify input value
* @param value
* @returns {*}
*/
purify(value) {
if (this.options.codeviewFilter) {
// filter code view regex
value = value.replace(this.options.codeviewFilterRegex, '');
// allow specific iframe tag
if (this.options.codeviewIframeFilter) {
const whitelist = this.options.codeviewIframeWhitelistSrc.concat(this.options.codeviewIframeWhitelistSrcBase);
value = value.replace(/(<iframe.*?>.*?(?:<\/iframe>)?)/gi, function(tag) {
// remove if src attribute is duplicated
if (/<.+src(?==?('|"|\s)?)[\s\S]+src(?=('|"|\s)?)[^>]*?>/i.test(tag)) {
return '';
}
for (const src of whitelist) {
// pass if src is trusted
if ((new RegExp('src="(https?:)?\/\/' + src + '\/(.+)"')).test(tag)) {
return tag;
}
}
return '';
});
}
}
return value;
}
/**
* activate code view
*/
activate() {
this.$codable.val(dom.html(this.$editable, this.options.prettifyHtml));
this.$codable.height(this.$editable.height());
this.context.invoke('toolbar.updateCodeview', true);
this.$editor.addClass('codeview');
this.$codable.focus();
// activate CodeMirror as codable
Iif (env.hasCodeMirror) {
const cmEditor = CodeMirror.fromTextArea(this.$codable[0], this.options.codemirror);
// CodeMirror TernServer
if (this.options.codemirror.tern) {
const server = new CodeMirror.TernServer(this.options.codemirror.tern);
cmEditor.ternServer = server;
cmEditor.on('cursorActivity', (cm) => {
server.updateArgHints(cm);
});
}
cmEditor.on('blur', (event) => {
this.context.triggerEvent('blur.codeview', cmEditor.getValue(), event);
});
cmEditor.on('change', (event) => {
this.context.triggerEvent('change.codeview', cmEditor.getValue(), cmEditor);
});
// CodeMirror hasn't Padding.
cmEditor.setSize(null, this.$editable.outerHeight());
this.$codable.data('cmEditor', cmEditor);
} else {
this.$codable.on('blur', (event) => {
this.context.triggerEvent('blur.codeview', this.$codable.val(), event);
});
this.$codable.on('input', (event) => {
this.context.triggerEvent('change.codeview', this.$codable.val(), this.$codable);
});
}
}
/**
* deactivate code view
*/
deactivate() {
// deactivate CodeMirror as codable
Iif (env.hasCodeMirror) {
const cmEditor = this.$codable.data('cmEditor');
this.$codable.val(cmEditor.getValue());
cmEditor.toTextArea();
}
const value = this.purify(dom.value(this.$codable, this.options.prettifyHtml) || dom.emptyPara);
const isChange = this.$editable.html() !== value;
this.$editable.html(value);
this.$editable.height(this.options.height ? this.$codable.height() : 'auto');
this.$editor.removeClass('codeview');
Iif (isChange) {
this.context.triggerEvent('change', this.$editable.html(), this.$editable);
}
this.$editable.focus();
this.context.invoke('toolbar.updateCodeview', false);
}
destroy() {
Iif (this.isActivated()) {
this.deactivate();
}
}
}
|