2016-02-26 15:54:06 -08:00
|
|
|
import $ from 'jquery';
|
|
|
|
|
import Buttons, { strategies as buttonStrategies } from './editor/buttons';
|
|
|
|
|
import codemirror from 'codemirror';
|
|
|
|
|
import { watch } from 'watchjs';
|
2016-03-02 14:38:48 -08:00
|
|
|
import jsyaml from 'js-yaml';
|
|
|
|
|
|
|
|
|
|
global.jsyaml = jsyaml;
|
2016-02-26 15:54:06 -08:00
|
|
|
|
|
|
|
|
// Modes
|
|
|
|
|
import 'codemirror/mode/css/css';
|
|
|
|
|
import 'codemirror/mode/gfm/gfm';
|
|
|
|
|
import 'codemirror/mode/htmlmixed/htmlmixed';
|
|
|
|
|
import 'codemirror/mode/javascript/javascript';
|
|
|
|
|
import 'codemirror/mode/markdown/markdown';
|
|
|
|
|
import 'codemirror/mode/php/php';
|
|
|
|
|
import 'codemirror/mode/sass/sass';
|
|
|
|
|
import 'codemirror/mode/twig/twig';
|
|
|
|
|
import 'codemirror/mode/xml/xml';
|
|
|
|
|
import 'codemirror/mode/yaml/yaml';
|
2016-03-02 14:38:48 -08:00
|
|
|
import 'codemirror/addon/lint/lint';
|
|
|
|
|
import 'codemirror/addon/lint/lint.css';
|
|
|
|
|
import 'codemirror/addon/lint/css-lint';
|
|
|
|
|
import 'codemirror/addon/lint/javascript-lint';
|
|
|
|
|
import 'codemirror/addon/lint/json-lint';
|
|
|
|
|
import 'codemirror/addon/lint/yaml-lint';
|
2016-02-26 15:54:06 -08:00
|
|
|
|
|
|
|
|
// Add-ons
|
|
|
|
|
import 'codemirror/addon/edit/continuelist';
|
|
|
|
|
import 'codemirror/addon/mode/overlay';
|
|
|
|
|
|
|
|
|
|
const ThemesMap = ['paper'];
|
|
|
|
|
const Defaults = {
|
|
|
|
|
codemirror: {
|
|
|
|
|
mode: 'htmlmixed',
|
|
|
|
|
theme: 'paper',
|
|
|
|
|
lineWrapping: true,
|
|
|
|
|
dragDrop: true,
|
|
|
|
|
autoCloseTags: true,
|
|
|
|
|
matchTags: true,
|
|
|
|
|
autoCloseBrackets: true,
|
|
|
|
|
matchBrackets: true,
|
|
|
|
|
indentUnit: 4,
|
|
|
|
|
indentWithTabs: false,
|
|
|
|
|
tabSize: 4,
|
|
|
|
|
hintOptions: { completionSingle: false },
|
|
|
|
|
extraKeys: { 'Enter': 'newlineAndIndentContinueMarkdownList' }
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default class EditorField {
|
|
|
|
|
constructor(options) {
|
|
|
|
|
this.editors = $();
|
|
|
|
|
this.options = Object.assign({}, Defaults, options);
|
|
|
|
|
this.buttons = Buttons;
|
|
|
|
|
this.buttonStrategies = buttonStrategies;
|
|
|
|
|
|
|
|
|
|
watch(Buttons, (/* key, modifier, prev, next */) => {
|
|
|
|
|
this.editors.each((index, editor) => $(editor).data('toolbar').renderButtons());
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$('[data-grav-editor]').each((index, editor) => this.addEditor(editor));
|
|
|
|
|
|
|
|
|
|
$('window').trigger('grav-editor-ready');
|
2016-03-01 19:12:05 -08:00
|
|
|
$('body').on('mutation._grav', this._onAddedNodes.bind(this));
|
2016-02-26 15:54:06 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addButton(button, options) {
|
|
|
|
|
if (options.before || options.after) {
|
|
|
|
|
let index = this.buttons.navigation.findIndex((obj) => {
|
|
|
|
|
let key = Object.keys(obj).shift();
|
|
|
|
|
return obj[key].identifier === (options.before || options.after);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!~index) {
|
|
|
|
|
options = 'end';
|
|
|
|
|
} else {
|
|
|
|
|
this.buttons.navigation.splice(options.before ? index : index + 1, 0, button);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (options === 'start') { this.buttons.navigation.splice(0, 0, button); }
|
|
|
|
|
if (options === 'end') { this.buttons.navigation.push(button); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addEditor(textarea) {
|
|
|
|
|
textarea = $(textarea);
|
|
|
|
|
let options = Object.assign(
|
|
|
|
|
{},
|
|
|
|
|
this.options.codemirror,
|
|
|
|
|
textarea.data('grav-editor').codemirror
|
|
|
|
|
);
|
|
|
|
|
let theme = options.theme || 'paper';
|
|
|
|
|
|
|
|
|
|
this.editors = this.editors.add(textarea);
|
|
|
|
|
if (theme && !~ThemesMap.indexOf(theme)) {
|
|
|
|
|
ThemesMap.push(theme);
|
|
|
|
|
let themeCSS = `https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.12.0/theme/${theme}.min.css`;
|
|
|
|
|
$('head').append($('<link rel="stylesheet" type="text/css" />').attr('href', themeCSS));
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-02 14:37:47 -08:00
|
|
|
if (options.mode === 'yaml') {
|
|
|
|
|
Object.assign(options.extraKeys, { Tab: function(cm) { cm.replaceSelection(' ', 'end'); }});
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-01 21:56:32 -08:00
|
|
|
let editor = codemirror.fromTextArea(textarea.get(0), options);
|
|
|
|
|
textarea.data('codemirror', editor);
|
2016-02-26 15:54:06 -08:00
|
|
|
textarea.data('toolbar', new Toolbar(textarea));
|
2016-03-01 21:56:32 -08:00
|
|
|
|
|
|
|
|
editor.on('change', () => editor.save());
|
2016-02-26 15:54:06 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_onAddedNodes(event, target/* , record, instance */) {
|
|
|
|
|
let editors = $(target).find('[data-grav-editor]');
|
|
|
|
|
if (!editors.length) { return; }
|
|
|
|
|
|
|
|
|
|
editors.each((index, editor) => {
|
|
|
|
|
editor = $(editor);
|
|
|
|
|
if (!~this.editors.index(editor)) {
|
|
|
|
|
this.addEditor(editor);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class Toolbar {
|
|
|
|
|
static templates() {
|
|
|
|
|
return {
|
|
|
|
|
navigation: `
|
2016-03-01 19:00:29 -08:00
|
|
|
<div class="grav-editor-toolbar">
|
|
|
|
|
<div class="grav-editor-actions"></div>
|
|
|
|
|
<div class="grav-editor-modes"></div>
|
|
|
|
|
</div>
|
2016-02-26 15:54:06 -08:00
|
|
|
`,
|
|
|
|
|
states: `
|
2016-03-01 19:00:29 -08:00
|
|
|
<div class="grav-editor-states">
|
|
|
|
|
<div class="grav-editor-info"></div>
|
|
|
|
|
<div class="grav-editor-modes"></div>
|
|
|
|
|
</div>
|
2016-02-26 15:54:06 -08:00
|
|
|
`
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
constructor(editor) {
|
|
|
|
|
this.editor = $(editor);
|
|
|
|
|
this.codemirror = this.editor.data('codemirror');
|
|
|
|
|
this.buttons = Buttons.navigation;
|
|
|
|
|
this.ui = {
|
|
|
|
|
navigation: $(Toolbar.templates().navigation),
|
|
|
|
|
states: $(Toolbar.templates().states)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
this.editor.parent('.grav-editor-content')
|
|
|
|
|
.before(this.ui.navigation)
|
|
|
|
|
.after(this.ui.states);
|
|
|
|
|
|
|
|
|
|
this.renderButtons();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
renderButtons() {
|
2016-03-01 19:00:29 -08:00
|
|
|
this.ui.navigation.find('.grav-editor-actions').empty().append('<ul />');
|
2016-02-26 15:54:06 -08:00
|
|
|
Buttons.navigation.forEach((button) => {
|
|
|
|
|
Object.keys(button).forEach((key) => {
|
2016-03-02 14:37:47 -08:00
|
|
|
let obj = button[key];
|
|
|
|
|
if (!obj.modes) { obj.modes = []; }
|
|
|
|
|
if (!~this.codemirror.options.ignore.indexOf(key) && (!obj.modes.length || obj.modes.indexOf(this.codemirror.options.mode) > -1)) {
|
2016-03-01 22:14:21 -08:00
|
|
|
let element = $(`<li class="grav-editor-button-${key}"><a class="hint--top" data-hint="${obj.title}" title="${obj.title}">${obj.label}</a></li>`);
|
|
|
|
|
this.ui.navigation.find('.grav-editor-actions ul').append(element);
|
|
|
|
|
|
|
|
|
|
obj.action && obj.action.call(obj.action, {
|
|
|
|
|
codemirror: this.codemirror,
|
|
|
|
|
button: element,
|
|
|
|
|
textarea: this.editor,
|
|
|
|
|
ui: this.ui
|
|
|
|
|
});
|
|
|
|
|
}
|
2016-03-01 19:00:29 -08:00
|
|
|
});
|
|
|
|
|
});
|
2016-02-26 15:54:06 -08:00
|
|
|
|
2016-03-01 19:00:29 -08:00
|
|
|
this.ui.navigation.find('.grav-editor-modes').empty().append('<ul />');
|
|
|
|
|
Buttons.states.forEach((button) => {
|
|
|
|
|
Object.keys(button).forEach((key) => {
|
2016-03-02 14:37:47 -08:00
|
|
|
let obj = button[key];
|
|
|
|
|
if (!obj.modes) { obj.modes = []; }
|
|
|
|
|
if (!~this.codemirror.options.ignore.indexOf(key) && (!obj.modes.length || obj.modes.indexOf(this.codemirror.options.mode) > -1)) {
|
2016-03-01 22:14:21 -08:00
|
|
|
let element = $(`<li class="grav-editor-button-${key}"><a class="hint--top" data-hint="${obj.title}" title="${obj.title}">${obj.label}</a></li>`);
|
|
|
|
|
this.ui.navigation.find('.grav-editor-modes ul').append(element);
|
|
|
|
|
|
|
|
|
|
obj.action && obj.action.call(obj.action, {
|
|
|
|
|
codemirror: this.codemirror,
|
|
|
|
|
button: element,
|
|
|
|
|
textarea: this.editor,
|
|
|
|
|
ui: this.ui
|
|
|
|
|
});
|
|
|
|
|
}
|
2016-02-26 15:54:06 -08:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export let Instance = new EditorField();
|