/** * -------------------------------------------------------------------------- * Ace (v4.0.0): wysiwyg.js Wrapper for Bootstrap wyswiwyg plugin */ import $ from 'jquery' import bootstrap from 'bootstrap' /** * ------------------------------------------------------------------------ * Constants * ------------------------------------------------------------------------ */ const NAME = 'aceWysiwyg' const VERSION = '4.0.0' const DATA_KEY = 'ace.wysiwyg' const DefaultType = { wysiwyg: 'object', colors: 'array', // speech: 'boolean', toolbar: 'array', toolbarPlacement: '(function|null)', toolbarStyle: '(string|number)' } const Default = { wysiwyg: {}, // speech: true, toolbarPlacement: null, toolbarStyle: '', colors: ['#000000', '#424242', '#636363', '#9c9c94', '#cec6ce', '#efefef', '#f7f7f7', '#ffffff', '#ff0000', '#ff9c00', '#ffff00', '#00ff00', '#00ffff', '#0000ff', '#9c00ff', '#ff00ff', '#f7c6ce', '#ffe7ce', '#ffefc6', '#d6efd6', '#cedee7', '#cee7f7', '#d6d6e7', '#e7d6de', '#e79c9c', '#ffc69c', '#ffe79c', '#b5d6a5', '#a5c6ce', '#9cc6ef', '#b5a5d6', '#d6a5bd', '#e76363', '#f7ad6b', '#ffd663', '#94bd7b', '#73a5ad', '#6badde', '#8c7bc6', '#c67ba5', '#ce0000', '#e79439', '#efc631', '#6ba54a', '#4a7b8c', '#3984c6', '#634aa5', '#a54a7b', '#9c0000', '#b56308', '#bd9400', '#397b21', '#104a5a', '#085294', '#311873', '#731842', '#630000', '#7b3900', '#846300', '#295218', '#083139', '#003163', '#21104a', '#4a1031'], toolbar: ['font', null, 'fontSize', null, 'bold', 'italic', 'strikethrough', 'underline', null, 'insertunorderedlist', 'insertorderedlist', 'outdent', 'indent', null, 'justifyleft', 'justifycenter', 'justifyright', 'justifyfull', null, 'createLink', 'unlink', null, 'insertImage', null, 'foreColor', null, 'undo', 'redo', null, 'viewSource'] } class Wysiwyg { constructor (element, config) { this._element = element this._config = this._getConfig(config) this.initEditor() } static get VERSION () { return VERSION } static get DefaultType () { return DefaultType } static get Default () { return Default } initEditor () { const toolbarHtml = this._createToolbarHtml() let toolbar // if we have a function to decide where to put the toolbar, then call that if (this._config.toolbarPlacement) toolbar = this._config.toolbarPlacement.call(this._element, toolbarHtml) else toolbar = $(this._element).before(toolbarHtml).prev()// otherwise put it just before our DIV if (this._config.toolbarStyle) toolbar.addClass('bsw-toolbar-style-' + this._config.toolbarStyle) // enable tooltips if ($.fn.tooltip) toolbar.find('a[title]').tooltip({ animation: false, container: 'body' }) toolbar.find('.dropdown-menu input[type=text]').on('click', function () { return false }) .on('change', function () { $(this).closest('.dropdown-menu').siblings('.dropdown-toggle').dropdown('toggle') }) .on('keydown', function (e) { if (e.which === 27) { this.value = '' // $(this).change() } else if (e.which === 13) { e.preventDefault() e.stopPropagation() // $(this).change() } }) toolbar.find('input[type=file]').prev().on('click', function (e) { // $(this).next().click() }) const self = $(this._element) // view source let viewSource = false toolbar.find('a[data-toggle=source]').on('click', function (e) { e.preventDefault() if (!viewSource) { $('') .css({ width: self.outerWidth(), height: self.outerHeight() }) .val(self.html()) .insertAfter(self) self.addClass('d-none') $(this).addClass('active') } else { const textarea = self.next() self.html(textarea.val()).removeClass('d-none') textarea.remove() $(this).removeClass('active') } viewSource = !viewSource }) // initiate wysiwyg plugin const $options = $.extend({}, { activeToolbarClass: 'active', toolbarSelector: toolbar }, this._config.wysiwyg || {}) $(this._element).wysiwyg($options) this._handleImages() } _createToolbarHtml () { const _buttonDefaults = { font: { values: ['Arial', 'Courier', 'Comic Sans MS', 'Helvetica', 'Open Sans', 'Tahoma', 'Verdana'], icon: 'fa fa-font text-secondary', title: 'Font' }, fontSize: { values: { 5: 'Huge', 3: 'Normal', 1: 'Small' }, icon: 'fa fa-text-height text-secondary', title: 'Font Size' }, bold: { icon: 'fa fa-bold text-secondary', title: 'Bold (Ctrl/Cmd+B)' }, italic: { icon: 'fa fa-italic text-secondary', title: 'Italic (Ctrl/Cmd+I)' }, strikethrough: { icon: 'fa fa-strikethrough text-secondary', title: 'Strikethrough' }, underline: { icon: 'fa fa-underline text-secondary', title: 'Underline' }, insertunorderedlist: { icon: 'fa fa-list-ul text-secondary', title: 'Bullet list' }, insertorderedlist: { icon: 'fa fa-list-ol text-secondary', title: 'Number list' }, outdent: { icon: 'fa fa-outdent text-secondary', title: 'Reduce indent (Shift+Tab)' }, indent: { icon: 'fa fa-indent text-secondary', title: 'Indent (Tab)' }, justifyleft: { icon: 'fa fa-align-left text-secondary', title: 'Align Left (Ctrl/Cmd+L)' }, justifycenter: { icon: 'fa fa-align-center text-secondary', title: 'Center (Ctrl/Cmd+E)' }, justifyright: { icon: 'fa fa-align-right text-secondary', title: 'Align Right (Ctrl/Cmd+R)' }, justifyfull: { icon: 'fa fa-align-justify text-secondary', title: 'Justify (Ctrl/Cmd+J)' }, createLink: { icon: 'fa fa-link text-secondary', title: 'Hyperlink', button_text: 'Add', placeholder: 'URL', button_class: 'btn-light-primary' }, unlink: { icon: 'fa fa-unlink text-secondary', title: 'Remove Hyperlink' }, insertImage: { icon: 'fa fa-image text-secondary', title: 'Insert picture', button_text: ' Choose an Image …', placeholder: 'Remote Image URL', button_insert: 'Insert', button_class: 'btn-light-success', button_insert_class: 'btn-light-primary', choose_file: true // show the choose file button? }, foreColor: { icon: 'fas fa-eye-dropper text-pink-m1', values: this._config.colors, title: 'Foreground Color' }, backColor: { icon: 'fas fa-fill-drip text-secondary', values: this._config.colors, title: 'Background Color' }, removeFormat: { icon: 'fa fa-eraser text-secondary', title: 'Remove Format' }, undo: { icon: 'fa fa-undo text-secondary', title: 'Undo (Ctrl/Cmd+Z)' }, redo: { icon: 'fa fa-redo text-secondary', title: 'Redo (Ctrl/Cmd+Y)' }, viewSource: { icon: 'fa fa-code text-secondary', title: 'View Source' } } const toolbarButtons = this._config.toolbar let toolbarHtml = '
'// for .btn-toolbar return toolbarHtml } _handleImages () { // option for resizing an image let currentImg = null $(this._element).on('click', 'img', function (ev) { if (currentImg) $(currentImg).popover('dispose') currentImg = this if (!$(currentImg).data('original-width')) $(currentImg).data('original-width', currentImg.width) $(currentImg).popover({ container: 'body', html: true, placement: function (popover) { const offset = currentImg.getBoundingClientRect() const scrollTop = document.scrollTop || document.documentElement.scrollTop || document.body.scrollTop $(popover).addClass('popover-wysiwyg-image shadow brc-secondary-m4').css({ 'margin-left': (offset.left + 4) + 'px', 'margin-top': (offset.top + scrollTop + 4) + 'px' }) return 'auto' }, title: 'Image Size & Position', trigger: 'manual', content: function () { return $(`