UNPKG

@btag/react-trumbowyg

Version:
1,475 lines (1,247 loc) 223 kB
(function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if(typeof define === 'function' && define.amd) define([], factory); else if(typeof exports === 'object') exports["react-trumbowyg"] = factory(); else root["react-trumbowyg"] = factory(); })(this, function() { return /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) /******/ return installedModules[moduleId].exports; /******/ /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ exports: {}, /******/ id: moduleId, /******/ loaded: false /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.loaded = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(0); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ (function(module, exports, __webpack_require__) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _react = __webpack_require__(11); var _react2 = _interopRequireDefault(_react); var _propTypes = __webpack_require__(8); var _propTypes2 = _interopRequireDefault(_propTypes); var _btTrumbowyg = __webpack_require__(6); var _btTrumbowyg2 = _interopRequireDefault(_btTrumbowyg); var _icons = __webpack_require__(12); var _icons2 = _interopRequireDefault(_icons); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var trumbowygIconsId = 'trumbowyg-icons'; var Trumbowyg = function (_Component) { _inherits(Trumbowyg, _Component); function Trumbowyg(props) { _classCallCheck(this, Trumbowyg); return _possibleConstructorReturn(this, (Trumbowyg.__proto__ || Object.getPrototypeOf(Trumbowyg)).call(this, props)); } _createClass(Trumbowyg, [{ key: 'componentDidMount', value: function componentDidMount() { var _props = this.props, id = _props.id, lang = _props.lang, buttons = _props.buttons, semantic = _props.semantic, resetCss = _props.resetCss, removeformatPasted = _props.removeformatPasted, autogrow = _props.autogrow, data = _props.data, disabled = _props.disabled, onFocus = _props.onFocus, onBlur = _props.onBlur, onInit = _props.onInit, onChange = _props.onChange, onResize = _props.onResize, onPaste = _props.onPaste, onOpenFullScreen = _props.onOpenFullScreen, onCloseFullScreen = _props.onCloseFullScreen, onClose = _props.onClose, shouldUseSvgIcons = _props.shouldUseSvgIcons, shouldInjectSvgIcons = _props.shouldInjectSvgIcons, svgIconsPath = _props.svgIconsPath, btnsDef = _props.btnsDef, plugins = _props.plugins; if (shouldInjectSvgIcons && $('#' + trumbowygIconsId).length === 0) { $('body').prepend('<div id="' + trumbowygIconsId + '">' + _icons2.default + '</div>'); } var trumbowygInstance = $('#' + id).trumbowyg({ lang: lang, btns: buttons, btnsDef: btnsDef, semantic: semantic, resetCss: resetCss, removeformatPasted: removeformatPasted, autogrow: autogrow, plugins: plugins, svgPath: shouldUseSvgIcons ? shouldInjectSvgIcons ? '' : svgIconsPath : false }); if (onFocus) { trumbowygInstance.on('tbwfocus', onFocus); } if (onBlur) { trumbowygInstance.on('tbwblur', onBlur); } if (onInit) { trumbowygInstance.on('tbwinit', onInit); } if (onChange) { trumbowygInstance.on('tbwchange', onChange); } if (onResize) { trumbowygInstance.on('tbwresize', onResize); } if (onPaste) { trumbowygInstance.on('tbwpaste', onPaste); } if (onOpenFullScreen) { trumbowygInstance.on('tbwopenfullscreen', onOpenFullScreen); } if (onCloseFullScreen) { trumbowygInstance.on('tbwclosefullscreen', onCloseFullScreen); } if (onClose) { trumbowygInstance.on('tbwclose', onClose); } $('#' + id).trumbowyg('html', data); $('#' + id).trumbowyg(disabled === true ? 'disable' : 'enable'); } }, { key: 'shouldComponentUpdate', value: function shouldComponentUpdate(nextProps, nextState) { return nextProps.data !== this.props.data || nextProps.disabled !== this.props.disabled; } }, { key: 'componentDidUpdate', value: function componentDidUpdate(prevProps, prevState) { $('#' + this.props.id).trumbowyg('html', this.props.data); $('#' + this.props.id).trumbowyg(this.props.disabled === true ? 'disable' : 'enable'); } }, { key: 'componentWillUnmount', value: function componentWillUnmount() { $('#' + this.props.id).trumbowyg('destroy'); } }, { key: 'render', value: function render() { return _react2.default.createElement('div', { id: '' + this.props.id, role: 'textbox', 'aria-multiline': 'true', 'aria-label': this.props.ariaLabel, placeholder: this.props.placeholder }); } }]); return Trumbowyg; }(_react.Component); Trumbowyg.defaultProps = { buttons: [['viewHTML'], ['formatting'], ['strong', 'em', 'underline'], ['link'], ['insertImage'], ['justifyLeft', 'justifyCenter', 'justifyRight', 'justifyFull'], ['unorderedList', 'orderedList'], ['horizontalRule'], ['removeformat']], semantic: true, resetCss: false, removeformatPasted: false, autogrow: false, disabled: false, shouldUseSvgIcons: true, shouldInjectSvgIcons: true, plugins: {}, ariaLabel: "Real Text Editor" }; Trumbowyg.propTypes = { id: _propTypes2.default.string.isRequired, data: _propTypes2.default.string.isRequired, placeholder: _propTypes2.default.string, buttons: _propTypes2.default.array, semantic: _propTypes2.default.bool, resetCss: _propTypes2.default.bool, removeformatPasted: _propTypes2.default.bool, autogrow: _propTypes2.default.bool, disabled: _propTypes2.default.bool, btnsDef: _propTypes2.default.object, //event handlers onFocus: _propTypes2.default.func, onBlur: _propTypes2.default.func, onInit: _propTypes2.default.func, onChange: _propTypes2.default.func, onResize: _propTypes2.default.func, onPaste: _propTypes2.default.func, onOpenFullScreen: _propTypes2.default.func, onCloseFullScreen: _propTypes2.default.func, onClose: _propTypes2.default.func, shouldUseSvgIcons: _propTypes2.default.bool.isRequired, svgIconsPath: _propTypes2.default.string, shouldInjectSvgIcons: _propTypes2.default.bool.isRequired, plugins: _propTypes2.default.object, ariaLabel: _propTypes2.default.string }; exports.default = Trumbowyg; /***/ }), /* 1 */ /***/ (function(module, exports) { /* object-assign (c) Sindre Sorhus @license MIT */ 'use strict'; /* eslint-disable no-unused-vars */ var getOwnPropertySymbols = Object.getOwnPropertySymbols; var hasOwnProperty = Object.prototype.hasOwnProperty; var propIsEnumerable = Object.prototype.propertyIsEnumerable; function toObject(val) { if (val === null || val === undefined) { throw new TypeError('Object.assign cannot be called with null or undefined'); } return Object(val); } function shouldUseNative() { try { if (!Object.assign) { return false; } // Detect buggy property enumeration order in older V8 versions. // https://bugs.chromium.org/p/v8/issues/detail?id=4118 var test1 = new String('abc'); // eslint-disable-line no-new-wrappers test1[5] = 'de'; if (Object.getOwnPropertyNames(test1)[0] === '5') { return false; } // https://bugs.chromium.org/p/v8/issues/detail?id=3056 var test2 = {}; for (var i = 0; i < 10; i++) { test2['_' + String.fromCharCode(i)] = i; } var order2 = Object.getOwnPropertyNames(test2).map(function (n) { return test2[n]; }); if (order2.join('') !== '0123456789') { return false; } // https://bugs.chromium.org/p/v8/issues/detail?id=3056 var test3 = {}; 'abcdefghijklmnopqrst'.split('').forEach(function (letter) { test3[letter] = letter; }); if (Object.keys(Object.assign({}, test3)).join('') !== 'abcdefghijklmnopqrst') { return false; } return true; } catch (err) { // We don't expect any of the above to throw, but better to be safe. return false; } } module.exports = shouldUseNative() ? Object.assign : function (target, source) { var from; var to = toObject(target); var symbols; for (var s = 1; s < arguments.length; s++) { from = Object(arguments[s]); for (var key in from) { if (hasOwnProperty.call(from, key)) { to[key] = from[key]; } } if (getOwnPropertySymbols) { symbols = getOwnPropertySymbols(from); for (var i = 0; i < symbols.length; i++) { if (propIsEnumerable.call(from, symbols[i])) { to[symbols[i]] = from[symbols[i]]; } } } } return to; }; /***/ }), /* 2 */ /***/ (function(module, exports, __webpack_require__) { /** * Copyright (c) 2013-present, Facebook, Inc. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ 'use strict'; var printWarning = function() {}; if (true) { var ReactPropTypesSecret = __webpack_require__(3); var loggedTypeFailures = {}; var has = __webpack_require__(4); printWarning = function(text) { var message = 'Warning: ' + text; if (typeof console !== 'undefined') { console.error(message); } try { // --- Welcome to debugging React --- // This error was thrown as a convenience so that you can use this stack // to find the callsite that caused this warning to fire. throw new Error(message); } catch (x) { /**/ } }; } /** * Assert that the values match with the type specs. * Error messages are memorized and will only be shown once. * * @param {object} typeSpecs Map of name to a ReactPropType * @param {object} values Runtime values that need to be type-checked * @param {string} location e.g. "prop", "context", "child context" * @param {string} componentName Name of the component for error messages. * @param {?Function} getStack Returns the component stack. * @private */ function checkPropTypes(typeSpecs, values, location, componentName, getStack) { if (true) { for (var typeSpecName in typeSpecs) { if (has(typeSpecs, typeSpecName)) { var error; // Prop type validation may throw. In case they do, we don't want to // fail the render phase where it didn't fail before. So we log it. // After these have been cleaned up, we'll let them throw. try { // This is intentionally an invariant that gets caught. It's the same // behavior as without this statement except with a better message. if (typeof typeSpecs[typeSpecName] !== 'function') { var err = Error( (componentName || 'React class') + ': ' + location + ' type `' + typeSpecName + '` is invalid; ' + 'it must be a function, usually from the `prop-types` package, but received `' + typeof typeSpecs[typeSpecName] + '`.' + 'This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`.' ); err.name = 'Invariant Violation'; throw err; } error = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, ReactPropTypesSecret); } catch (ex) { error = ex; } if (error && !(error instanceof Error)) { printWarning( (componentName || 'React class') + ': type specification of ' + location + ' `' + typeSpecName + '` is invalid; the type checker ' + 'function must return `null` or an `Error` but returned a ' + typeof error + '. ' + 'You may have forgotten to pass an argument to the type checker ' + 'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' + 'shape all require an argument).' ); } if (error instanceof Error && !(error.message in loggedTypeFailures)) { // Only monitor this failure once because there tends to be a lot of the // same error. loggedTypeFailures[error.message] = true; var stack = getStack ? getStack() : ''; printWarning( 'Failed ' + location + ' type: ' + error.message + (stack != null ? stack : '') ); } } } } } /** * Resets warning cache when testing. * * @private */ checkPropTypes.resetWarningCache = function() { if (true) { loggedTypeFailures = {}; } } module.exports = checkPropTypes; /***/ }), /* 3 */ /***/ (function(module, exports) { /** * Copyright (c) 2013-present, Facebook, Inc. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ 'use strict'; var ReactPropTypesSecret = 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED'; module.exports = ReactPropTypesSecret; /***/ }), /* 4 */ /***/ (function(module, exports) { module.exports = Function.call.bind(Object.prototype.hasOwnProperty); /***/ }), /* 5 */ /***/ (function(module, exports, __webpack_require__) { 'use strict'; if (false) { module.exports = require('./cjs/react-is.production.min.js'); } else { module.exports = __webpack_require__(9); } /***/ }), /* 6 */ /***/ (function(module, exports) { /** * bt-trumbowyg v2.25.2 - Bananatag Version of Trumbowyg * Trumbowyg core file * ------------------------ * @link http://alex-d.github.io/Trumbowyg * @license MIT * @author Alexandre Demode (Alex-D) * Twitter : @AlexandreDemode * Website : alex-d.fr */ jQuery.trumbowyg = { langs: { en: { viewHTML: 'View HTML', undo: 'Undo', redo: 'Redo', formatting: 'Formatting', p: 'Paragraph', blockquote: 'Quote', code: 'Code', header: 'Header', bold: 'Bold', italic: 'Italic', strikethrough: 'Strikethrough', underline: 'Underline', strong: 'Strong', em: 'Emphasis', del: 'Deleted', superscript: 'Superscript', subscript: 'Subscript', unorderedList: 'Unordered list', orderedList: 'Ordered list', insertFirstName: "Insert {First Name}", insertLastName: "Insert {Last Name}", insertCustom: "Insert custom variable", insertImage: 'Insert Image', link: 'Link', createLink: 'Insert link', unlink: 'Remove link', justifyLeft: 'Align Left', justifyCenter: 'Align Center', justifyRight: 'Align Right', justifyFull: 'Align Justify', horizontalRule: 'Insert horizontal rule', removeformat: 'Remove format', fullscreen: 'Fullscreen', close: 'Close', submit: 'Confirm', reset: 'Cancel', required: 'Required', description: 'Description', title: 'Title', text: 'Text', target: 'Target', width: 'Width' } }, // Plugins plugins: {}, // SVG Path globally svgPath: null, svgAbsoluteUseHref: false, hideButtonTexts: null }; // Makes default options read-only Object.defineProperty(jQuery.trumbowyg, 'defaultOptions', { value: { lang: 'en', fixedBtnPane: false, fixedFullWidth: false, autogrow: false, autogrowOnEnter: false, imageWidthModalEdit: false, hideButtonTexts: null, prefix: 'trumbowyg-', tagClasses: {}, semantic: true, semanticKeepAttributes: false, resetCss: false, removeformatPasted: false, tabToIndent: false, tagsToRemove: [], tagsToKeep: ['hr', 'img', 'embed', 'iframe', 'input'], btns: [ ['viewHTML'], ['undo', 'redo'], // Only supported in Blink browsers ['formatting'], ['strong', 'em', 'del'], ['superscript', 'subscript'], ['link'], ['insertImage'], ['justifyLeft', 'justifyCenter', 'justifyRight', 'justifyFull'], ['unorderedList', 'orderedList'], ['horizontalRule'], ['removeformat'], ['fullscreen'] ], // For custom button definitions btnsDef: {}, changeActiveDropdownIcon: false, inlineElementsSelector: 'a,abbr,acronym,b,caption,cite,code,col,dfn,dir,dt,dd,em,font,hr,i,kbd,li,q,span,strikeout,strong,sub,sup,u', pasteHandlers: [], // imgDblClickHandler: default is defined in constructor plugins: {}, urlProtocol: false, minimalLinks: false, defaultLinkTarget: undefined, svgPath: null }, writable: false, enumerable: true, configurable: false }); (function (navigator, window, document, $) { 'use strict'; var CONFIRM_EVENT = 'tbwconfirm', CANCEL_EVENT = 'tbwcancel'; $.fn.trumbowyg = function (options, params) { var trumbowygDataName = 'trumbowyg'; if (options === Object(options) || !options) { return this.each(function () { if (!$(this).data(trumbowygDataName)) { $(this).data(trumbowygDataName, new Trumbowyg(this, options)); } }); } if (this.length === 1) { try { var t = $(this).data(trumbowygDataName); switch (options) { // Exec command case 'execCmd': return t.execCmd(params.cmd, params.param, params.forceCss, params.skipTrumbowyg); // Modal box case 'openModal': return t.openModal(params.title, params.content); case 'closeModal': return t.closeModal(); case 'openModalInsert': return t.openModalInsert(params.title, params.fields, params.callback); // Range case 'saveRange': return t.saveRange(); case 'getRange': return t.range; case 'getRangeText': return t.getRangeText(); case 'restoreRange': return t.restoreRange(); // Enable/disable case 'enable': return t.setDisabled(false); case 'disable': return t.setDisabled(true); // Toggle case 'toggle': return t.toggle(); // Destroy case 'destroy': return t.destroy(); // Empty case 'empty': return t.empty(); // HTML case 'html': return t.html(params); } } catch (c) { } } return false; }; // @param: editorElem is the DOM element var Trumbowyg = function (editorElem, options) { var t = this, trumbowygIconsId = 'trumbowyg-icons', $trumbowyg = $.trumbowyg; // Get the document of the element. It use to makes the plugin // compatible on iframes. t.doc = editorElem.ownerDocument || document; // jQuery object of the editor t.$ta = $(editorElem); // $ta : Textarea t.$c = $(editorElem); // $c : creator options = options || {}; // Localization management if (options.lang != null || $trumbowyg.langs[options.lang] != null) { t.lang = $.extend(true, {}, $trumbowyg.langs.en, $trumbowyg.langs[options.lang]); } else { t.lang = $trumbowyg.langs.en; } t.hideButtonTexts = $trumbowyg.hideButtonTexts != null ? $trumbowyg.hideButtonTexts : options.hideButtonTexts; // SVG path var svgPathOption = $trumbowyg.svgPath != null ? $trumbowyg.svgPath : options.svgPath; t.hasSvg = svgPathOption !== false; if (svgPathOption !== false && ($trumbowyg.svgAbsoluteUseHref || $('#' + trumbowygIconsId, t.doc).length === 0)) { if (svgPathOption == null) { // Hack to get svgPathOption based on trumbowyg.js path var $scriptElements = $('script[src]'); $scriptElements.each(function (i, scriptElement) { var source = scriptElement.src; var matches = source.match('trumbowyg(\.min)?\.js'); if (matches != null) { svgPathOption = source.substring(0, source.indexOf(matches[0])) + 'ui/icons.svg'; } }); } // Do not merge with previous if block: svgPathOption can be redefined in it. // Here we are checking that we find a match if (svgPathOption == null) { console.warn('You must define svgPath: https://goo.gl/CfTY9U'); // jshint ignore:line } else if (!$trumbowyg.svgAbsoluteUseHref) { var div = t.doc.createElement('div'); div.id = trumbowygIconsId; t.doc.body.insertBefore(div, t.doc.body.childNodes[0]); $.ajax({ async: true, type: 'GET', contentType: 'application/x-www-form-urlencoded; charset=UTF-8', dataType: 'xml', crossDomain: true, url: svgPathOption, data: null, beforeSend: null, complete: null, success: function (data) { div.innerHTML = new XMLSerializer().serializeToString(data.documentElement); } }); } } var baseHref = !!t.doc.querySelector('base') ? window.location.href.split(/[?#]/)[0] : ''; t.svgPath = $trumbowyg.svgAbsoluteUseHref ? svgPathOption : baseHref; /** * When the button is associated to a empty object * fn and title attributes are defined from the button key value * * For example * foo: {} * is equivalent to : * foo: { * fn: 'foo', * title: this.lang.foo * } */ var h = t.lang.header, // Header translation isBlinkFunction = function () { return (window.chrome || (window.Intl && Intl.v8BreakIterator)) && 'CSS' in window; }; t.btnsDef = { viewHTML: { fn: 'toggle', class: 'trumbowyg-not-disable', }, undo: { isSupported: isBlinkFunction, key: 'Z' }, redo: { isSupported: isBlinkFunction, key: 'Y' }, p: { fn: 'formatBlock' }, blockquote: { fn: 'formatBlock' }, h1: { fn: 'formatBlock', title: h + ' 1' }, h2: { fn: 'formatBlock', title: h + ' 2' }, h3: { fn: 'formatBlock', title: h + ' 3' }, h4: { fn: 'formatBlock', title: h + ' 4' }, h5: { fn: 'formatBlock', title: h + ' 5' }, h6: { fn: 'formatBlock', title: h + ' 6' }, subscript: { tag: 'sub' }, superscript: { tag: 'sup' }, insertFirstName: {}, insertLastName: {}, insertCustom: { dropdown: ['insertFirstName', 'insertLastName', 'insertCustom'], ico: 'custom' }, bold: { key: 'B', tag: 'b' }, italic: { key: 'I', tag: 'i' }, underline: { tag: 'u' }, strikethrough: { tag: 'strike' }, strong: { fn: 'bold', key: 'B' }, em: { fn: 'italic', key: 'I' }, del: { fn: 'strikethrough' }, createLink: { key: 'K', tag: 'a' }, unlink: {}, insertImage: {}, justifyLeft: { tag: 'left', forceCss: true }, justifyCenter: { tag: 'center', forceCss: true }, justifyRight: { tag: 'right', forceCss: true }, justifyFull: { tag: 'justify', forceCss: true }, unorderedList: { fn: 'insertUnorderedList', tag: 'ul' }, orderedList: { fn: 'insertOrderedList', tag: 'ol' }, horizontalRule: { fn: 'insertHorizontalRule' }, removeformat: {}, fullscreen: { class: 'trumbowyg-not-disable' }, close: { fn: 'destroy', class: 'trumbowyg-not-disable' }, // Dropdowns formatting: { dropdown: ['p', 'blockquote', 'h1', 'h2', 'h3', 'h4'], ico: 'p' }, link: { dropdown: ['createLink', 'unlink'] } }; // Default Options t.o = $.extend(true, {}, $trumbowyg.defaultOptions, options); if (!t.o.hasOwnProperty('imgDblClickHandler')) { t.o.imgDblClickHandler = t.getDefaultImgDblClickHandler(); } t.urlPrefix = t.setupUrlPrefix(); t.disabled = t.o.disabled || (editorElem.nodeName === 'TEXTAREA' && editorElem.disabled); if (options.btns) { t.o.btns = options.btns; } else if (!t.o.semantic) { t.o.btns[3] = ['bold', 'italic', 'underline', 'strikethrough']; } $.each(t.o.btnsDef, function (btnName, btnDef) { t.addBtnDef(btnName, btnDef); }); // put this here in the event it would be merged in with options t.eventNamespace = 'trumbowyg-event'; // Keyboard shortcuts are load in this array t.keys = []; // Tag to button dynamically hydrated t.tagToButton = {}; t.tagHandlers = []; // Admit multiple paste handlers t.pasteHandlers = [].concat(t.o.pasteHandlers); // Check if browser is IE t.isIE = navigator.userAgent.indexOf('MSIE') !== -1 || navigator.appVersion.indexOf('Trident/') !== -1; // Check if we are on macOs t.isMac = navigator.platform.toUpperCase().indexOf('MAC') !== -1; t.init(); }; Trumbowyg.prototype = { DEFAULT_SEMANTIC_MAP: { 'b': 'strong', 'i': 'em', 's': 'del', 'strike': 'del', 'div': 'p' }, init: function () { var t = this; t.height = t.$ta.height(); t.initPlugins(); try { // Disable image resize, try-catch for old IE t.doc.execCommand('enableObjectResizing', false, false); t.doc.execCommand('defaultParagraphSeparator', false, 'div'); } catch (e) { } t.buildEditor(); t.buildBtnPane(); t.fixedBtnPaneEvents(); t.buildOverlay(); setTimeout(function () { if (t.disabled) { t.setDisabled(true); } t.$c.trigger('tbwinit'); }); }, addBtnDef: function (btnName, btnDef) { this.btnsDef[btnName] = $.extend(btnDef, this.btnsDef[btnName] || {}); }, setupUrlPrefix: function () { var protocol = this.o.urlProtocol; if (!protocol) { return; } if (typeof (protocol) !== 'string') { return 'https://'; } return protocol.replace('://', '') + '://'; }, buildEditor: function () { var t = this, prefix = t.o.prefix, html = ''; t.$box = $('<div/>', { class: prefix + 'box ' + prefix + 'editor-visible ' + prefix + t.o.lang + ' trumbowyg' }); // $ta = Textarea // $ed = Editor t.isTextarea = t.$ta.is('textarea'); if (t.isTextarea) { html = t.$ta.val(); t.$ed = $('<div/>'); t.$box .insertAfter(t.$ta) .append(t.$ed, t.$ta); } else { t.$ed = t.$ta; html = t.$ed.html(); t.$ta = $('<textarea/>', { name: t.$ta.attr('id'), height: t.height }).val(html); t.$box .insertAfter(t.$ed) .append(t.$ta, t.$ed); t.syncCode(); } t.$ta .addClass(prefix + 'textarea') .attr('tabindex', -1) ; t.$ed .addClass(prefix + 'editor') .attr({ contenteditable: true, dir: t.lang._dir || 'ltr' }) .html(html) ; if (t.o.tabindex) { t.$ed.attr('tabindex', t.o.tabindex); } if (t.$c.is('[placeholder]')) { t.$ed.attr('placeholder', t.$c.attr('placeholder')); } if (t.$c.is('[spellcheck]')) { t.$ed.attr('spellcheck', t.$c.attr('spellcheck')); } if (t.o.resetCss) { t.$ed.addClass(prefix + 'reset-css'); } if (!t.o.autogrow) { t.$ta.add(t.$ed).css({ height: t.height }); } t.semanticCode(); if (t.o.autogrowOnEnter) { t.$ed.addClass(prefix + 'autogrow-on-enter'); } var ctrl = false, composition = false, debounceButtonPaneStatus; t.$ed .on('dblclick', 'img', t.o.imgDblClickHandler) .on('keydown', function (e) { // append flags to differentiate Chrome spans var keyCode = e.which; if (keyCode === 8 || keyCode === 13 || keyCode === 46) { t.toggleSpan(true); } if ((e.ctrlKey || e.metaKey) && !e.altKey) { ctrl = true; var key = t.keys[String.fromCharCode(e.which).toUpperCase()]; try { t.execCmd(key.fn, key.param); return false; } catch (c) { } } else { if (t.o.tabToIndent && e.key === 'Tab') { try { if (e.shiftKey) { t.execCmd('outdent', true, null); } else { t.execCmd('indent', true, null); } return false; } catch (c) { } } } }) .on('compositionstart compositionupdate', function () { composition = true; }) .on('keyup compositionend', function (e) { if (e.type === 'compositionend') { composition = false; } else if (composition) { return; } var keyCode = e.which; if (keyCode >= 37 && keyCode <= 40) { return; } // remove Chrome generated span tags if (keyCode === 8 || keyCode === 13 || keyCode === 46) { t.toggleSpan(); } if ((e.ctrlKey || e.metaKey) && (keyCode === 89 || keyCode === 90)) { t.semanticCode(false, true); t.$c.trigger('tbwchange'); } else if (!ctrl && keyCode !== 17) { var compositionEndIE = t.isIE ? e.type === 'compositionend' : true; t.semanticCode(false, compositionEndIE && keyCode === 13); t.$c.trigger('tbwchange'); } else if (typeof e.which === 'undefined') { t.semanticCode(false, false, true); } setTimeout(function () { ctrl = false; }, 50); }) .on('mouseup keydown keyup', function (e) { if ((!e.ctrlKey && !e.metaKey) || e.altKey) { setTimeout(function () { // "hold on" to the ctrl key for 50ms ctrl = false; }, 50); } clearTimeout(debounceButtonPaneStatus); debounceButtonPaneStatus = setTimeout(function () { t.updateButtonPaneStatus(); }, 50); }) .on('focus blur', function (e) { if (e.type === 'blur') { t.clearButtonPaneStatus(); } t.$c.trigger('tbw' + e.type); if (t.o.autogrowOnEnter) { if (t.autogrowOnEnterDontClose) { return; } if (e.type === 'focus') { t.autogrowOnEnterWasFocused = true; t.autogrowEditorOnEnter(); } else if (!t.o.autogrow) { t.$ed.css({height: t.$ed.css('min-height')}); t.$c.trigger('tbwresize'); } } }) .on('cut drop', function () { setTimeout(function () { t.semanticCode(false, true); t.$c.trigger('tbwchange'); }, 0); }) .on('paste', function (e) { if (t.o.removeformatPasted) { e.preventDefault(); if (window.getSelection && window.getSelection().deleteFromDocument) { window.getSelection().deleteFromDocument(); } try { // IE var text = window.clipboardData.getData('Text'); try { // <= IE10 t.doc.selection.createRange().pasteHTML(text); } catch (c) { // IE 11 t.doc.getSelection().getRangeAt(0).insertNode(t.doc.createTextNode(text)); } t.$c.trigger('tbwchange', e); } catch (d) { // Not IE t.execCmd('insertText', (e.originalEvent || e).clipboardData.getData('text/plain')); } } // Call pasteHandlers $.each(t.pasteHandlers, function (i, pasteHandler) { pasteHandler(e); }); setTimeout(function () { t.semanticCode(false, true); t.$c.trigger('tbwpaste', e); t.$c.trigger('tbwchange'); }, 0); }); t.$ta .on('keyup', function () { t.$c.trigger('tbwchange'); }) .on('paste', function () { setTimeout(function () { t.$c.trigger('tbwchange'); }, 0); }); $(t.doc.body).on('keydown.' + t.eventNamespace, function (e) { if (e.which === 27 && $('.' + prefix + 'modal-box').length >= 1) { t.closeModal(); return false; } }); }, //autogrow when entering logic autogrowEditorOnEnter: function () { var t = this; t.$ed.removeClass('autogrow-on-enter'); var oldHeight = t.$ed[0].clientHeight; t.$ed.height('auto'); var totalHeight = t.$ed[0].scrollHeight; t.$ed.addClass('autogrow-on-enter'); if (oldHeight !== totalHeight) { t.$ed.height(oldHeight); setTimeout(function () { t.$ed.css({height: totalHeight}); t.$c.trigger('tbwresize'); }, 0); } }, // Build button pane, use o.btns option buildBtnPane: function () { var t = this, prefix = t.o.prefix; var $btnPane = t.$btnPane = $('<div/>', { class: prefix + 'button-pane' }); $.each(t.o.btns, function (i, btnGrp) { if (!$.isArray(btnGrp)) { btnGrp = [btnGrp]; } var $btnGroup = $('<div/>', { class: prefix + 'button-group ' + ((btnGrp.indexOf('fullscreen') >= 0) ? prefix + 'right' : '') }); $.each(btnGrp, function (i, btn) { try { // Prevent buildBtn error if (t.isSupportedBtn(btn)) { // It's a supported button $btnGroup.append(t.buildBtn(btn)); } } catch (c) { } }); if ($btnGroup.html().trim().length > 0) { $btnPane.append($btnGroup); } }); t.$box.prepend($btnPane); }, cleanSpans: function(t) { const spans = t.$ed[0].getElementsByTagName('span'); const stylesCheck = [ 'fontWeight', 'fontSize', 'fontFamily', 'color', 'textColor', 'lineHeight', 'backgroundColor' ] for (const span of spans) { for (let i = 0; i < stylesCheck.length; i++) { const name = stylesCheck[i]; const attr = span.style[name]; // chrome does this when it is detecting styles from member site itself // cannot turn on resetCss as they use important which overrides styles. if (attr === 'initial') { span.style[name] = ""; } } } }, // Build a button and his action buildBtn: function (btnName) { // btnName is name of the button var t = this, prefix = t.o.prefix, btn = t.btnsDef[btnName], isDropdown = btn.dropdown, hasIcon = btn.hasIcon != null ? btn.hasIcon : true, textDef = t.lang[btnName] || btnName, $btn = $('<button/>', { type: 'button', class: prefix + btnName + '-button ' + (btn.class || '') + (!hasIcon ? ' ' + prefix + 'textual-button' : ''), html: t.hasSvg && hasIcon ? '<svg><use xlink:href="' + t.svgPath + '#' + prefix + (btn.ico || btnName).replace(/([A-Z]+)/g, '-$1').toLowerCase() + '"/></svg>' : t.hideButtonTexts ? '' : (btn.text || btn.title || t.lang[btnName] || btnName), title: (btn.title || btn.text || textDef) + (btn.key ? ' (' + (t.isMac ? 'Cmd' : 'Ctrl') + ' + ' + btn.key + ')' : ''), tabindex: 0, mousedown: function () { if (!isDropdown || $('.' + btnName + '-' + prefix + 'dropdown', t.$box).is(':hidden')) { $('body', t.doc).trigger('mousedown'); } if ((t.$btnPane.hasClass(prefix + 'disable') || t.$box.hasClass(prefix + 'disabled')) && !$(this).hasClass(prefix + 'active') && !$(this).hasClass(prefix + 'not-disable')) { return false; } t.execCmd((isDropdown ? 'dropdown' : false) || btn.fn || btnName, btn.param || btnName, btn.forceCss); t.cleanSpans(t); t.syncCode(); return false; }, keydown: function(e) { if(e.originalEvent.code === 'Enter' || e.originalEvent.code === 'Space') { e.preventDefault(); if(isDropdown) { setTimeout(function() { $(`.${prefix}dropdown-${btnName}`)[0].focus(); }, 0); } if(!isDropdown || $('.'+btnName+'-'+prefix + 'dropdown', t.$box).is(':hidden')) $('body', t.doc).trigger('mousedown'); // Making html viewer accessible if (btn.fn === 'toggle') { t.toggle(); return false; } if(t.$btnPane.hasClass(prefix + 'disable') && !$(this).hasClass(prefix + 'active') && !$(this).parent().hasClass(prefix + 'not-disable')) return false; t.execCmd((isDropdown ? 'dropdown' : false) || btn.func || btnName, btn.param || btnName); t.cleanSpans(t); t.syncCode(); return false; } } }); if (isDropdown) { $btn.addClass(prefix + 'open-dropdown'); $btn.attr('aria-controls', `${btnName}-${prefix}dropdown`); var dropdownPrefix = prefix + 'dropdown', dropdownOptions = { // the dropdown class: dropdownPrefix + '-' + btnName + ' ' + dropdownPrefix + ' ' + prefix + 'fixed-top ' + (btn.dropdownClass || ''), id: btnName + '-' + dropdownPrefix, tabindex: 0 }; dropdownOptions['data-' + dropdownPrefix] = btnName; var $dropdown = $('<div/>', dropdownOptions); $.each(isDropdown, function (i, def) { if (t.btnsDef[def] && t.isSupportedBtn(def)) { $dropdown.append(t.buildSubBtn(def)); } }); t.$box.append($dropdown.hide()); } else if (btn.key) { t.keys[btn.key] = { fn: btn.fn || btnName, param: btn.param || btnName }; } if (!isDropdown) { t.tagToButton[(btn.tag || btnName).toLowerCase()] = btnName; } return $btn; }, // Build a button for dropdown menu // @param n : name of the subbutton buildSubBtn: function (btnName) { var t = this, prefix = t.o.prefix, btn = t.btnsDef[btnName], hasIcon = btn.hasIcon != null ? btn.hasIcon : true; if (btn.key) { t.keys[btn.key] = { fn: btn.fn || btnName, param: btn.param || btnName }; } t.tagToButton[(btn.tag || btnName).toLowerCase()] = btnName; return $('<button/>', { type: 'button', class: prefix + btnName + '-dropdown-button ' + (btn.class || '') + (btn.ico ? ' ' + prefix + btn.ico + '-button' : ''), html: t.hasSvg