(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["CodexEditor"] = factory(); else root["CodexEditor"] = factory(); })(window, 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] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = 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; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); /******/ } /******/ }; /******/ /******/ // define __esModule on exports /******/ __webpack_require__.r = function(exports) { /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); /******/ } /******/ Object.defineProperty(exports, '__esModule', { value: true }); /******/ }; /******/ /******/ // create a fake namespace object /******/ // mode & 1: value is a module id, require it /******/ // mode & 2: merge all properties of value into the ns /******/ // mode & 4: return value when already ns object /******/ // mode & 8|1: behave like require /******/ __webpack_require__.t = function(value, mode) { /******/ if(mode & 1) value = __webpack_require__(value); /******/ if(mode & 8) return value; /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; /******/ var ns = Object.create(null); /******/ __webpack_require__.r(ns); /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); /******/ return ns; /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = "./src/codex.js"); /******/ }) /************************************************************************/ /******/ ({ /***/ "./build/sprite.svg": /*!**************************!*\ !*** ./build/sprite.svg ***! \**************************/ /*! no static exports found */ /***/ (function(module, exports) { module.exports = "\n\n\r\n \r\n\n\n\r\n \r\n\n\n\r\n \r\n\n\n\r\n \r\n\n\n\r\n \r\n \r\n \r\n \r\n \r\n\n\n\r\n \r\n\n\n\r\n \r\n\n\n\r\n \r\n\n\n\r\n \r\n\n" /***/ }), /***/ "./node_modules/css-loader/lib/css-base.js": /*!*************************************************!*\ !*** ./node_modules/css-loader/lib/css-base.js ***! \*************************************************/ /*! no static exports found */ /***/ (function(module, exports) { /* MIT License http://www.opensource.org/licenses/mit-license.php Author Tobias Koppers @sokra */ // css base code, injected by the css-loader module.exports = function(useSourceMap) { var list = []; // return the list of modules as css string list.toString = function toString() { return this.map(function (item) { var content = cssWithMappingToString(item, useSourceMap); if(item[2]) { return "@media " + item[2] + "{" + content + "}"; } else { return content; } }).join(""); }; // import a list of modules into the list list.i = function(modules, mediaQuery) { if(typeof modules === "string") modules = [[null, modules, ""]]; var alreadyImportedModules = {}; for(var i = 0; i < this.length; i++) { var id = this[i][0]; if(typeof id === "number") alreadyImportedModules[id] = true; } for(i = 0; i < modules.length; i++) { var item = modules[i]; // skip already imported module // this implementation is not 100% perfect for weird media query combinations // when a module is imported multiple times with different media queries. // I hope this will never occur (Hey this way we have smaller bundles) if(typeof item[0] !== "number" || !alreadyImportedModules[item[0]]) { if(mediaQuery && !item[2]) { item[2] = mediaQuery; } else if(mediaQuery) { item[2] = "(" + item[2] + ") and (" + mediaQuery + ")"; } list.push(item); } } }; return list; }; function cssWithMappingToString(item, useSourceMap) { var content = item[1] || ''; var cssMapping = item[3]; if (!cssMapping) { return content; } if (useSourceMap && typeof btoa === 'function') { var sourceMapping = toComment(cssMapping); var sourceURLs = cssMapping.sources.map(function (source) { return '/*# sourceURL=' + cssMapping.sourceRoot + source + ' */' }); return [content].concat(sourceURLs).concat([sourceMapping]).join('\n'); } return [content].join('\n'); } // Adapted from convert-source-map (MIT) function toComment(sourceMap) { // eslint-disable-next-line no-undef var base64 = btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))); var data = 'sourceMappingURL=data:application/json;charset=utf-8;base64,' + base64; return '/*# ' + data + ' */'; } /***/ }), /***/ "./node_modules/html-janitor/src/html-janitor.js": /*!*******************************************************!*\ !*** ./node_modules/html-janitor/src/html-janitor.js ***! \*******************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_RESULT__;(function (root, factory) { if (true) { !(__WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.call(exports, __webpack_require__, exports, module)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); } else {} }(this, function () { /** * @param {Object} config.tags Dictionary of allowed tags. * @param {boolean} config.keepNestedBlockElements Default false. */ function HTMLJanitor(config) { var tagDefinitions = config['tags']; var tags = Object.keys(tagDefinitions); var validConfigValues = tags .map(function(k) { return typeof tagDefinitions[k]; }) .every(function(type) { return type === 'object' || type === 'boolean' || type === 'function'; }); if(!validConfigValues) { throw new Error("The configuration was invalid"); } this.config = config; } // TODO: not exhaustive? var blockElementNames = ['P', 'LI', 'TD', 'TH', 'DIV', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'PRE']; function isBlockElement(node) { return blockElementNames.indexOf(node.nodeName) !== -1; } var inlineElementNames = ['A', 'B', 'STRONG', 'I', 'EM', 'SUB', 'SUP', 'U', 'STRIKE']; function isInlineElement(node) { return inlineElementNames.indexOf(node.nodeName) !== -1; } HTMLJanitor.prototype.clean = function (html) { var sandbox = document.createElement('div'); sandbox.innerHTML = html; this._sanitize(sandbox); return sandbox.innerHTML; }; HTMLJanitor.prototype._sanitize = function (parentNode) { var treeWalker = createTreeWalker(parentNode); var node = treeWalker.firstChild(); if (!node) { return; } do { // Ignore nodes that have already been sanitized if (node._sanitized) { continue; } if (node.nodeType === Node.TEXT_NODE) { // If this text node is just whitespace and the previous or next element // sibling is a block element, remove it // N.B.: This heuristic could change. Very specific to a bug with // `contenteditable` in Firefox: http://jsbin.com/EyuKase/1/edit?js,output // FIXME: make this an option? if (node.data.trim() === '' && ((node.previousElementSibling && isBlockElement(node.previousElementSibling)) || (node.nextElementSibling && isBlockElement(node.nextElementSibling)))) { parentNode.removeChild(node); this._sanitize(parentNode); break; } else { continue; } } // Remove all comments if (node.nodeType === Node.COMMENT_NODE) { parentNode.removeChild(node); this._sanitize(parentNode); break; } var isInline = isInlineElement(node); var containsBlockElement; if (isInline) { containsBlockElement = Array.prototype.some.call(node.childNodes, isBlockElement); } // Block elements should not be nested (e.g.
  • ...); if // they are, we want to unwrap the inner block element. var isNotTopContainer = !! parentNode.parentNode; var isNestedBlockElement = isBlockElement(parentNode) && isBlockElement(node) && isNotTopContainer; var nodeName = node.nodeName.toLowerCase(); var allowedAttrs = getAllowedAttrs(this.config, nodeName, node); var isInvalid = isInline && containsBlockElement; // Drop tag entirely according to the whitelist *and* if the markup // is invalid. if (isInvalid || shouldRejectNode(node, allowedAttrs) || (!this.config.keepNestedBlockElements && isNestedBlockElement)) { // Do not keep the inner text of SCRIPT/STYLE elements. if (! (node.nodeName === 'SCRIPT' || node.nodeName === 'STYLE')) { while (node.childNodes.length > 0) { parentNode.insertBefore(node.childNodes[0], node); } } parentNode.removeChild(node); this._sanitize(parentNode); break; } // Sanitize attributes for (var a = 0; a < node.attributes.length; a += 1) { var attr = node.attributes[a]; if (shouldRejectAttr(attr, allowedAttrs, node)) { node.removeAttribute(attr.name); // Shift the array to continue looping. a = a - 1; } } // Sanitize children this._sanitize(node); // Mark node as sanitized so it's ignored in future runs node._sanitized = true; } while ((node = treeWalker.nextSibling())); }; function createTreeWalker(node) { return document.createTreeWalker(node, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT, null, false); } function getAllowedAttrs(config, nodeName, node){ if (typeof config.tags[nodeName] === 'function') { return config.tags[nodeName](node); } else { return config.tags[nodeName]; } } function shouldRejectNode(node, allowedAttrs){ if (typeof allowedAttrs === 'undefined') { return true; } else if (typeof allowedAttrs === 'boolean') { return !allowedAttrs; } return false; } function shouldRejectAttr(attr, allowedAttrs, node){ var attrName = attr.name.toLowerCase(); if (allowedAttrs === true){ return false; } else if (typeof allowedAttrs[attrName] === 'function'){ return !allowedAttrs[attrName](attr.value, node); } else if (typeof allowedAttrs[attrName] === 'undefined'){ return true; } else if (allowedAttrs[attrName] === false) { return true; } else if (typeof allowedAttrs[attrName] === 'string') { return (allowedAttrs[attrName] !== attr.value); } return false; } return HTMLJanitor; })); /***/ }), /***/ "./src/codex.js": /*!**********************!*\ !*** ./src/codex.js ***! \**********************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(_) {/** * Codex Editor * * Short Description (눈_눈;) * @version 2.0.0 * * How to start? * Example: * new CodexEditor({ * holderId : 'codex-editor', * initialBlock : 'text', * placeholder : 'Write your story....', * tools: { * quote: Quote, * anotherTool : AnotherTool * }, * toolsConfig: { * quote: { * iconClassname : 'quote-icon', * displayInToolbox : true, * enableLineBreaks : true * }, * anotherTool: { * iconClassname : 'tool-icon' * } * } * }); * * - tools is an object: { * pluginName: PluginClass, * ..... * } * - toolsConfig is an additional configuration that uses Codex Editor API * iconClassname - CSS classname of toolbox icon * displayInToolbox - if you want to see your Tool in toolbox hided in "plus" button, than set "True". By default : "False" * enableLineBreaks - by default enter creates new block that set as initialblock, but if you set this property "True", enter will break the lines in current block * * @author CodeX-Team * */ /** * @typedef {CodexEditor} CodexEditor - editor class */ /** * @typedef {Object} EditorConfig * @property {String} holderId - Element to append Editor * @property {Array} data - Blocks list in JSON-format * @property {Object} tools - Map for used Tools in format { name : Class, ... } * @property {String} initialBlock - This Tool will be added by default * @property {String} placeholder - First Block placeholder * @property {Object} sanitizer - @todo fill desc * @property {Boolean} hideToolbar - @todo fill desc * @property {Object} toolsConfig - tools configuration {@link tools#ToolConfig} */ /** * Dynamically imported utils * * @typedef {Dom} $ - {@link components/dom.js} * @typedef {Util} _ - {@link components/utils.js} */ /** * Apply polyfills */ 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; }; }(); __webpack_require__(/*! components/polyfills */ "./src/components/polyfills.js"); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /** * Require Editor modules places in components/modules dir */ // eslint-disable-next-line var modules = ["api-blocks.ts","api-events.ts","api-listener.ts","api-sanitizer.ts","api-saver.ts","api-selection.ts","api-toolbar.ts","api.ts","block-events.ts","blockManager.js","caret.js","events.js","listeners.js","renderer.js","sanitizer.js","saver.js","toolbar-blockSettings.js","toolbar-inline.ts","toolbar-toolbox.js","toolbar.js","tools.js","ui.js"].map(function (module) { return __webpack_require__("./src/components/modules sync recursive ^\\.\\/.*$")("./" + module); }); /** * @class * * @classdesc CodeX Editor base class * * @property this.config - all settings * @property this.moduleInstances - constructed editor components * * @type {CodexEditor} */ var CodexEditor = function () { _createClass(CodexEditor, null, [{ key: 'version', /** Editor version */ get: function get() { return "2.0.0"; } /** * @param {EditorConfig} config - user configuration * */ }]); function CodexEditor(config) { var _this = this; _classCallCheck(this, CodexEditor); /** * Configuration object * @type {EditorConfig} */ this.config = {}; /** * @typedef {Object} EditorComponents * @property {BlockManager} BlockManager * @property {Tools} Tools * @property {Events} Events * @property {UI} UI * @property {Toolbar} Toolbar * @property {Toolbox} Toolbox * @property {BlockSettings} BlockSettings * @property {Renderer} Renderer * @property {InlineToolbar} InlineToolbar */ this.moduleInstances = {}; Promise.resolve().then(function () { _this.configuration = config; }).then(function () { return _this.init(); }).then(function () { return _this.start(); }).then(function () { var methods = _this.moduleInstances.API.methods; /** * Make API methods available from inside easier */ for (var method in methods) { _this[method] = methods[method]; } // todo Is it necessary? delete _this.moduleInstances; }).then(function () { console.log('CodeX Editor is ready!'); }).catch(function (error) { console.log('CodeX Editor does not ready because of %o', error); }); } /** * Setting for configuration * @param {EditorConfig} config */ _createClass(CodexEditor, [{ key: 'init', /** * Initializes modules: * - make and save instances * - configure */ value: function init() { /** * Make modules instances and save it to the @property this.moduleInstances */ this.constructModules(); /** * Modules configuration */ this.configureModules(); } /** * Make modules instances and save it to the @property this.moduleInstances */ }, { key: 'constructModules', value: function constructModules() { var _this2 = this; modules.forEach(function (Module) { try { /** * We use class name provided by displayName property * * On build, Babel will transform all Classes to the Functions so, name will always be 'Function' * To prevent this, we use 'babel-plugin-class-display-name' plugin * @see https://www.npmjs.com/package/babel-plugin-class-display-name */ _this2.moduleInstances[Module.displayName] = new Module({ config: _this2.configuration }); } catch (e) { console.log('Module %o skipped because %o', Module, e); } }); } /** * Modules instances configuration: * - pass other modules to the 'state' property * - ... */ }, { key: 'configureModules', value: function configureModules() { for (var name in this.moduleInstances) { /** * Module does not need self-instance */ this.moduleInstances[name].state = this.getModulesDiff(name); } } /** * Return modules without passed name */ }, { key: 'getModulesDiff', value: function getModulesDiff(name) { var diff = {}; for (var moduleName in this.moduleInstances) { /** * Skip module with passed name */ if (moduleName === name) { continue; } diff[moduleName] = this.moduleInstances[moduleName]; } return diff; } /** * Start Editor! * * Get list of modules that needs to be prepared and return a sequence (Promise) * @return {Promise} */ }, { key: 'start', value: function start() { var _this3 = this; var prepareDecorator = function prepareDecorator(module) { return module.prepare(); }; return Promise.resolve().then(prepareDecorator(this.moduleInstances.Tools)).then(prepareDecorator(this.moduleInstances.UI)).then(prepareDecorator(this.moduleInstances.BlockManager)).then(function () { return _this3.moduleInstances.Renderer.render(_this3.config.data.items); }); } }, { key: 'configuration', set: function set(config) { /** * Initlai block type * Uses in case when there is no items passed * @type {{type: (*), data: {text: null}}} */ var initialBlock = { type: config.initialBlock, data: {} }; this.config.holderId = config.holderId; this.config.placeholder = config.placeholder || 'write your story...'; this.config.sanitizer = config.sanitizer || { p: true, b: true, a: true }; this.config.hideToolbar = config.hideToolbar ? config.hideToolbar : false; this.config.tools = config.tools || {}; this.config.toolsConfig = config.toolsConfig || {}; this.config.data = config.data || {}; /** * Initialize items to pass data to the Renderer */ if (_.isEmpty(this.config.data)) { this.config.data = {}; this.config.data.items = [initialBlock]; } else { if (!this.config.data.items || this.config.data.items.length === 0) { this.config.data.items = [initialBlock]; } } /** * If initial Block's Tool was not passed, use the first Tool in config.tools */ if (!config.initialBlock) { for (this.config.initialBlock in this.config.tools) { break; } } else { this.config.initialBlock = config.initialBlock; } } /** * Returns private property * @returns {EditorConfig} */ , get: function get() { return this.config; } }]); return CodexEditor; }(); CodexEditor.displayName = 'CodexEditor'; exports.default = CodexEditor; ; // module.exports = (function (editor) { // // 'use strict'; // // editor.version = VERSION; // editor.scriptPrefix = 'cdx-script-'; // // var init = function () { // // editor.core = require('./modules/core'); // editor.tools = require('./modules/tools'); // editor.ui = require('./modules/ui'); // editor.transport = require('./modules/transport'); // editor.renderer = require('./modules/renderer'); // editor.saver = require('./modules/saver'); // editor.content = require('./modules/content'); // editor.toolbar = require('./modules/toolbar/toolbar'); // editor.callback = require('./modules/callbacks'); // editor.draw = require('./modules/draw'); // editor.caret = require('./modules/caret'); // editor.notifications = require('./modules/notifications'); // editor.parser = require('./modules/parser'); // editor.sanitizer = require('./modules/sanitizer'); // editor.listeners = require('./modules/listeners'); // editor.destroyer = require('./modules/destroyer'); // editor.paste = require('./modules/paste'); // // }; // // /** // * @public // * holds initial settings // */ // editor.settings = { // tools : ['text', 'header', 'picture', 'list', 'quote', 'code', 'twitter', 'instagram', 'smile'], // holderId : 'codex-editor', // // // Type of block showing on empty editor // initialBlockPlugin: 'text' // }; // // /** // * public // * // * Static nodes // */ // editor.nodes = { // holder : null, // wrapper : null, // toolbar : null, // inlineToolbar : { // wrapper : null, // buttons : null, // actions : null // }, // toolbox : null, // notifications : null, // plusButton : null, // showSettingsButton: null, // showTrashButton : null, // blockSettings : null, // pluginSettings : null, // defaultSettings : null, // toolbarButtons : {}, // { type : DomEl, ... } // redactor : null // }; // // /** // * @public // * // * Output state // */ // editor.state = { // jsonOutput : [], // blocks : [], // inputs : [] // }; // // /** // * @public // * Editor plugins // */ // editor.tools = {}; // // editor.start = function (userSettings) { // // init(); // // editor.core.prepare(userSettings) // // // If all ok, make UI, bind events and parse initial-content // .then(editor.ui.prepare) // .then(editor.tools.prepare) // .then(editor.sanitizer.prepare) // .then(editor.paste.prepare) // .then(editor.transport.prepare) // .then(editor.renderer.makeBlocksFromData) // .then(editor.ui.saveInputs) // .catch(function (error) { // // editor.core.log('Initialization failed with error: %o', 'warn', error); // // }); // // }; // // return editor; // // })({}); module.exports = exports['default']; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! utils */ "./src/components/utils.js"))) /***/ }), /***/ "./src/components/__module.ts": /*!************************************!*\ !*** ./src/components/__module.ts ***! \************************************/ /*! no static exports found */ /***/ (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; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /** * @abstract * @class Module * @classdesc All modules inherits from this class. * * @typedef {Module} Module * @property {Object} config - Editor user settings * @property {IEditorConfig} Editor - List of Editor modules */ var Module = function () { /** * @constructor * @param {IModuleConfig} */ function Module(_ref) { var config = _ref.config; _classCallCheck(this, Module); if (new.target === Module) { throw new TypeError('Constructors for abstract class Module are not allowed.'); } this.config = config; } /** * Editor modules setter * @param {IEditor} Editor */ _createClass(Module, [{ key: 'state', set: function set(Editor) { this.Editor = Editor; } }]); return Module; }(); Module.displayName = 'Module'; exports.default = Module; module.exports = exports['default']; /***/ }), /***/ "./src/components/block-tunes/block-tune-delete.ts": /*!*********************************************************!*\ !*** ./src/components/block-tunes/block-tune-delete.ts ***! \*********************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function($) { 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; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var DeleteTune = function () { /** * DeleteTune constructor * * @param {Object} api */ function DeleteTune(_ref) { var _this = this; var api = _ref.api; _classCallCheck(this, DeleteTune); /** * Styles * @type {{wrapper: string}} */ this.CSS = { wrapper: 'ass', button: 'ce-settings__button', buttonDelete: 'ce-settings__button--delete', buttonConfirm: 'ce-settings__button--confirm' }; /** * Tune nodes */ this.nodes = { button: null }; this.api = api; this.resetConfirmation = function () { _this.setConfirmation(false); }; } /** * Create "Delete" button and add click event listener * @returns [Element} */ _createClass(DeleteTune, [{ key: 'render', value: function render() { var _this2 = this; this.nodes.button = $.make('div', [this.CSS.button, this.CSS.buttonDelete], {}); this.nodes.button.appendChild($.svg('cross', 12, 12)); this.api.listener.on(this.nodes.button, 'click', function (event) { return _this2.handleClick(event); }, false); return this.nodes.button; } /** * Delete block conditions passed * @param {MouseEvent} event */ }, { key: 'handleClick', value: function handleClick(event) { /** * if block is not waiting the confirmation, subscribe on block-settings-closing event to reset * otherwise delete block */ if (!this.needConfirmation) { this.setConfirmation(true); /** * Subscribe on event. * When toolbar block settings is closed but block deletion is not confirmed, * then reset confirmation state */ this.api.events.on('block-settings-closed', this.resetConfirmation); } else { /** * Unsubscribe from block-settings closing event */ this.api.events.off('block-settings-closed', this.resetConfirmation); this.api.blocks.delete(); } } /** * change tune state */ }, { key: 'setConfirmation', value: function setConfirmation(state) { this.needConfirmation = state; this.nodes.button.classList.add(this.CSS.buttonConfirm); } }]); return DeleteTune; }(); DeleteTune.displayName = 'DeleteTune'; exports.default = DeleteTune; module.exports = exports['default']; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! dom */ "./src/components/dom.js"))) /***/ }), /***/ "./src/components/block-tunes/block-tune-move-down.ts": /*!************************************************************!*\ !*** ./src/components/block-tunes/block-tune-move-down.ts ***! \************************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function($) { 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; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var MoveDownTune = function () { /** * MoveDownTune constructor * * @param {Object} api */ function MoveDownTune(_ref) { var api = _ref.api; _classCallCheck(this, MoveDownTune); /** * Styles * @type {{wrapper: string}} */ this.CSS = { button: 'ce-settings__button', wrapper: 'ce-tune-move-down', animation: 'wobble' }; this.api = api; } /** * Return 'move down' button */ _createClass(MoveDownTune, [{ key: 'render', value: function render() { var _this = this; var moveDownButton = $.make('div', [this.CSS.button, this.CSS.wrapper], {}); moveDownButton.appendChild($.svg('arrow-down', 14, 14)); this.api.listener.on(moveDownButton, 'click', function (event) { return _this.handleClick(event, moveDownButton); }, false); return moveDownButton; } /** * Handle clicks on 'move down' button * @param {MouseEvent} event * @param {HTMLElement} button */ }, { key: 'handleClick', value: function handleClick(event, button) { var _this2 = this; var currentBlockIndex = this.api.blocks.getCurrentBlockIndex(); // If Block is last do nothing if (currentBlockIndex === this.api.blocks.getBlocksCount() - 1) { button.classList.add(this.CSS.animation); window.setTimeout(function () { button.classList.remove(_this2.CSS.animation); }, 500); return; } var nextBlockElement = this.api.blocks.getBlockByIndex(currentBlockIndex + 1).holder, nextBlockCoords = nextBlockElement.getBoundingClientRect(); var scrollOffset = Math.abs(window.innerHeight - nextBlockElement.offsetHeight); /** * Next block ends on screen. * Increment scroll by next block's height to save element onscreen-position */ if (nextBlockCoords.top < window.innerHeight) { scrollOffset = window.scrollY + nextBlockElement.offsetHeight; } window.scrollTo(0, scrollOffset); /** Change blocks positions */ this.api.blocks.swap(currentBlockIndex, currentBlockIndex + 1); } }]); return MoveDownTune; }(); MoveDownTune.displayName = 'MoveDownTune'; exports.default = MoveDownTune; module.exports = exports['default']; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! dom */ "./src/components/dom.js"))) /***/ }), /***/ "./src/components/block-tunes/block-tune-move-up.ts": /*!**********************************************************!*\ !*** ./src/components/block-tunes/block-tune-move-up.ts ***! \**********************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function($) { 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; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var MoveUpTune = function () { /** * MoveUpTune constructor * * @param {Object} api */ function MoveUpTune(_ref) { var api = _ref.api; _classCallCheck(this, MoveUpTune); /** * Styles * @type {{wrapper: string}} */ this.CSS = { button: 'ce-settings__button', wrapper: 'ce-tune-move-up', animation: 'wobble' }; this.api = api; } /** * Create "MoveUp" button and add click event listener * @returns [Element} */ _createClass(MoveUpTune, [{ key: 'render', value: function render() { var _this = this; var moveUpButton = $.make('div', [this.CSS.button, this.CSS.wrapper], {}); moveUpButton.appendChild($.svg('arrow-up', 14, 14)); this.api.listener.on(moveUpButton, 'click', function (event) { return _this.handleClick(event, moveUpButton); }, false); return moveUpButton; } /** * Move current block up * @param {MouseEvent} event * @param {HTMLElement} button */ }, { key: 'handleClick', value: function handleClick(event, button) { var _this2 = this; var currentBlockIndex = this.api.blocks.getCurrentBlockIndex(); if (currentBlockIndex === 0) { button.classList.add(this.CSS.animation); window.setTimeout(function () { button.classList.remove(_this2.CSS.animation); }, 500); return; } var currentBlockElement = this.api.blocks.getBlockByIndex(currentBlockIndex).holder, previousBlockElement = this.api.blocks.getBlockByIndex(currentBlockIndex - 1).holder; /** * Here is two cases: * - when previous block has negative offset and part of it is visible on window, then we scroll * by window's height and add offset which is mathematically difference between two blocks * * - when previous block is visible and has offset from the window, * than we scroll window to the difference between this offsets. */ var currentBlockCoords = currentBlockElement.getBoundingClientRect(), previousBlockCoords = previousBlockElement.getBoundingClientRect(); var scrollUpOffset = void 0; if (previousBlockCoords.top > 0) { scrollUpOffset = Math.abs(currentBlockCoords.top) - Math.abs(previousBlockCoords.top); } else { scrollUpOffset = window.innerHeight - Math.abs(currentBlockCoords.top) + Math.abs(previousBlockCoords.top); } window.scrollBy(0, -1 * scrollUpOffset); /** Change blocks positions */ this.api.blocks.swap(currentBlockIndex, currentBlockIndex - 1); } }]); return MoveUpTune; }(); MoveUpTune.displayName = 'MoveUpTune'; exports.default = MoveUpTune; module.exports = exports['default']; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! dom */ "./src/components/dom.js"))) /***/ }), /***/ "./src/components/block.js": /*!*********************************!*\ !*** ./src/components/block.js ***! \*********************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function($, _) { 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; }; }(); /** * @class Block * @classdesc This class describes editor`s block, including block`s HTMLElement, data and tool * * @property {Tool} tool — current block tool (Paragraph, for example) * @property {Object} CSS — block`s css classes * */ /** Import default tunes */ var _blockTuneMoveUp = __webpack_require__(/*! ./block-tunes/block-tune-move-up */ "./src/components/block-tunes/block-tune-move-up.ts"); var _blockTuneMoveUp2 = _interopRequireDefault(_blockTuneMoveUp); var _blockTuneDelete = __webpack_require__(/*! ./block-tunes/block-tune-delete */ "./src/components/block-tunes/block-tune-delete.ts"); var _blockTuneDelete2 = _interopRequireDefault(_blockTuneDelete); var _blockTuneMoveDown = __webpack_require__(/*! ./block-tunes/block-tune-move-down */ "./src/components/block-tunes/block-tune-move-down.ts"); var _blockTuneMoveDown2 = _interopRequireDefault(_blockTuneMoveDown); 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"); } } /** * @classdesc Abstract Block class that contains Block information, Tool name and Tool class instance * * @property tool - Tool instance * @property html - Returns HTML content of plugin * @property holder - Div element that wraps block content with Tool's content. Has `ce-block` CSS class * @property pluginsContent - HTML content that returns by Tool's render function */ var Block = function () { /** * @constructor * @param {String} toolName - Tool name that passed on initialization * @param {Object} toolInstance — passed Tool`s instance that rendered the Block * @param {Object} settings - default settings * @param {Object} apiMethods - Editor API */ function Block(toolName, toolInstance, settings, apiMethods) { _classCallCheck(this, Block); this.name = toolName; this.tool = toolInstance; this.settings = settings; this.api = apiMethods; this.holder = this.compose(); this.inputIndex = 0; /** * @type {IBlockTune[]} */ this.tunes = this.makeTunes(); } /** * CSS classes for the Block * @return {{wrapper: string, content: string}} */ _createClass(Block, [{ key: 'compose', /** * Make default Block wrappers and put Tool`s content there * @returns {HTMLDivElement} */ value: function compose() { var wrapper = $.make('div', Block.CSS.wrapper), contentNode = $.make('div', Block.CSS.content), pluginsContent = this.tool.render(); contentNode.appendChild(pluginsContent); wrapper.appendChild(contentNode); return wrapper; } /** * Calls Tool's method * * Method checks tool property {MethodName}. Fires method with passes params If it is instance of Function * * @param {String} methodName * @param {Object} params */ }, { key: 'call', value: function call(methodName, params) { /** * call Tool's method with the instance context */ if (this.tool[methodName] && this.tool[methodName] instanceof Function) { this.tool[methodName].call(this.tool, params); } } /** * Returns Plugins content * @return {Node} */ }, { key: 'mergeWith', /** * Call plugins merge method * @param {Object} data */ value: function mergeWith(data) { var _this = this; return Promise.resolve().then(function () { _this.tool.merge(data); }); } /** * Extracts data from Block * Groups Tool's save processing time * @return {Object} */ }, { key: 'save', value: function save() { var _this2 = this; var extractedBlock = this.tool.save(this.pluginsContent); /** Measuring execution time*/ var measuringStart = window.performance.now(), measuringEnd = void 0; return Promise.resolve(extractedBlock).then(function (finishedExtraction) { /** measure promise execution */ measuringEnd = window.performance.now(); return { tool: _this2.name, data: finishedExtraction, time: measuringEnd - measuringStart }; }).catch(function (error) { _.log('Saving proccess for ' + this.tool.name + ' tool failed due to the ' + error, 'log', 'red'); }); } /** * Uses Tool's validation method to check the correctness of output data * Tool's validation method is optional * * @description Method also can return data if it passed the validation * * @param {Object} data * @returns {Boolean|Object} valid */ }, { key: 'validateData', value: function validateData(data) { var isValid = true; if (this.tool.validate instanceof Function) { isValid = this.tool.validate(data); } if (!isValid) { return false; } return data; } /** * Make an array with default settings * Each block has default tune instance that have states * @return {IBlockTune[]} */ }, { key: 'makeTunes', value: function makeTunes() { var _this3 = this; var tunesList = [_blockTuneMoveUp2.default, _blockTuneDelete2.default, _blockTuneMoveDown2.default]; // Pluck tunes list and return tune instances with passed Editor API and settings return tunesList.map(function (tune) { return new tune({ api: _this3.api, settings: _this3.settings }); }); } /** * Enumerates initialized tunes and returns fragment that can be appended to the toolbars area * @return {DocumentFragment} */ }, { key: 'renderTunes', value: function renderTunes() { var tunesElement = document.createDocumentFragment(); this.tunes.forEach(function (tune) { $.append(tunesElement, tune.render()); }); return tunesElement; } /** * Check block for emptiness * @return {Boolean} */ }, { key: 'pluginsContent', get: function get() { var pluginsContent = this.holder.querySelector('.' + Block.CSS.content); if (pluginsContent && pluginsContent.childNodes.length) { return pluginsContent.childNodes[0]; } return null; } /** * Get Block's JSON data * @return {Object} */ }, { key: 'data', get: function get() { return this.save(); } }, { key: 'inputs', get: function get() { var collection = this.holder.querySelectorAll('[contenteditable], input, textarea'); return _.array(collection); } }, { key: 'nextInput', get: function get() { var inputs = this.inputs; this.inputIndex = Math.min(inputs.length - 1, this.inputIndex + 1); return inputs[this.inputIndex]; } }, { key: 'previousInput', get: function get() { this.inputIndex = Math.max(0, this.inputIndex - 1); return this.inputs[this.inputIndex]; } /** * is block mergeable * We plugin have merge function then we call it mergable * @return {boolean} */ }, { key: 'mergeable', get: function get() { return typeof this.tool.merge === 'function'; } }, { key: 'isEmpty', get: function get() { /** * Allow Tool to represent decorative contentless blocks: for example "* * *"-tool * That Tools are not empty */ if (this.tool.contentless) { return false; } var emptyText = $.isEmpty(this.pluginsContent), emptyMedia = !this.hasMedia; return emptyText && emptyMedia; } /** * Check if block has a media content such as images, iframes and other * @return {Boolean} */ }, { key: 'hasMedia', get: function get() { /** * This tags represents media-content * @type {string[]} */ var mediaTags = ['img', 'iframe', 'video', 'audio', 'source', 'input', 'textarea', 'twitterwidget']; return !!this.holder.querySelector(mediaTags.join(',')); } /** * Set selected state * @param {Boolean} state - 'true' to select, 'false' to remove selection */ }, { key: 'selected', set: function set(state) { /** * We don't need to mark Block as Selected when it is not empty */ if (state === true && !this.isEmpty) { this.holder.classList.add(Block.CSS.selected); } else { this.holder.classList.remove(Block.CSS.selected); } } }], [{ key: 'CSS', get: function get() { return { wrapper: 'ce-block', content: 'ce-block__content', selected: 'ce-block--selected' }; } }]); return Block; }(); Block.displayName = 'Block'; exports.default = Block; module.exports = exports['default']; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! dom */ "./src/components/dom.js"), __webpack_require__(/*! utils */ "./src/components/utils.js"))) /***/ }), /***/ "./src/components/dom.js": /*!*******************************!*\ !*** ./src/components/dom.js ***! \*******************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; 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; }; }(); function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /** * DOM manipulations helper */ var Dom = function () { function Dom() { _classCallCheck(this, Dom); } _createClass(Dom, null, [{ key: 'isSingleTag', /** * Check if passed tag has no closed tag * @param {Element} tag * @return {Boolean} */ value: function isSingleTag(tag) { return tag.tagName && ['AREA', 'BASE', 'BR', 'COL', 'COMMAND', 'EMBED', 'HR', 'IMG', 'INPUT', 'KEYGEN', 'LINK', 'META', 'PARAM', 'SOURCE', 'TRACK', 'WBR'].includes(tag.tagName); } }, { key: 'make', /** * Helper for making Elements with classname and attributes * * @param {string} tagName - new Element tag name * @param {array|string} classNames - list or name of CSS classname(s) * @param {Object} attributes - any attributes * @return {Element} */ value: function make(tagName) { var classNames = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var el = document.createElement(tagName); if (Array.isArray(classNames)) { var _el$classList; (_el$classList = el.classList).add.apply(_el$classList, _toConsumableArray(classNames)); } else if (classNames) { el.classList.add(classNames); } for (var attrName in attributes) { el[attrName] = attributes[attrName]; } return el; } /** * Creates Text Node with the passed content * @param {String} content - text content * @return {Text} */ }, { key: 'text', value: function text(content) { return document.createTextNode(content); } /** * Creates SVG icon linked to the sprite * @param {string} name - name (id) of icon from sprite * @param {number} width * @param {number} height * @return {SVGElement} */ }, { key: 'svg', value: function svg(name) { var width = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 14; var height = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 14; var icon = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); icon.classList.add('icon', 'icon--' + name); icon.setAttribute('width', width + 'px'); icon.setAttribute('height', height + 'px'); icon.innerHTML = ''; return icon; } /** * Append one or several elements to the parent * * @param {Element} parent - where to append * @param {Element|Element[]} - element ore elements list */ }, { key: 'append', value: function append(parent, elements) { if (Array.isArray(elements)) { elements.forEach(function (el) { return parent.appendChild(el); }); } else { parent.appendChild(elements); } } /** * Swap two elements in parent * @param {HTMLElement} el1 - from * @param {HTMLElement} el2 - to */ }, { key: 'swap', value: function swap(el1, el2) { // create marker element and insert it where el1 is var temp = document.createElement('div'), parent = el1.parentNode; parent.insertBefore(temp, el1); // move el1 to right before el2 parent.insertBefore(el1, el2); // move el2 to right before where el1 used to be parent.insertBefore(el2, temp); // remove temporary marker node parent.removeChild(temp); } /** * Selector Decorator * * Returns first match * * @param {Element} el - element we searching inside. Default - DOM Document * @param {String} selector - searching string * * @returns {Element} */ }, { key: 'find', value: function find() { var el = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : document; var selector = arguments[1]; return el.querySelector(selector); } /** * Selector Decorator. * * Returns all matches * * @param {Element} el - element we searching inside. Default - DOM Document * @param {String} selector - searching string * @returns {NodeList} */ }, { key: 'findAll', value: function findAll() { var el = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : document; var selector = arguments[1]; return el.querySelectorAll(selector); } /** * Search for deepest node which is Leaf. * Leaf is the vertex that doesn't have any child nodes * * @description Method recursively goes throw the all Node until it finds the Leaf * * @param {Node} node - root Node. From this vertex we start Deep-first search {@link https://en.wikipedia.org/wiki/Depth-first_search} * @param {Boolean} atLast - find last text node * @return {Node} - it can be text Node or Element Node, so that caret will able to work with it */ }, { key: 'getDeepestNode', value: function getDeepestNode(node) { var atLast = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; /** * Current function have two directions: * - starts from first child and every time gets first or nextSibling in special cases * - starts from last child and gets last or previousSibling * @type {string} */ var child = atLast ? 'lastChild' : 'firstChild', sibling = atLast ? 'previousSibling' : 'nextSibling'; if (node && node.nodeType === Node.ELEMENT_NODE && node[child]) { var nodeChild = node[child]; /** * special case when child is single tag that can't contain any content */ if (Dom.isSingleTag(nodeChild)) { /** * 1) We need to check the next sibling. If it is Node Element then continue searching for deepest * from sibling * * 2) If single tag's next sibling is null, then go back to parent and check his sibling * In case of Node Element continue searching * * 3) If none of conditions above happened return parent Node Element */ if (nodeChild[sibling]) { nodeChild = nodeChild[sibling]; } else if (nodeChild.parentNode[sibling]) { nodeChild = nodeChild.parentNode[sibling]; } else { return nodeChild.parentNode; } } return this.getDeepestNode(nodeChild, atLast); } return node; } /** * Check if object is DOM node * * @param {Object} node * @returns {boolean} */ }, { key: 'isElement', value: function isElement(node) { return node && (typeof node === 'undefined' ? 'undefined' : _typeof(node)) === 'object' && node.nodeType && node.nodeType === Node.ELEMENT_NODE; } /** * Checks target if it is native input * @param {Element|String|Node} target - HTML element or string * @return {Boolean} */ }, { key: 'isNativeInput', value: function isNativeInput(target) { var nativeInputs = ['INPUT', 'TEXTAREA']; return target ? nativeInputs.includes(target.tagName) : false; } /** * Checks node if it is empty * * @description Method checks simple Node without any childs for emptiness * If you have Node with 2 or more children id depth, you better use {@link Dom#isEmpty} method * * @param {Node} node * @return {Boolean} true if it is empty */ }, { key: 'isNodeEmpty', value: function isNodeEmpty(node) { var nodeText = void 0; if (this.isElement(node) && this.isNativeInput(node)) { nodeText = node.value; } else { nodeText = node.textContent.replace('\u200B', ''); } return nodeText.trim().length === 0; } /** * checks node if it is doesn't have any child nodes * @param {Node} node * @return {boolean} */ }, { key: 'isLeaf', value: function isLeaf(node) { if (!node) { return false; } return node.childNodes.length === 0; } /** * breadth-first search (BFS) * {@link https://en.wikipedia.org/wiki/Breadth-first_search} * * @description Pushes to stack all DOM leafs and checks for emptiness * * @param {Node} node * @return {boolean} */ }, { key: 'isEmpty', value: function isEmpty(node) { var _this = this; var treeWalker = [], leafs = []; if (!node) { return true; } if (!node.childNodes.length) { return this.isNodeEmpty(node); } treeWalker.push(node.firstChild); while (treeWalker.length > 0) { node = treeWalker.shift(); if (!node) continue; if (this.isLeaf(node)) { leafs.push(node); } else { treeWalker.push(node.firstChild); } while (node && node.nextSibling) { node = node.nextSibling; if (!node) continue; treeWalker.push(node); } /** * If one of childs is not empty, checked Node is not empty too */ if (node && !this.isNodeEmpty(node)) { return false; } } return leafs.every(function (leaf) { return _this.isNodeEmpty(leaf); }); } }]); return Dom; }(); Dom.displayName = 'Dom'; exports.default = Dom; ; module.exports = exports['default']; /***/ }), /***/ "./src/components/inline-tools/inline-tool-bold.ts": /*!*********************************************************!*\ !*** ./src/components/inline-tools/inline-tool-bold.ts ***! \*********************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function($) { 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; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /** * Bold Tool * * Inline Toolbar Tool * * Makes selected text bolder */ var BoldInlineTool = function () { function BoldInlineTool(api) { _classCallCheck(this, BoldInlineTool); /** * Native Document's command that uses for Bold */ this.commandName = 'bold'; /** * Styles */ this.CSS = { button: 'ce-inline-tool', buttonActive: 'ce-inline-tool--active', buttonModifier: 'ce-inline-tool--bold' }; /** * Elements */ this.nodes = { button: null }; console.log('Bold Inline Tool is ready'); } /** * Create button for Inline Toolbar */ _createClass(BoldInlineTool, [{ key: 'render', value: function render() { this.nodes.button = document.createElement('button'); this.nodes.button.classList.add(this.CSS.button, this.CSS.buttonModifier); this.nodes.button.appendChild($.svg('bold', 13, 15)); return this.nodes.button; } /** * Wrap range with tag * @param {Range} range */ }, { key: 'surround', value: function surround(range) { document.execCommand(this.commandName); } /** * Check selection and set activated state to button if there are tag * @param {Selection} selection */ }, { key: 'checkState', value: function checkState(selection) { var isActive = document.queryCommandState(this.commandName); this.nodes.button.classList.toggle(this.CSS.buttonActive, isActive); return isActive; } }]); return BoldInlineTool; }(); BoldInlineTool.displayName = 'BoldInlineTool'; exports.default = BoldInlineTool; module.exports = exports['default']; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! dom */ "./src/components/dom.js"))) /***/ }), /***/ "./src/components/inline-tools/inline-tool-italic.ts": /*!***********************************************************!*\ !*** ./src/components/inline-tools/inline-tool-italic.ts ***! \***********************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function($) { 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; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /** * Italic Tool * * Inline Toolbar Tool * * Style selected text with italic */ var ItalicInlineTool = function () { function ItalicInlineTool(api) { _classCallCheck(this, ItalicInlineTool); /** * Native Document's command that uses for Italic */ this.commandName = 'italic'; /** * Styles */ this.CSS = { button: 'ce-inline-tool', buttonActive: 'ce-inline-tool--active', buttonModifier: 'ce-inline-tool--italic' }; /** * Elements */ this.nodes = { button: null }; console.log('Italic Inline Tool is ready'); } /** * Create button for Inline Toolbar */ _createClass(ItalicInlineTool, [{ key: 'render', value: function render() { this.nodes.button = document.createElement('button'); this.nodes.button.classList.add(this.CSS.button, this.CSS.buttonModifier); this.nodes.button.appendChild($.svg('italic', 6, 15)); return this.nodes.button; } /** * Wrap range with tag * @param {Range} range */ }, { key: 'surround', value: function surround(range) { document.execCommand(this.commandName); } /** * Check selection and set activated state to button if there are tag * @param {Selection} selection */ }, { key: 'checkState', value: function checkState(selection) { var isActive = document.queryCommandState(this.commandName); this.nodes.button.classList.toggle(this.CSS.buttonActive, isActive); return isActive; } }]); return ItalicInlineTool; }(); ItalicInlineTool.displayName = 'ItalicInlineTool'; exports.default = ItalicInlineTool; module.exports = exports['default']; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! dom */ "./src/components/dom.js"))) /***/ }), /***/ "./src/components/inline-tools/inline-tool-link.ts": /*!*********************************************************!*\ !*** ./src/components/inline-tools/inline-tool-link.ts ***! \*********************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function($, _) { 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 _selection = __webpack_require__(/*! ../selection */ "./src/components/selection.js"); var _selection2 = _interopRequireDefault(_selection); 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"); } } /** * Link Tool * * Inline Toolbar Tool * * Wrap selected text with tag */ var LinkInlineTool = function () { /** * @param {object} api - CodeX Editor API * @param {object} api.toolbar - Inline Toolbar API */ function LinkInlineTool(api) { _classCallCheck(this, LinkInlineTool); /** * Native Document's commands for link/unlink */ this.commandLink = 'createLink'; this.commandUnlink = 'unlink'; /** * Enter key code */ this.ENTER_KEY = 13; /** * Styles */ this.CSS = { button: 'ce-inline-tool', buttonActive: 'ce-inline-tool--active', buttonModifier: 'ce-inline-tool--link', buttonUnlink: 'ce-inline-tool--unlink', input: 'ce-inline-tool-input', inputShowed: 'ce-inline-tool-input--showed' }; /** * Elements */ this.nodes = { button: null, input: null }; /** * Input opening state */ this.inputOpened = false; this.inlineToolbar = api.toolbar; this.selection = new _selection2.default(); } /** * Create button for Inline Toolbar */ _createClass(LinkInlineTool, [{ key: 'render', value: function render() { this.nodes.button = document.createElement('button'); this.nodes.button.classList.add(this.CSS.button, this.CSS.buttonModifier); this.nodes.button.appendChild($.svg('link', 15, 14)); this.nodes.button.appendChild($.svg('unlink', 16, 18)); return this.nodes.button; } /** * Input for the link */ }, { key: 'renderActions', value: function renderActions() { var _this = this; this.nodes.input = document.createElement('input'); this.nodes.input.placeholder = 'Add a link'; this.nodes.input.classList.add(this.CSS.input); this.nodes.input.addEventListener('keydown', function (event) { if (event.keyCode === _this.ENTER_KEY) { _this.enterPressed(event); } }); return this.nodes.input; } /** * Handle clicks on the Inline Toolbar icon * @param {Range} range */ }, { key: 'surround', value: function surround(range) { /** * Range will be null when user makes second click on the 'link icon' to close opened input */ if (range) { /** * Save selection before change focus to the input */ this.selection.save(); var parentAnchor = this.selection.findParentTag('A'); /** * Unlink icon pressed */ if (parentAnchor) { this.selection.expandToTag(parentAnchor); this.unlink(); this.closeActions(); this.checkState(); this.inlineToolbar.close(); return; } } this.toggleActions(); } /** * Check selection and set activated state to button if there are tag * @param {Selection} selection */ }, { key: 'checkState', value: function checkState(selection) { var anchorTag = this.selection.findParentTag('A'); if (anchorTag) { this.nodes.button.classList.add(this.CSS.buttonUnlink); this.nodes.button.classList.add(this.CSS.buttonActive); this.openActions(); /** * Fill input value with link href */ var hrefAttr = anchorTag.getAttribute('href'); this.nodes.input.value = hrefAttr !== 'null' ? hrefAttr : ''; this.selection.save(); } else { this.nodes.button.classList.remove(this.CSS.buttonUnlink); this.nodes.button.classList.remove(this.CSS.buttonActive); } return !!anchorTag; } /** * Function called with Inline Toolbar closing */ }, { key: 'clear', value: function clear() { this.closeActions(); } }, { key: 'toggleActions', value: function toggleActions() { if (!this.inputOpened) { this.openActions(true); } else { this.closeActions(false); } } /** * @param {boolean} needFocus - on link creation we need to focus input. On editing - nope. */ }, { key: 'openActions', value: function openActions() { var needFocus = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; this.nodes.input.classList.add(this.CSS.inputShowed); if (needFocus) { this.nodes.input.focus(); } this.inputOpened = true; } /** * Close input * @param {boolean} clearSavedSelection — we don't need to clear saved selection * on toggle-clicks on the icon of opened Toolbar */ }, { key: 'closeActions', value: function closeActions() { var clearSavedSelection = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; this.nodes.input.classList.remove(this.CSS.inputShowed); this.nodes.input.value = ''; if (clearSavedSelection) { this.selection.clearSaved(); } this.inputOpened = false; } /** * Enter pressed on input * @param {KeyboardEvent} event */ }, { key: 'enterPressed', value: function enterPressed(event) { var value = this.nodes.input.value || ''; if (!value.trim()) { this.selection.restore(); this.unlink(); event.preventDefault(); this.closeActions(); } if (!this.validateURL(value)) { /** * @todo show notification 'Incorrect Link' */ _.log('Incorrect Link pasted', 'warn', value); return; } value = this.prepareLink(value); this.selection.restore(); this.insertLink(value); /** * Preventing events that will be able to happen */ event.preventDefault(); event.stopPropagation(); event.stopImmediatePropagation(); this.closeActions(); this.inlineToolbar.close(); this.checkState(); } /** * Detects if passed string is URL * @param {string} str * @return {Boolean} */ }, { key: 'validateURL', value: function validateURL(str) { /** * Don't allow spaces */ return !/\s/.test(str); } /** * Process link before injection * - sanitize * - add protocol for links like 'google.com' * @param {string} link - raw user input */ }, { key: 'prepareLink', value: function prepareLink(link) { link = link.trim(); link = this.addProtocol(link); return link; } /** * Add 'http' protocol to the links like 'vc.ru', 'google.com' * @param {String} link */ }, { key: 'addProtocol', value: function addProtocol(link) { /** * If protocol already exists, do nothing */ if (/^(\w+):\/\//.test(link)) { return link; } /** * We need to add missed HTTP protocol to the link, but skip 2 cases: * 1) Internal links like "/general" * 2) Anchors looks like "#results" * 3) Protocol-relative URLs like "//google.com" */ var isInternal = /^\/[^\/\s]/.test(link), isAnchor = link.substring(0, 1) === '#', isProtocolRelative = /^\/\/[^\/\s]/.test(link); if (!isInternal && !isAnchor && !isProtocolRelative) { link = 'http://' + link; } return link; } /** * Inserts tag with "href" * @param {string} link - "href" value */ }, { key: 'insertLink', value: function insertLink(link) { /** * Edit all link, not selected part */ var anchorTag = this.selection.findParentTag('A'); if (anchorTag) { this.selection.expandToTag(anchorTag); } document.execCommand(this.commandLink, false, link); } /** * Removes tag */ }, { key: 'unlink', value: function unlink() { document.execCommand(this.commandUnlink); } }]); return LinkInlineTool; }(); LinkInlineTool.displayName = 'LinkInlineTool'; exports.default = LinkInlineTool; module.exports = exports['default']; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! dom */ "./src/components/dom.js"), __webpack_require__(/*! utils */ "./src/components/utils.js"))) /***/ }), /***/ "./src/components/modules sync recursive ^\\.\\/.*$": /*!**********************************************!*\ !*** ./src/components/modules sync ^\.\/.*$ ***! \**********************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { var map = { "./_anchors": "./src/components/modules/_anchors.js", "./_anchors.js": "./src/components/modules/_anchors.js", "./_callbacks": "./src/components/modules/_callbacks.js", "./_callbacks.js": "./src/components/modules/_callbacks.js", "./_caret": "./src/components/modules/_caret.js", "./_caret.js": "./src/components/modules/_caret.js", "./_content": "./src/components/modules/_content.js", "./_content.js": "./src/components/modules/_content.js", "./_destroyer": "./src/components/modules/_destroyer.js", "./_destroyer.js": "./src/components/modules/_destroyer.js", "./_notifications": "./src/components/modules/_notifications.js", "./_notifications.js": "./src/components/modules/_notifications.js", "./_parser": "./src/components/modules/_parser.js", "./_parser.js": "./src/components/modules/_parser.js", "./_paste": "./src/components/modules/_paste.js", "./_paste.js": "./src/components/modules/_paste.js", "./_transport": "./src/components/modules/_transport.js", "./_transport.js": "./src/components/modules/_transport.js", "./api": "./src/components/modules/api.ts", "./api-blocks": "./src/components/modules/api-blocks.ts", "./api-blocks.ts": "./src/components/modules/api-blocks.ts", "./api-events": "./src/components/modules/api-events.ts", "./api-events.ts": "./src/components/modules/api-events.ts", "./api-listener": "./src/components/modules/api-listener.ts", "./api-listener.ts": "./src/components/modules/api-listener.ts", "./api-sanitizer": "./src/components/modules/api-sanitizer.ts", "./api-sanitizer.ts": "./src/components/modules/api-sanitizer.ts", "./api-saver": "./src/components/modules/api-saver.ts", "./api-saver.ts": "./src/components/modules/api-saver.ts", "./api-selection": "./src/components/modules/api-selection.ts", "./api-selection.ts": "./src/components/modules/api-selection.ts", "./api-toolbar": "./src/components/modules/api-toolbar.ts", "./api-toolbar.ts": "./src/components/modules/api-toolbar.ts", "./api.ts": "./src/components/modules/api.ts", "./block-events": "./src/components/modules/block-events.ts", "./block-events.ts": "./src/components/modules/block-events.ts", "./blockManager": "./src/components/modules/blockManager.js", "./blockManager.js": "./src/components/modules/blockManager.js", "./caret": "./src/components/modules/caret.js", "./caret.js": "./src/components/modules/caret.js", "./events": "./src/components/modules/events.js", "./events.js": "./src/components/modules/events.js", "./listeners": "./src/components/modules/listeners.js", "./listeners.js": "./src/components/modules/listeners.js", "./renderer": "./src/components/modules/renderer.js", "./renderer.js": "./src/components/modules/renderer.js", "./sanitizer": "./src/components/modules/sanitizer.js", "./sanitizer.js": "./src/components/modules/sanitizer.js", "./saver": "./src/components/modules/saver.js", "./saver.js": "./src/components/modules/saver.js", "./toolbar": "./src/components/modules/toolbar.js", "./toolbar-blockSettings": "./src/components/modules/toolbar-blockSettings.js", "./toolbar-blockSettings.js": "./src/components/modules/toolbar-blockSettings.js", "./toolbar-inline": "./src/components/modules/toolbar-inline.ts", "./toolbar-inline.ts": "./src/components/modules/toolbar-inline.ts", "./toolbar-toolbox": "./src/components/modules/toolbar-toolbox.js", "./toolbar-toolbox.js": "./src/components/modules/toolbar-toolbox.js", "./toolbar.js": "./src/components/modules/toolbar.js", "./toolbar/inline": "./src/components/modules/toolbar/inline.js", "./toolbar/inline.js": "./src/components/modules/toolbar/inline.js", "./toolbar/settings": "./src/components/modules/toolbar/settings.js", "./toolbar/settings.js": "./src/components/modules/toolbar/settings.js", "./toolbar/toolbar": "./src/components/modules/toolbar/toolbar.js", "./toolbar/toolbar.js": "./src/components/modules/toolbar/toolbar.js", "./toolbar/toolbox": "./src/components/modules/toolbar/toolbox.js", "./toolbar/toolbox.js": "./src/components/modules/toolbar/toolbox.js", "./tools": "./src/components/modules/tools.js", "./tools.js": "./src/components/modules/tools.js", "./ui": "./src/components/modules/ui.js", "./ui.js": "./src/components/modules/ui.js" }; function webpackContext(req) { var id = webpackContextResolve(req); return __webpack_require__(id); } function webpackContextResolve(req) { var id = map[req]; if(!(id + 1)) { // check for number or string var e = new Error("Cannot find module '" + req + "'"); e.code = 'MODULE_NOT_FOUND'; throw e; } return id; } webpackContext.keys = function webpackContextKeys() { return Object.keys(map); }; webpackContext.resolve = webpackContextResolve; module.exports = webpackContext; webpackContext.id = "./src/components/modules sync recursive ^\\.\\/.*$"; /***/ }), /***/ "./src/components/modules/_anchors.js": /*!********************************************!*\ !*** ./src/components/modules/_anchors.js ***! \********************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /** * Codex Editor Anchors module * * @author Codex Team * @version 1.0 */ module.exports = function (anchors) { var editor = codex.editor; anchors.input = null; anchors.currentNode = null; anchors.settingsOpened = function (currentBlock) { anchors.currentNode = currentBlock; anchors.input.value = anchors.currentNode.dataset.anchor || ''; }; anchors.anchorChanged = function (e) { var newAnchor = e.target.value = anchors.rusToTranslit(e.target.value); anchors.currentNode.dataset.anchor = newAnchor; if (newAnchor.trim() !== '') { anchors.currentNode.classList.add(editor.ui.className.BLOCK_WITH_ANCHOR); } else { anchors.currentNode.classList.remove(editor.ui.className.BLOCK_WITH_ANCHOR); } }; anchors.keyDownOnAnchorInput = function (e) { if (e.keyCode == editor.core.keys.ENTER) { e.preventDefault(); e.stopPropagation(); e.target.blur(); editor.toolbar.settings.close(); } }; anchors.keyUpOnAnchorInput = function (e) { if (e.keyCode >= editor.core.keys.LEFT && e.keyCode <= editor.core.keys.DOWN) { e.stopPropagation(); } }; anchors.rusToTranslit = function (string) { var ru = ['А', 'Б', 'В', 'Г', 'Д', 'Е', 'Ё', 'Ж', 'З', 'И', 'Й', 'К', 'Л', 'М', 'Н', 'О', 'П', 'Р', 'С', 'Т', 'У', 'Ф', 'Х', 'Ц', 'Ч', 'Ш', 'Щ', 'Ь', 'Ы', 'Ь', 'Э', 'Ю', 'Я'], en = ['A', 'B', 'V', 'G', 'D', 'E', 'E', 'Zh', 'Z', 'I', 'Y', 'K', 'L', 'M', 'N', 'O', 'P', 'R', 'S', 'T', 'U', 'F', 'H', 'C', 'Ch', 'Sh', 'Sch', '', 'Y', '', 'E', 'Yu', 'Ya']; for (var i = 0; i < ru.length; i++) { string = string.split(ru[i]).join(en[i]); string = string.split(ru[i].toLowerCase()).join(en[i].toLowerCase()); } string = string.replace(/[^0-9a-zA-Z_]+/g, '-'); return string; }; return anchors; }({}); /***/ }), /***/ "./src/components/modules/_callbacks.js": /*!**********************************************!*\ !*** ./src/components/modules/_callbacks.js ***! \**********************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /** * @module Codex Editor Callbacks module * @description Module works with editor added Elements * * @author Codex Team * @version 1.4.0 */ module.exports = function (callbacks) { var editor = codex.editor; /** * used by UI module * @description Routes all keydowns on document * @param {Object} event */ callbacks.globalKeydown = function (event) { switch (event.keyCode) { case editor.core.keys.ENTER: enterKeyPressed_(event);break; } }; /** * used by UI module * @description Routes all keydowns on redactors area * @param {Object} event */ callbacks.redactorKeyDown = function (event) { switch (event.keyCode) { case editor.core.keys.TAB: tabKeyPressedOnRedactorsZone_(event);break; case editor.core.keys.ENTER: enterKeyPressedOnRedactorsZone_(event);break; case editor.core.keys.ESC: escapeKeyPressedOnRedactorsZone_(event);break; default: defaultKeyPressedOnRedactorsZone_(event);break; } }; /** * used by UI module * @description Routes all keyup events * @param {Object} event */ callbacks.globalKeyup = function (event) { switch (event.keyCode) { case editor.core.keys.UP: case editor.core.keys.LEFT: case editor.core.keys.RIGHT: case editor.core.keys.DOWN: arrowKeyPressed_(event);break; } }; /** * @param {Object} event * @private * * Handles behaviour when tab pressed * @description if Content is empty show toolbox (if it is closed) or leaf tools * uses Toolbars toolbox module to handle the situation */ var tabKeyPressedOnRedactorsZone_ = function tabKeyPressedOnRedactorsZone_(event) { /** * Wait for solution. Would like to know the behaviour * @todo Add spaces */ event.preventDefault(); if (!editor.core.isBlockEmpty(editor.content.currentNode)) { return; } if (!editor.toolbar.opened) { editor.toolbar.open(); } if (editor.toolbar.opened && !editor.toolbar.toolbox.opened) { editor.toolbar.toolbox.open(); } else { editor.toolbar.toolbox.leaf(); } }; /** * Handles global EnterKey Press * @see enterPressedOnBlock_ * @param {Object} event */ var enterKeyPressed_ = function enterKeyPressed_() { if (editor.content.editorAreaHightlighted) { /** * it means that we lose input index, saved index before is not correct * therefore we need to set caret when we insert new block */ editor.caret.inputIndex = -1; enterPressedOnBlock_(); } }; /** * Callback for enter key pressing in first-level block area * * @param {Event} event * @private * * @description Inserts new block with initial type from settings */ var enterPressedOnBlock_ = function enterPressedOnBlock_() { var NEW_BLOCK_TYPE = editor.settings.initialBlockPlugin; editor.content.insertBlock({ type: NEW_BLOCK_TYPE, block: editor.tools[NEW_BLOCK_TYPE].render() }, true); editor.toolbar.move(); editor.toolbar.open(); }; /** * ENTER key handler * * @param {Object} event * @private * * @description Makes new block with initial type from settings */ var enterKeyPressedOnRedactorsZone_ = function enterKeyPressedOnRedactorsZone_(event) { if (event.target.contentEditable == 'true') { /** Update input index */ editor.caret.saveCurrentInputIndex(); } var currentInputIndex = editor.caret.getCurrentInputIndex() || 0, workingNode = editor.content.currentNode, tool = workingNode.dataset.tool, isEnterPressedOnToolbar = editor.toolbar.opened && editor.toolbar.current && event.target == editor.state.inputs[currentInputIndex]; /** The list of tools which needs the default browser behaviour */ var enableLineBreaks = editor.tools[tool].enableLineBreaks; /** This type of block creates when enter is pressed */ var NEW_BLOCK_TYPE = editor.settings.initialBlockPlugin; /** * When toolbar is opened, select tool instead of making new paragraph */ if (isEnterPressedOnToolbar) { event.preventDefault(); editor.toolbar.toolbox.toolClicked(event); editor.toolbar.close(); /** * Stop other listeners callback executions */ event.stopPropagation(); event.stopImmediatePropagation(); return; } /** * Allow paragraph lineBreaks with shift enter * Or if shiftkey pressed and enter and enabledLineBreaks, the let new block creation */ if (event.shiftKey || enableLineBreaks) { event.stopPropagation(); event.stopImmediatePropagation(); return; } var currentSelection = window.getSelection(), currentSelectedNode = currentSelection.anchorNode, caretAtTheEndOfText = editor.caret.position.atTheEnd(), isTextNodeHasParentBetweenContenteditable = false; /** * Allow making new

    in same block by SHIFT+ENTER and forbids to prevent default browser behaviour */ if (event.shiftKey && !enableLineBreaks) { editor.callback.enterPressedOnBlock(editor.content.currentBlock, event); event.preventDefault(); return; } /** * Workaround situation when caret at the Text node that has some wrapper Elements * Split block cant handle this. * We need to save default behavior */ isTextNodeHasParentBetweenContenteditable = currentSelectedNode && currentSelectedNode.parentNode.contentEditable != 'true'; /** * Split blocks when input has several nodes and caret placed in textNode */ if (currentSelectedNode.nodeType == editor.core.nodeTypes.TEXT && !isTextNodeHasParentBetweenContenteditable && !caretAtTheEndOfText) { event.preventDefault(); editor.core.log('Splitting Text node...'); editor.content.splitBlock(currentInputIndex); /** Show plus button when next input after split is empty*/ if (!editor.state.inputs[currentInputIndex + 1].textContent.trim()) { editor.toolbar.showPlusButton(); } } else { var islastNode = editor.content.isLastNode(currentSelectedNode); if (islastNode && caretAtTheEndOfText) { event.preventDefault(); event.stopPropagation(); event.stopImmediatePropagation(); editor.core.log('ENTER clicked in last textNode. Create new BLOCK'); editor.content.insertBlock({ type: NEW_BLOCK_TYPE, block: editor.tools[NEW_BLOCK_TYPE].render() }, true); editor.toolbar.move(); editor.toolbar.open(); /** Show plus button with empty block */ editor.toolbar.showPlusButton(); } } /** get all inputs after new appending block */ editor.ui.saveInputs(); }; /** * Escape behaviour * @param event * @private * * @description Closes toolbox and toolbar. Prevents default behaviour */ var escapeKeyPressedOnRedactorsZone_ = function escapeKeyPressedOnRedactorsZone_(event) { /** Close all toolbar */ editor.toolbar.close(); /** Close toolbox */ editor.toolbar.toolbox.close(); event.preventDefault(); }; /** * @param {Event} event * @private * * closes and moves toolbar */ var arrowKeyPressed_ = function arrowKeyPressed_(event) { editor.content.workingNodeChanged(); /* Closing toolbar */ editor.toolbar.close(); editor.toolbar.move(); }; /** * @private * @param {Event} event * * @description Closes all opened bars from toolbar. * If block is mark, clears highlightning */ var defaultKeyPressedOnRedactorsZone_ = function defaultKeyPressedOnRedactorsZone_() { editor.toolbar.close(); if (!editor.toolbar.inline.actionsOpened) { editor.toolbar.inline.close(); editor.content.clearMark(); } }; /** * Handler when clicked on redactors area * * @protected * @param event * * @description Detects clicked area. If it is first-level block area, marks as detected and * on next enter press will be inserted new block * Otherwise, save carets position (input index) and put caret to the editable zone. * * @see detectWhenClickedOnFirstLevelBlockArea_ * */ callbacks.redactorClicked = function (event) { detectWhenClickedOnFirstLevelBlockArea_(); editor.content.workingNodeChanged(event.target); editor.ui.saveInputs(); var selectedText = editor.toolbar.inline.getSelectionText(), firstLevelBlock; /** If selection range took off, then we hide inline toolbar */ if (selectedText.length === 0) { editor.toolbar.inline.close(); } /** Update current input index in memory when caret focused into existed input */ if (event.target.contentEditable == 'true') { editor.caret.saveCurrentInputIndex(); } if (editor.content.currentNode === null) { /** * If inputs in redactor does not exits, then we put input index 0 not -1 */ var indexOfLastInput = editor.state.inputs.length > 0 ? editor.state.inputs.length - 1 : 0; /** If we have any inputs */ if (editor.state.inputs.length) { /** getting firstlevel parent of input */ firstLevelBlock = editor.content.getFirstLevelBlock(editor.state.inputs[indexOfLastInput]); } /** If input is empty, then we set caret to the last input */ if (editor.state.inputs.length && editor.state.inputs[indexOfLastInput].textContent === '' && firstLevelBlock.dataset.tool == editor.settings.initialBlockPlugin) { editor.caret.setToBlock(indexOfLastInput); } else { /** Create new input when caret clicked in redactors area */ var NEW_BLOCK_TYPE = editor.settings.initialBlockPlugin; editor.content.insertBlock({ type: NEW_BLOCK_TYPE, block: editor.tools[NEW_BLOCK_TYPE].render() }); /** If there is no inputs except inserted */ if (editor.state.inputs.length === 1) { editor.caret.setToBlock(indexOfLastInput); } else { /** Set caret to this appended input */ editor.caret.setToNextBlock(indexOfLastInput); } } } else { /** Close all panels */ editor.toolbar.settings.close(); editor.toolbar.toolbox.close(); } /** * Move toolbar and open */ editor.toolbar.move(); editor.toolbar.open(); var inputIsEmpty = !editor.content.currentNode.textContent.trim(), currentNodeType = editor.content.currentNode.dataset.tool, isInitialType = currentNodeType == editor.settings.initialBlockPlugin; /** Hide plus buttons */ editor.toolbar.hidePlusButton(); if (!inputIsEmpty) { /** Mark current block */ editor.content.markBlock(); } if (isInitialType && inputIsEmpty) { /** Show plus button */ editor.toolbar.showPlusButton(); } }; /** * This method allows to define, is caret in contenteditable element or not. * * @private * * @description Otherwise, if we get TEXT node from range container, that will means we have input index. * In this case we use default browsers behaviour (if plugin allows that) or overwritten action. * Therefore, to be sure that we've clicked first-level block area, we should have currentNode, which always * specifies to the first-level block. Other cases we just ignore. */ var detectWhenClickedOnFirstLevelBlockArea_ = function detectWhenClickedOnFirstLevelBlockArea_() { var selection = window.getSelection(), anchorNode = selection.anchorNode, flag = false; if (selection.rangeCount === 0) { editor.content.editorAreaHightlighted = true; } else { if (!editor.core.isDomNode(anchorNode)) { anchorNode = anchorNode.parentNode; } /** Already founded, without loop */ if (anchorNode.contentEditable == 'true') { flag = true; } while (anchorNode.contentEditable != 'true') { anchorNode = anchorNode.parentNode; if (anchorNode.contentEditable == 'true') { flag = true; } if (anchorNode == document.body) { break; } } /** If editable element founded, flag is "TRUE", Therefore we return "FALSE" */ editor.content.editorAreaHightlighted = !flag; } }; /** * Toolbar button click handler * * @param {Object} event - cursor to the button * @protected * * @description gets current tool and calls render method */ callbacks.toolbarButtonClicked = function (event) { var button = this; editor.toolbar.current = button.dataset.type; editor.toolbar.toolbox.toolClicked(event); editor.toolbar.close(); }; /** * Show or Hide toolbox when plus button is clicked */ callbacks.plusButtonClicked = function () { if (!editor.nodes.toolbox.classList.contains('opened')) { editor.toolbar.toolbox.open(); } else { editor.toolbar.toolbox.close(); } }; /** * Block handlers for KeyDown events * * @protected * @param {Object} event * * Handles keydowns on block * @see blockRightOrDownArrowPressed_ * @see backspacePressed_ * @see blockLeftOrUpArrowPressed_ */ callbacks.blockKeydown = function (event) { var block = event.target; // event.target is input switch (event.keyCode) { case editor.core.keys.DOWN: case editor.core.keys.RIGHT: blockRightOrDownArrowPressed_(event); break; case editor.core.keys.BACKSPACE: backspacePressed_(block, event); break; case editor.core.keys.UP: case editor.core.keys.LEFT: blockLeftOrUpArrowPressed_(event); break; } }; /** * RIGHT or DOWN keydowns on block * * @param {Object} event * @private * * @description watches the selection and gets closest editable element. * Uses method getDeepestTextNodeFromPosition to get the last node of next block * Sets caret if it is contenteditable */ var blockRightOrDownArrowPressed_ = function blockRightOrDownArrowPressed_(event) { var selection = window.getSelection(), inputs = editor.state.inputs, focusedNode = selection.anchorNode, focusedNodeHolder; /** Check for caret existance */ if (!focusedNode) { return false; } /** Looking for closest (parent) contentEditable element of focused node */ while (focusedNode.contentEditable != 'true') { focusedNodeHolder = focusedNode.parentNode; focusedNode = focusedNodeHolder; } /** Input index in DOM level */ var editableElementIndex = 0; while (focusedNode != inputs[editableElementIndex]) { editableElementIndex++; } /** * Founded contentEditable element doesn't have childs * Or maybe New created block */ if (!focusedNode.textContent) { editor.caret.setToNextBlock(editableElementIndex); return; } /** * Do nothing when caret doesn not reaches the end of last child */ var caretInLastChild = false, caretAtTheEndOfText = false; var lastChild, deepestTextnode; lastChild = focusedNode.childNodes[focusedNode.childNodes.length - 1]; if (editor.core.isDomNode(lastChild)) { deepestTextnode = editor.content.getDeepestTextNodeFromPosition(lastChild, lastChild.childNodes.length); } else { deepestTextnode = lastChild; } caretInLastChild = selection.anchorNode == deepestTextnode; caretAtTheEndOfText = deepestTextnode.length == selection.anchorOffset; if (!caretInLastChild || !caretAtTheEndOfText) { editor.core.log('arrow [down|right] : caret does not reached the end'); return false; } editor.caret.setToNextBlock(editableElementIndex); }; /** * LEFT or UP keydowns on block * * @param {Object} event * @private * * watches the selection and gets closest editable element. * Uses method getDeepestTextNodeFromPosition to get the last node of previous block * Sets caret if it is contenteditable * */ var blockLeftOrUpArrowPressed_ = function blockLeftOrUpArrowPressed_(event) { var selection = window.getSelection(), inputs = editor.state.inputs, focusedNode = selection.anchorNode, focusedNodeHolder; /** Check for caret existance */ if (!focusedNode) { return false; } /** * LEFT or UP not at the beginning */ if (selection.anchorOffset !== 0) { return false; } /** Looking for parent contentEditable block */ while (focusedNode.contentEditable != 'true') { focusedNodeHolder = focusedNode.parentNode; focusedNode = focusedNodeHolder; } /** Input index in DOM level */ var editableElementIndex = 0; while (focusedNode != inputs[editableElementIndex]) { editableElementIndex++; } /** * Do nothing if caret is not at the beginning of first child */ var caretInFirstChild = false, caretAtTheBeginning = false; var firstChild, deepestTextnode; /** * Founded contentEditable element doesn't have childs * Or maybe New created block */ if (!focusedNode.textContent) { editor.caret.setToPreviousBlock(editableElementIndex); return; } firstChild = focusedNode.childNodes[0]; if (editor.core.isDomNode(firstChild)) { deepestTextnode = editor.content.getDeepestTextNodeFromPosition(firstChild, 0); } else { deepestTextnode = firstChild; } caretInFirstChild = selection.anchorNode == deepestTextnode; caretAtTheBeginning = selection.anchorOffset === 0; if (caretInFirstChild && caretAtTheBeginning) { editor.caret.setToPreviousBlock(editableElementIndex); } }; /** * Handles backspace keydown * * @param {Element} block * @param {Object} event * @private * * @description if block is empty, delete the block and set caret to the previous block * If block is not empty, try to merge two blocks - current and previous * But it we try'n to remove first block, then we should set caret to the next block, not previous. * If we removed the last block, create new one */ var backspacePressed_ = function backspacePressed_(block, event) { var currentInputIndex = editor.caret.getCurrentInputIndex(), range, selectionLength, firstLevelBlocksCount; if (editor.core.isNativeInput(event.target)) { /** If input value is empty - remove block */ if (event.target.value.trim() == '') { block.remove(); } else { return; } } if (block.textContent.trim()) { range = editor.content.getRange(); selectionLength = range.endOffset - range.startOffset; if (editor.caret.position.atStart() && !selectionLength && editor.state.inputs[currentInputIndex - 1]) { editor.content.mergeBlocks(currentInputIndex); } else { return; } } if (!selectionLength) { block.remove(); } firstLevelBlocksCount = editor.nodes.redactor.childNodes.length; /** * If all blocks are removed */ if (firstLevelBlocksCount === 0) { /** update currentNode variable */ editor.content.currentNode = null; /** Inserting new empty initial block */ editor.ui.addInitialBlock(); /** Updating inputs state after deleting last block */ editor.ui.saveInputs(); /** Set to current appended block */ window.setTimeout(function () { editor.caret.setToPreviousBlock(1); }, 10); } else { if (editor.caret.inputIndex !== 0) { /** Target block is not first */ editor.caret.setToPreviousBlock(editor.caret.inputIndex); } else { /** If we try to delete first block */ editor.caret.setToNextBlock(editor.caret.inputIndex); } } editor.toolbar.move(); if (!editor.toolbar.opened) { editor.toolbar.open(); } /** Updating inputs state */ editor.ui.saveInputs(); /** Prevent default browser behaviour */ event.preventDefault(); }; /** * used by UI module * Clicks on block settings button * * @param {Object} event * @protected * @description Opens toolbar settings */ callbacks.showSettingsButtonClicked = function (event) { /** * Get type of current block * It uses to append settings from tool.settings property. * ... * Type is stored in data-type attribute on block */ var currentToolType = editor.content.currentNode.dataset.tool; editor.toolbar.settings.toggle(currentToolType); /** Close toolbox when settings button is active */ editor.toolbar.toolbox.close(); editor.toolbar.settings.hideRemoveActions(); }; return callbacks; }({}); /***/ }), /***/ "./src/components/modules/_caret.js": /*!******************************************!*\ !*** ./src/components/modules/_caret.js ***! \******************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /** * Codex Editor Caret Module * * @author Codex Team * @version 1.0 */ module.exports = function (caret) { var editor = codex.editor; /** * @var {int} InputIndex - editable element in DOM */ caret.inputIndex = null; /** * @var {int} offset - caret position in a text node. */ caret.offset = null; /** * @var {int} focusedNodeIndex - we get index of child node from first-level block */ caret.focusedNodeIndex = null; /** * Creates Document Range and sets caret to the element. * @protected * @uses caret.save — if you need to save caret position * @param {Element} el - Changed Node. */ caret.set = function (el, index, offset) { offset = offset || caret.offset || 0; index = index || caret.focusedNodeIndex || 0; var childs = el.childNodes, nodeToSet; if (childs.length === 0) { nodeToSet = el; } else { nodeToSet = childs[index]; } /** If Element is INPUT */ if (el.contentEditable != 'true') { el.focus(); return; } if (editor.core.isDomNode(nodeToSet)) { nodeToSet = editor.content.getDeepestTextNodeFromPosition(nodeToSet, nodeToSet.childNodes.length); } var range = document.createRange(), selection = window.getSelection(); window.setTimeout(function () { range.setStart(nodeToSet, offset); range.setEnd(nodeToSet, offset); selection.removeAllRanges(); selection.addRange(range); editor.caret.saveCurrentInputIndex(); }, 20); }; /** * @protected * Updates index of input and saves it in caret object */ caret.saveCurrentInputIndex = function () { /** Index of Input that we paste sanitized content */ var selection = window.getSelection(), inputs = editor.state.inputs, focusedNode = selection.anchorNode, focusedNodeHolder; if (!focusedNode) { return; } /** Looking for parent contentEditable block */ while (focusedNode.contentEditable != 'true') { focusedNodeHolder = focusedNode.parentNode; focusedNode = focusedNodeHolder; } /** Input index in DOM level */ var editableElementIndex = 0; while (focusedNode != inputs[editableElementIndex]) { editableElementIndex++; } caret.inputIndex = editableElementIndex; }; /** * Returns current input index (caret object) */ caret.getCurrentInputIndex = function () { return caret.inputIndex; }; /** * @param {int} index - index of first-level block after that we set caret into next input */ caret.setToNextBlock = function (index) { var inputs = editor.state.inputs, nextInput = inputs[index + 1]; if (!nextInput) { editor.core.log('We are reached the end'); return; } /** * When new Block created or deleted content of input * We should add some text node to set caret */ if (!nextInput.childNodes.length) { var emptyTextElement = document.createTextNode(''); nextInput.appendChild(emptyTextElement); } editor.caret.inputIndex = index + 1; editor.caret.set(nextInput, 0, 0); editor.content.workingNodeChanged(nextInput); }; /** * @param {int} index - index of target input. * Sets caret to input with this index */ caret.setToBlock = function (index) { var inputs = editor.state.inputs, targetInput = inputs[index]; if (!targetInput) { return; } /** * When new Block created or deleted content of input * We should add some text node to set caret */ if (!targetInput.childNodes.length) { var emptyTextElement = document.createTextNode(''); targetInput.appendChild(emptyTextElement); } editor.caret.inputIndex = index; editor.caret.set(targetInput, 0, 0); editor.content.workingNodeChanged(targetInput); }; /** * @param {int} index - index of input */ caret.setToPreviousBlock = function (index) { index = index || 0; var inputs = editor.state.inputs, previousInput = inputs[index - 1], lastChildNode, lengthOfLastChildNode, emptyTextElement; if (!previousInput) { editor.core.log('We are reached first node'); return; } lastChildNode = editor.content.getDeepestTextNodeFromPosition(previousInput, previousInput.childNodes.length); lengthOfLastChildNode = lastChildNode.length; /** * When new Block created or deleted content of input * We should add some text node to set caret */ if (!previousInput.childNodes.length) { emptyTextElement = document.createTextNode(''); previousInput.appendChild(emptyTextElement); } editor.caret.inputIndex = index - 1; editor.caret.set(previousInput, previousInput.childNodes.length - 1, lengthOfLastChildNode); editor.content.workingNodeChanged(inputs[index - 1]); }; caret.position = { atStart: function atStart() { var selection = window.getSelection(), anchorOffset = selection.anchorOffset, anchorNode = selection.anchorNode, firstLevelBlock = editor.content.getFirstLevelBlock(anchorNode), pluginsRender = firstLevelBlock.childNodes[0]; if (!editor.core.isDomNode(anchorNode)) { anchorNode = anchorNode.parentNode; } var isFirstNode = anchorNode === pluginsRender.childNodes[0], isOffsetZero = anchorOffset === 0; return isFirstNode && isOffsetZero; }, atTheEnd: function atTheEnd() { var selection = window.getSelection(), anchorOffset = selection.anchorOffset, anchorNode = selection.anchorNode; /** Caret is at the end of input */ return !anchorNode || !anchorNode.length || anchorOffset === anchorNode.length; } }; /** * Inserts node at the caret location * @param {HTMLElement|DocumentFragment} node */ caret.insertNode = function (node) { var selection, range, lastNode = node; if (node.nodeType == editor.core.nodeTypes.DOCUMENT_FRAGMENT) { lastNode = node.lastChild; } selection = window.getSelection(); range = selection.getRangeAt(0); range.deleteContents(); range.insertNode(node); range.setStartAfter(lastNode); range.collapse(true); selection.removeAllRanges(); selection.addRange(range); }; return caret; }({}); /***/ }), /***/ "./src/components/modules/_content.js": /*!********************************************!*\ !*** ./src/components/modules/_content.js ***! \********************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; 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; }; }(); /** * Codex Editor Content Module * Works with DOM * * @class Content * @classdesc Class works provides COdex Editor appearance logic * * @author Codex Team * @version 2.0.0 */ var _dom = __webpack_require__(/*! ../dom */ "./src/components/dom.js"); var _dom2 = _interopRequireDefault(_dom); 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"); } } module.exports = function () { _createClass(Content, null, [{ key: 'name', /** * Module key name * @returns {string} */ get: function get() { return 'Content'; } /** * @constructor * * @param {EditorConfig} config */ }]); function Content(config) { _classCallCheck(this, Content); this.config = config; this.Editor = null; this.CSS = { block: 'ce-block', content: 'ce-block__content', stretched: 'ce-block--stretched', highlighted: 'ce-block--highlighted' }; this._currentNode = null; this._currentIndex = 0; } /** * Editor modules setter * @param {object} Editor */ _createClass(Content, [{ key: 'composeBlock_', /** * @private * @param pluginHTML * @param {Boolean} isStretched - make stretched block or not * * @description adds necessary information to wrap new created block by first-level holder */ value: function composeBlock_(pluginHTML) { var isStretched = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; var block = _dom2.default.make('DIV', this.CSS.block), blockContent = _dom2.default.make('DIV', this.CSS.content); blockContent.appendChild(pluginHTML); block.appendChild(blockContent); if (isStretched) { blockContent.classList.add(this.CSS.stretched); } block.dataset.toolId = this._currentIndex++; return block; } }, { key: 'getFirstLevelBlock', /** * Finds first-level block * @description looks for first-level block. * gets parent while node is not first-level * * @param {Element} node - selected or clicked in redactors area node * @protected * */ value: function getFirstLevelBlock(node) { if (!_dom2.default.isElement(node)) { node = node.parentNode; } if (node === this.Editor.ui.nodes.redactor || node === document.body) { return null; } else { while (node.classList && !node.classList.contains(this.CSS.block)) { node = node.parentNode; } return node; } } }, { key: 'insertBlock', /** * Insert new block to working area * * @param {HTMLElement} tool * * @returns {Number} tool index * */ value: function insertBlock(tool) { var newBlock = this.composeBlock_(tool); if (this.currentNode) { this.currentNode.insertAdjacentElement('afterend', newBlock); } else { /** * If redactor is empty, append as first child */ this.Editor.ui.nodes.redactor.appendChild(newBlock); } /** * Set new node as current */ this.currentNode = newBlock; return newBlock.dataset.toolId; } }, { key: 'state', set: function set(Editor) { this.Editor = Editor; } /** * Get current working node * * @returns {null|HTMLElement} */ }, { key: 'currentNode', get: function get() { return this._currentNode; } /** * Set working node. Working node should be first level block, so we find it before set one to _currentNode property * * @param {HTMLElement} node */ , set: function set(node) { var firstLevelBlock = this.getFirstLevelBlock(node); this._currentNode = firstLevelBlock; } }]); return Content; }(); // module.exports = (function (content) { // // let editor = codex.editor; // // /** // * Links to current active block // * @type {null | Element} // */ // content.currentNode = null; // // /** // * clicked in redactor area // * @type {null | Boolean} // */ // content.editorAreaHightlighted = null; // // /** // * @deprecated // * Synchronizes redactor with original textarea // */ // content.sync = function () { // // editor.core.log('syncing...'); // // /** // * Save redactor content to editor.state // */ // editor.state.html = editor.nodes.redactor.innerHTML; // // }; // // /** // * Appends background to the block // * // * @description add CSS class to highlight visually first-level block area // */ // content.markBlock = function () { // // editor.content.currentNode.classList.add(editor.ui.className.BLOCK_HIGHLIGHTED); // // }; // // /** // * Clear background // * // * @description clears styles that highlights block // */ // content.clearMark = function () { // // if (editor.content.currentNode) { // // editor.content.currentNode.classList.remove(editor.ui.className.BLOCK_HIGHLIGHTED); // // } // // }; // // /** // * Finds first-level block // * // * @param {Element} node - selected or clicked in redactors area node // * @protected // * // * @description looks for first-level block. // * gets parent while node is not first-level // */ // content.getFirstLevelBlock = function (node) { // // if (!editor.core.isDomNode(node)) { // // node = node.parentNode; // // } // // if (node === editor.nodes.redactor || node === document.body) { // // return null; // // } else { // // while(!node.classList.contains(editor.ui.className.BLOCK_CLASSNAME)) { // // node = node.parentNode; // // } // // return node; // // } // // }; // // /** // * Trigger this event when working node changed // * @param {Element} targetNode - first-level of this node will be current // * @protected // * // * @description If targetNode is first-level then we set it as current else we look for parents to find first-level // */ // content.workingNodeChanged = function (targetNode) { // // /** Clear background from previous marked block before we change */ // editor.content.clearMark(); // // if (!targetNode) { // // return; // // } // // content.currentNode = content.getFirstLevelBlock(targetNode); // // }; // // /** // * Replaces one redactor block with another // * @protected // * @param {Element} targetBlock - block to replace. Mostly currentNode. // * @param {Element} newBlock // * @param {string} newBlockType - type of new block; we need to store it to data-attribute // * // * [!] Function does not saves old block content. // * You can get it manually and pass with newBlock.innerHTML // */ // content.replaceBlock = function (targetBlock, newBlock) { // // if (!targetBlock || !newBlock) { // // editor.core.log('replaceBlock: missed params'); // return; // // } // // /** If target-block is not a frist-level block, then we iterate parents to find it */ // while(!targetBlock.classList.contains(editor.ui.className.BLOCK_CLASSNAME)) { // // targetBlock = targetBlock.parentNode; // // } // // /** Replacing */ // editor.nodes.redactor.replaceChild(newBlock, targetBlock); // // /** // * Set new node as current // */ // editor.content.workingNodeChanged(newBlock); // // /** // * Add block handlers // */ // editor.ui.addBlockHandlers(newBlock); // // /** // * Save changes // */ // editor.ui.saveInputs(); // // }; // // /** // * @protected // * // * Inserts new block to redactor // * Wrapps block into a DIV with BLOCK_CLASSNAME class // * // * @param blockData {object} // * @param blockData.block {Element} element with block content // * @param blockData.type {string} block plugin // * @param needPlaceCaret {bool} pass true to set caret in new block // * // */ // content.insertBlock = function ( blockData, needPlaceCaret ) { // // var workingBlock = editor.content.currentNode, // newBlockContent = blockData.block, // blockType = blockData.type, // isStretched = blockData.stretched; // // var newBlock = composeNewBlock_(newBlockContent, blockType, isStretched); // // if (workingBlock) { // // editor.core.insertAfter(workingBlock, newBlock); // // } else { // // /** // * If redactor is empty, append as first child // */ // editor.nodes.redactor.appendChild(newBlock); // // } // // /** // * Block handler // */ // editor.ui.addBlockHandlers(newBlock); // // /** // * Set new node as current // */ // editor.content.workingNodeChanged(newBlock); // // /** // * Save changes // */ // editor.ui.saveInputs(); // // // if ( needPlaceCaret ) { // // /** // * If we don't know input index then we set default value -1 // */ // var currentInputIndex = editor.caret.getCurrentInputIndex() || -1; // // // if (currentInputIndex == -1) { // // // var editableElement = newBlock.querySelector('[contenteditable]'), // emptyText = document.createTextNode(''); // // editableElement.appendChild(emptyText); // editor.caret.set(editableElement, 0, 0); // // editor.toolbar.move(); // editor.toolbar.showPlusButton(); // // // } else { // // if (currentInputIndex === editor.state.inputs.length - 1) // return; // // /** Timeout for browsers execution */ // window.setTimeout(function () { // // /** Setting to the new input */ // editor.caret.setToNextBlock(currentInputIndex); // editor.toolbar.move(); // editor.toolbar.open(); // // }, 10); // // } // // } // // /** // * Block is inserted, wait for new click that defined focusing on editors area // * @type {boolean} // */ // content.editorAreaHightlighted = false; // // }; // // /** // * Replaces blocks with saving content // * @protected // * @param {Element} noteToReplace // * @param {Element} newNode // * @param {Element} blockType // */ // content.switchBlock = function (blockToReplace, newBlock, tool) { // // tool = tool || editor.content.currentNode.dataset.tool; // var newBlockComposed = composeNewBlock_(newBlock, tool); // // /** Replacing */ // editor.content.replaceBlock(blockToReplace, newBlockComposed); // // /** Save new Inputs when block is changed */ // editor.ui.saveInputs(); // // }; // // /** // * Iterates between child noted and looking for #text node on deepest level // * @protected // * // * @param {Element} block - node where find // * @param {int} postiton - starting postion // * Example: childNodex.length to find from the end // * or 0 to find from the start // * @return {Text} block // * @uses DFS // */ // content.getDeepestTextNodeFromPosition = function (block, position) { // // /** // * Clear Block from empty and useless spaces with trim. // * Such nodes we should remove // */ // var blockChilds = block.childNodes, // index, // node, // text; // // for(index = 0; index < blockChilds.length; index++) { // // node = blockChilds[index]; // // if (node.nodeType == editor.core.nodeTypes.TEXT) { // // text = node.textContent.trim(); // // /** Text is empty. We should remove this child from node before we start DFS // * decrease the quantity of childs. // */ // if (text === '') { // // block.removeChild(node); // position--; // // } // // } // // } // // if (block.childNodes.length === 0) { // // return document.createTextNode(''); // // } // // /** Setting default position when we deleted all empty nodes */ // if ( position < 0 ) // position = 1; // // var lookingFromStart = false; // // /** For looking from START */ // if (position === 0) { // // lookingFromStart = true; // position = 1; // // } // // while ( position ) { // // /** initial verticle of node. */ // if ( lookingFromStart ) { // // block = block.childNodes[0]; // // } else { // // block = block.childNodes[position - 1]; // // } // // if ( block.nodeType == editor.core.nodeTypes.TAG ) { // // position = block.childNodes.length; // // } else if (block.nodeType == editor.core.nodeTypes.TEXT ) { // // position = 0; // // } // // } // // return block; // // }; // // /** // * @private // * @param {Element} block - current plugins render // * @param {String} tool - plugins name // * @param {Boolean} isStretched - make stretched block or not // * // * @description adds necessary information to wrap new created block by first-level holder // */ // var composeNewBlock_ = function (block, tool, isStretched) { // // var newBlock = editor.draw.node('DIV', editor.ui.className.BLOCK_CLASSNAME, {}), // blockContent = editor.draw.node('DIV', editor.ui.className.BLOCK_CONTENT, {}); // // blockContent.appendChild(block); // newBlock.appendChild(blockContent); // // if (isStretched) { // // blockContent.classList.add(editor.ui.className.BLOCK_STRETCHED); // // } // // newBlock.dataset.tool = tool; // return newBlock; // // }; // // /** // * Returns Range object of current selection // * @protected // */ // content.getRange = function () { // // var selection = window.getSelection().getRangeAt(0); // // return selection; // // }; // // /** // * Divides block in two blocks (after and before caret) // * // * @protected // * @param {int} inputIndex - target input index // * // * @description splits current input content to the separate blocks // * When enter is pressed among the words, that text will be splited. // */ // content.splitBlock = function (inputIndex) { // // var selection = window.getSelection(), // anchorNode = selection.anchorNode, // anchorNodeText = anchorNode.textContent, // caretOffset = selection.anchorOffset, // textBeforeCaret, // textNodeBeforeCaret, // textAfterCaret, // textNodeAfterCaret; // // var currentBlock = editor.content.currentNode.querySelector('[contentEditable]'); // // // textBeforeCaret = anchorNodeText.substring(0, caretOffset); // textAfterCaret = anchorNodeText.substring(caretOffset); // // textNodeBeforeCaret = document.createTextNode(textBeforeCaret); // // if (textAfterCaret) { // // textNodeAfterCaret = document.createTextNode(textAfterCaret); // // } // // var previousChilds = [], // nextChilds = [], // reachedCurrent = false; // // if (textNodeAfterCaret) { // // nextChilds.push(textNodeAfterCaret); // // } // // for ( var i = 0, child; !!(child = currentBlock.childNodes[i]); i++) { // // if ( child != anchorNode ) { // // if ( !reachedCurrent ) { // // previousChilds.push(child); // // } else { // // nextChilds.push(child); // // } // // } else { // // reachedCurrent = true; // // } // // } // // /** Clear current input */ // editor.state.inputs[inputIndex].innerHTML = ''; // // /** // * Append all childs founded before anchorNode // */ // var previousChildsLength = previousChilds.length; // // for(i = 0; i < previousChildsLength; i++) { // // editor.state.inputs[inputIndex].appendChild(previousChilds[i]); // // } // // editor.state.inputs[inputIndex].appendChild(textNodeBeforeCaret); // // /** // * Append text node which is after caret // */ // var nextChildsLength = nextChilds.length, // newNode = document.createElement('div'); // // for(i = 0; i < nextChildsLength; i++) { // // newNode.appendChild(nextChilds[i]); // // } // // newNode = newNode.innerHTML; // // /** This type of block creates when enter is pressed */ // var NEW_BLOCK_TYPE = editor.settings.initialBlockPlugin; // // /** // * Make new paragraph with text after caret // */ // editor.content.insertBlock({ // type : NEW_BLOCK_TYPE, // block : editor.tools[NEW_BLOCK_TYPE].render({ // text : newNode // }) // }, true ); // // }; // // /** // * Merges two blocks — current and target // * If target index is not exist, then previous will be as target // * // * @protected // * @param {int} currentInputIndex // * @param {int} targetInputIndex // * // * @description gets two inputs indexes and merges into one // */ // content.mergeBlocks = function (currentInputIndex, targetInputIndex) { // // /** If current input index is zero, then prevent method execution */ // if (currentInputIndex === 0) { // // return; // // } // // var targetInput, // currentInputContent = editor.state.inputs[currentInputIndex].innerHTML; // // if (!targetInputIndex) { // // targetInput = editor.state.inputs[currentInputIndex - 1]; // // } else { // // targetInput = editor.state.inputs[targetInputIndex]; // // } // // targetInput.innerHTML += currentInputContent; // // }; // // /** // * Iterates all right siblings and parents, which has right siblings // * while it does not reached the first-level block // * // * @param {Element} node // * @return {boolean} // */ // content.isLastNode = function (node) { // // // console.log('погнали перебор родителей'); // // var allChecked = false; // // while ( !allChecked ) { // // // console.log('Смотрим на %o', node); // // console.log('Проверим, пустые ли соседи справа'); // // if ( !allSiblingsEmpty_(node) ) { // // // console.log('Есть непустые соседи. Узел не последний. Выходим.'); // return false; // // } // // node = node.parentNode; // // /** // * Проверяем родителей до тех пор, пока не найдем блок первого уровня // */ // if ( node.classList.contains(editor.ui.className.BLOCK_CONTENT) ) { // // allChecked = true; // // } // // } // // return true; // // }; // // /** // * Checks if all element right siblings is empty // * @param node // */ // var allSiblingsEmpty_ = function (node) { // // /** // * Нужно убедиться, что после пустого соседа ничего нет // */ // var sibling = node.nextSibling; // // while ( sibling ) { // // if (sibling.textContent.length) { // // return false; // // } // // sibling = sibling.nextSibling; // // } // // return true; // // }; // // /** // * @public // * // * @param {string} htmlData - html content as string // * @param {string} plainData - plain text // * @return {string} - html content as string // */ // content.wrapTextWithParagraphs = function (htmlData, plainData) { // // if (!htmlData.trim()) { // // return wrapPlainTextWithParagraphs(plainData); // // } // // var wrapper = document.createElement('DIV'), // newWrapper = document.createElement('DIV'), // i, // paragraph, // firstLevelBlocks = ['DIV', 'P'], // blockTyped, // node; // // /** // * Make HTML Element to Wrap Text // * It allows us to work with input data as HTML content // */ // wrapper.innerHTML = htmlData; // paragraph = document.createElement('P'); // // for (i = 0; i < wrapper.childNodes.length; i++) { // // node = wrapper.childNodes[i]; // // blockTyped = firstLevelBlocks.indexOf(node.tagName) != -1; // // /** // * If node is first-levet // * we add this node to our new wrapper // */ // if ( blockTyped ) { // // /** // * If we had splitted inline nodes to paragraph before // */ // if ( paragraph.childNodes.length ) { // // newWrapper.appendChild(paragraph.cloneNode(true)); // // /** empty paragraph */ // paragraph = null; // paragraph = document.createElement('P'); // // } // // newWrapper.appendChild(node.cloneNode(true)); // // } else { // // /** Collect all inline nodes to one as paragraph */ // paragraph.appendChild(node.cloneNode(true)); // // /** if node is last we should append this node to paragraph and paragraph to new wrapper */ // if ( i == wrapper.childNodes.length - 1 ) { // // newWrapper.appendChild(paragraph.cloneNode(true)); // // } // // } // // } // // return newWrapper.innerHTML; // // }; // // /** // * Splits strings on new line and wraps paragraphs with

    tag // * @param plainText // * @returns {string} // */ // var wrapPlainTextWithParagraphs = function (plainText) { // // if (!plainText) return ''; // // return '

    ' + plainText.split('\n\n').join('

    ') + '

    '; // // }; // // /** // * Finds closest Contenteditable parent from Element // * @param {Element} node element looking from // * @return {Element} node contenteditable // */ // content.getEditableParent = function (node) { // // while (node && node.contentEditable != 'true') { // // node = node.parentNode; // // } // // return node; // // }; // // /** // * Clear editors content // * // * @param {Boolean} all — if true, delete all article data (content, id, etc.) // */ // content.clear = function (all) { // // editor.nodes.redactor.innerHTML = ''; // editor.content.sync(); // editor.ui.saveInputs(); // if (all) { // // editor.state.blocks = {}; // // } else if (editor.state.blocks) { // // editor.state.blocks.items = []; // // } // // editor.content.currentNode = null; // // }; // // /** // * // * Load new data to editor // * If editor is not empty, just append articleData.items // * // * @param articleData.items // */ // content.load = function (articleData) { // // var currentContent = Object.assign({}, editor.state.blocks); // // editor.content.clear(); // // if (!Object.keys(currentContent).length) { // // editor.state.blocks = articleData; // // } else if (!currentContent.items) { // // currentContent.items = articleData.items; // editor.state.blocks = currentContent; // // } else { // // currentContent.items = currentContent.items.concat(articleData.items); // editor.state.blocks = currentContent; // // } // // editor.renderer.makeBlocksFromData(); // // }; // // return content; // // })({}); /***/ }), /***/ "./src/components/modules/_destroyer.js": /*!**********************************************!*\ !*** ./src/components/modules/_destroyer.js ***! \**********************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; /** * Codex Editor Destroyer module * * @auhor Codex Team * @version 1.0 */ module.exports = function (destroyer) { var editor = codex.editor; destroyer.removeNodes = function () { editor.nodes.wrapper.remove(); editor.nodes.notifications.remove(); }; destroyer.destroyPlugins = function () { for (var tool in editor.tools) { if (typeof editor.tools[tool].destroy === 'function') { editor.tools[tool].destroy(); } } }; destroyer.destroyScripts = function () { var scripts = document.getElementsByTagName('SCRIPT'); for (var i = 0; i < scripts.length; i++) { if (scripts[i].id.indexOf(editor.scriptPrefix) + 1) { scripts[i].remove(); i--; } } }; /** * Delete editor data from webpage. * You should send settings argument with boolean flags: * @param settings.ui- remove redactor event listeners and DOM nodes * @param settings.scripts - remove redactor scripts from DOM * @param settings.plugins - remove plugin's objects * @param settings.core - remove editor core. You can remove core only if UI and scripts flags is true * } * */ destroyer.destroy = function (settings) { if (!settings || (typeof settings === 'undefined' ? 'undefined' : _typeof(settings)) !== 'object') { return; } if (settings.ui) { destroyer.removeNodes(); editor.listeners.removeAll(); } if (settings.scripts) { destroyer.destroyScripts(); } if (settings.plugins) { destroyer.destroyPlugins(); } if (settings.ui && settings.scripts && settings.core) { delete codex.editor; } }; return destroyer; }({}); /***/ }), /***/ "./src/components/modules/_notifications.js": /*!**************************************************!*\ !*** ./src/components/modules/_notifications.js ***! \**************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /** * Codex Editor Notification Module * * @author Codex Team * @version 1.0 */ module.exports = function (notifications) { var editor = codex.editor; var queue = []; var addToQueue = function addToQueue(settings) { queue.push(settings); var index = 0; while (index < queue.length && queue.length > 5) { if (queue[index].type == 'confirm' || queue[index].type == 'prompt') { index++; continue; } queue[index].close(); queue.splice(index, 1); } }; notifications.createHolder = function () { var holder = editor.draw.node('DIV', 'cdx-notifications-block'); editor.nodes.notifications = document.body.appendChild(holder); return holder; }; /** * Error notificator. Shows block with message * @protected */ notifications.errorThrown = function (errorMsg, event) { editor.notifications.notification({ message: 'This action is not available currently', type: event.type }); }; /** * * Appends notification * * settings = { * type - notification type (reserved types: alert, confirm, prompt). Just add class 'cdx-notification-'+type * message - notification message * okMsg - confirm button text (default - 'Ok') * cancelBtn - cancel button text (default - 'Cancel'). Only for confirm and prompt types * confirm - function-handler for ok button click * cancel - function-handler for cancel button click. Only for confirm and prompt types * time - time (in seconds) after which notification will close (default - 10s) * } * * @param settings */ notifications.notification = function (constructorSettings) { /** Private vars and methods */ var notification = null, cancel = null, type = null, confirm = null, inputField = null; var confirmHandler = function confirmHandler() { close(); if (typeof confirm !== 'function') { return; } if (type == 'prompt') { confirm(inputField.value); return; } confirm(); }; var cancelHandler = function cancelHandler() { close(); if (typeof cancel !== 'function') { return; } cancel(); }; /** Public methods */ function create(settings) { if (!(settings && settings.message)) { editor.core.log('Can\'t create notification. Message is missed'); return; } settings.type = settings.type || 'alert'; settings.time = settings.time * 1000 || 10000; var wrapper = editor.draw.node('DIV', 'cdx-notification'), message = editor.draw.node('DIV', 'cdx-notification__message'), input = editor.draw.node('INPUT', 'cdx-notification__input'), okBtn = editor.draw.node('SPAN', 'cdx-notification__ok-btn'), cancelBtn = editor.draw.node('SPAN', 'cdx-notification__cancel-btn'); message.textContent = settings.message; okBtn.textContent = settings.okMsg || 'ОК'; cancelBtn.textContent = settings.cancelMsg || 'Отмена'; editor.listeners.add(okBtn, 'click', confirmHandler); editor.listeners.add(cancelBtn, 'click', cancelHandler); wrapper.appendChild(message); if (settings.type == 'prompt') { wrapper.appendChild(input); } wrapper.appendChild(okBtn); if (settings.type == 'prompt' || settings.type == 'confirm') { wrapper.appendChild(cancelBtn); } wrapper.classList.add('cdx-notification-' + settings.type); wrapper.dataset.type = settings.type; notification = wrapper; type = settings.type; confirm = settings.confirm; cancel = settings.cancel; inputField = input; if (settings.type != 'prompt' && settings.type != 'confirm') { window.setTimeout(close, settings.time); } }; /** * Show notification block */ function send() { editor.nodes.notifications.appendChild(notification); inputField.focus(); editor.nodes.notifications.classList.add('cdx-notification__notification-appending'); window.setTimeout(function () { editor.nodes.notifications.classList.remove('cdx-notification__notification-appending'); }, 100); addToQueue({ type: type, close: close }); }; /** * Remove notification block */ function close() { notification.remove(); }; if (constructorSettings) { create(constructorSettings); send(); } return { create: create, send: send, close: close }; }; notifications.clear = function () { editor.nodes.notifications.innerHTML = ''; queue = []; }; return notifications; }({}); /***/ }), /***/ "./src/components/modules/_parser.js": /*!*******************************************!*\ !*** ./src/components/modules/_parser.js ***! \*******************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /** * Codex Editor Parser Module * * @author Codex Team * @version 1.1 */ module.exports = function (parser) { var editor = codex.editor; /** inserting text */ parser.insertPastedContent = function (blockType, tag) { editor.content.insertBlock({ type: blockType.type, block: blockType.render({ text: tag.innerHTML }) }); }; /** * Check DOM node for display style: separated block or child-view */ parser.isFirstLevelBlock = function (node) { return node.nodeType == editor.core.nodeTypes.TAG && node.classList.contains(editor.ui.className.BLOCK_CLASSNAME); }; return parser; }({}); /***/ }), /***/ "./src/components/modules/_paste.js": /*!******************************************!*\ !*** ./src/components/modules/_paste.js ***! \******************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /** * Codex Editor Paste module * * @author Codex Team * @version 1.1.1 */ module.exports = function (paste) { var editor = codex.editor; var patterns = []; paste.prepare = function () { var tools = editor.tools; for (var tool in tools) { if (!tools[tool].renderOnPastePatterns || !Array.isArray(tools[tool].renderOnPastePatterns)) { continue; } tools[tool].renderOnPastePatterns.map(function (pattern) { patterns.push(pattern); }); } return Promise.resolve(); }; /** * Saves data * @param event */ paste.pasted = function (event) { var clipBoardData = event.clipboardData || window.clipboardData, content = clipBoardData.getData('Text'); var result = analize(content); if (result) { event.preventDefault(); event.stopImmediatePropagation(); } return result; }; /** * Analizes pated string and calls necessary method */ var analize = function analize(string) { var result = false, content = editor.content.currentNode, plugin = content.dataset.tool; patterns.map(function (pattern) { var execArray = pattern.regex.exec(string), match = execArray && execArray[0]; if (match && match === string.trim()) { /** current block is not empty */ if (content.textContent.trim() && plugin == editor.settings.initialBlockPlugin) { pasteToNewBlock_(); } pattern.callback(string, pattern); result = true; } }); return result; }; var pasteToNewBlock_ = function pasteToNewBlock_() { /** Create new initial block */ editor.content.insertBlock({ type: editor.settings.initialBlockPlugin, block: editor.tools[editor.settings.initialBlockPlugin].render({ text: '' }) }, false); }; /** * This method prevents default behaviour. * * @param {Object} event * @protected * * @description We get from clipboard pasted data, sanitize, make a fragment that contains of this sanitized nodes. * Firstly, we need to memorize the caret position. We can do that by getting the range of selection. * After all, we insert clear fragment into caret placed position. Then, we should move the caret to the last node */ paste.blockPasteCallback = function (event) { if (!needsToHandlePasteEvent(event.target)) { return; } /** Prevent default behaviour */ event.preventDefault(); /** get html pasted data - dirty data */ var htmlData = event.clipboardData.getData('text/html'), plainData = event.clipboardData.getData('text/plain'); /** Temporary DIV that is used to work with text's paragraphs as DOM-elements*/ var paragraphs = editor.draw.node('DIV', '', {}), cleanData, wrappedData; /** Create fragment, that we paste to range after proccesing */ cleanData = editor.sanitizer.clean(htmlData); /** * We wrap pasted text with

    tags to split it logically * @type {string} */ wrappedData = editor.content.wrapTextWithParagraphs(cleanData, plainData); paragraphs.innerHTML = wrappedData; /** * If there only one paragraph, just insert in at the caret location */ if (paragraphs.childNodes.length == 1) { emulateUserAgentBehaviour(paragraphs.firstChild); return; } insertPastedParagraphs(paragraphs.childNodes); }; /** * Checks if we should handle paste event on block * @param block * * @return {boolean} */ var needsToHandlePasteEvent = function needsToHandlePasteEvent(block) { /** If area is input or textarea then allow default behaviour */ if (editor.core.isNativeInput(block)) { return false; } var editableParent = editor.content.getEditableParent(block); /** Allow paste when event target placed in Editable element */ if (!editableParent) { return false; } return true; }; /** * Inserts new initial plugin blocks with data in paragraphs * * @param {Array} paragraphs - array of paragraphs (

    ) whit content, that should be inserted */ var insertPastedParagraphs = function insertPastedParagraphs(paragraphs) { var NEW_BLOCK_TYPE = editor.settings.initialBlockPlugin, currentNode = editor.content.currentNode; paragraphs.forEach(function (paragraph) { /** Don't allow empty paragraphs */ if (editor.core.isBlockEmpty(paragraph)) { return; } editor.content.insertBlock({ type: NEW_BLOCK_TYPE, block: editor.tools[NEW_BLOCK_TYPE].render({ text: paragraph.innerHTML }) }); editor.caret.inputIndex++; }); editor.caret.setToPreviousBlock(editor.caret.getCurrentInputIndex() + 1); /** * If there was no data in working node, remove it */ if (editor.core.isBlockEmpty(currentNode)) { currentNode.remove(); editor.ui.saveInputs(); } }; /** * Inserts node content at the caret position * * @param {Node} node - DOM node (could be DocumentFragment), that should be inserted at the caret location */ var emulateUserAgentBehaviour = function emulateUserAgentBehaviour(node) { var newNode; if (node.childElementCount) { newNode = document.createDocumentFragment(); node.childNodes.forEach(function (current) { if (!editor.core.isDomNode(current) && current.data.trim() === '') { return; } newNode.appendChild(current.cloneNode(true)); }); } else { newNode = document.createTextNode(node.textContent); } editor.caret.insertNode(newNode); }; return paste; }({}); /***/ }), /***/ "./src/components/modules/_transport.js": /*!**********************************************!*\ !*** ./src/components/modules/_transport.js ***! \**********************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /** * * Codex.Editor Transport Module * * @copyright 2017 Codex-Team * @version 1.2.0 */ module.exports = function (transport) { var editor = codex.editor; /** * @private {Object} current XmlHttpRequest instance */ var currentRequest = null; /** * @type {null} | {DOMElement} input - keeps input element in memory */ transport.input = null; /** * @property {Object} arguments - keep plugin settings and defined callbacks */ transport.arguments = null; /** * Prepares input element where will be files */ transport.prepare = function () { var input = editor.draw.node('INPUT', '', { type: 'file' }); editor.listeners.add(input, 'change', editor.transport.fileSelected); editor.transport.input = input; }; /** Clear input when files is uploaded */ transport.clearInput = function () { /** Remove old input */ transport.input = null; /** Prepare new one */ transport.prepare(); }; /** * Callback for file selection * @param {Event} event */ transport.fileSelected = function () { var input = this, i, files = input.files, formData = new FormData(); if (editor.transport.arguments.multiple === true) { for (i = 0; i < files.length; i++) { formData.append('files[]', files[i], files[i].name); } } else { formData.append('files', files[0], files[0].name); } currentRequest = editor.core.ajax({ type: 'POST', data: formData, url: editor.transport.arguments.url, beforeSend: editor.transport.arguments.beforeSend, success: editor.transport.arguments.success, error: editor.transport.arguments.error, progress: editor.transport.arguments.progress }); /** Clear input */ transport.clearInput(); }; /** * Use plugin callbacks * @protected * * @param {Object} args - can have : * @param {String} args.url - fetch URL * @param {Function} args.beforeSend - function calls before sending ajax * @param {Function} args.success - success callback * @param {Function} args.error - on error handler * @param {Function} args.progress - xhr onprogress handler * @param {Boolean} args.multiple - allow select several files * @param {String} args.accept - adds accept attribute */ transport.selectAndUpload = function (args) { transport.arguments = args; if (args.multiple === true) { transport.input.setAttribute('multiple', 'multiple'); } if (args.accept) { transport.input.setAttribute('accept', args.accept); } transport.input.click(); }; transport.abort = function () { currentRequest.abort(); currentRequest = null; }; return transport; }({}); /***/ }), /***/ "./src/components/modules/api-blocks.ts": /*!**********************************************!*\ !*** ./src/components/modules/api-blocks.ts ***! \**********************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(Module) { 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; }; }(); 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; } /** * @class BlocksAPI * provides with methods working with Block */ var BlocksAPI = function (_Module) { _inherits(BlocksAPI, _Module); /** * Save Editor config. API provides passed configuration to the Blocks */ function BlocksAPI(_ref) { var config = _ref.config; _classCallCheck(this, BlocksAPI); return _possibleConstructorReturn(this, (BlocksAPI.__proto__ || Object.getPrototypeOf(BlocksAPI)).call(this, { config: config })); } /** * Available methods * @return {IBlocksAPI} */ _createClass(BlocksAPI, [{ key: "getBlocksCount", /** * Returns Blocks count * @return {number} */ value: function getBlocksCount() { return this.Editor.BlockManager.blocks.length; } /** * Returns current block index * @return {number} */ }, { key: "getCurrentBlockIndex", value: function getCurrentBlockIndex() { return this.Editor.BlockManager.currentBlockIndex; } /** * Returns Current Block * @param {Number} index * * @return {Object} */ }, { key: "getBlockByIndex", value: function getBlockByIndex(index) { return this.Editor.BlockManager.getBlockByIndex(index); } /** * Call Block Manager method that swap Blocks * @param {number} fromIndex - position of first Block * @param {number} toIndex - position of second Block */ }, { key: "swap", value: function swap(fromIndex, toIndex) { this.Editor.BlockManager.swap(fromIndex, toIndex); /** * Move toolbar * DO not close the settings */ this.Editor.Toolbar.move(false); } /** * Deletes Block * @param blockIndex */ }, { key: "delete", value: function _delete(blockIndex) { this.Editor.BlockManager.removeBlock(blockIndex); /** * in case of last block deletion * Insert new initial empty block */ if (this.Editor.BlockManager.blocks.length === 0) { this.Editor.BlockManager.insert(); } /** * In case of deletion first block we need to set caret to the current Block */ if (this.Editor.BlockManager.currentBlockIndex === 0) { this.Editor.Caret.setToBlock(this.Editor.BlockManager.currentBlock); } else { this.Editor.Caret.navigatePrevious(true); } this.Editor.Toolbar.close(); } /** * Clear Editor's area */ }, { key: "clear", value: function clear() { this.Editor.BlockManager.clear(true); } /** * Fills Editor with Blocks data * @param {IInputOutputData} data — Saved Editor data */ }, { key: "render", value: function render(data) { this.Editor.BlockManager.clear(); this.Editor.Renderer.render(data.items); } }, { key: "methods", get: function get() { var _this2 = this; return { clear: function clear() { return _this2.clear(); }, render: function render(data) { return _this2.render(data); }, delete: function _delete() { return _this2.delete(); }, swap: function swap(fromIndex, toIndex) { return _this2.swap(fromIndex, toIndex); }, getBlockByIndex: function getBlockByIndex(index) { return _this2.getBlockByIndex(index); }, getCurrentBlockIndex: function getCurrentBlockIndex() { return _this2.getCurrentBlockIndex(); }, getBlocksCount: function getBlocksCount() { return _this2.getBlocksCount(); } }; } }]); return BlocksAPI; }(Module); BlocksAPI.displayName = "BlocksAPI"; exports.default = BlocksAPI; module.exports = exports["default"]; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../__module.ts */ "./src/components/__module.ts"))) /***/ }), /***/ "./src/components/modules/api-events.ts": /*!**********************************************!*\ !*** ./src/components/modules/api-events.ts ***! \**********************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(Module) { 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; }; }(); 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; } /** * @class EventsAPI * provides with methods working with Toolbar */ var EventsAPI = function (_Module) { _inherits(EventsAPI, _Module); /** * Save Editor config. API provides passed configuration to the Blocks */ function EventsAPI(_ref) { var config = _ref.config; _classCallCheck(this, EventsAPI); return _possibleConstructorReturn(this, (EventsAPI.__proto__ || Object.getPrototypeOf(EventsAPI)).call(this, { config: config })); } /** * Available methods * @return {IEventsAPI} */ _createClass(EventsAPI, [{ key: "on", /** * Subscribe on Events * @param {String} eventName * @param {Function} callback */ value: function on(eventName, callback) { this.Editor.Events.on(eventName, callback); } /** * Emit event with data * @param {String} eventName * @param {Object} data */ }, { key: "emit", value: function emit(eventName, data) { this.Editor.Events.emit(eventName, data); } /** * Unsubscribe from Event * @param {String} eventName * @param {Function} callback */ }, { key: "off", value: function off(eventName, callback) { this.Editor.Events.off(eventName, callback); } }, { key: "methods", get: function get() { var _this2 = this; return { emit: function emit(eventName, data) { return _this2.emit(eventName, data); }, off: function off(eventName, callback) { return _this2.off(eventName, callback); }, on: function on(eventName, callback) { return _this2.on(eventName, callback); } }; } }]); return EventsAPI; }(Module); EventsAPI.displayName = "EventsAPI"; exports.default = EventsAPI; module.exports = exports["default"]; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../__module.ts */ "./src/components/__module.ts"))) /***/ }), /***/ "./src/components/modules/api-listener.ts": /*!************************************************!*\ !*** ./src/components/modules/api-listener.ts ***! \************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(Module) { 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; }; }(); 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; } /** * @class API * Provides with methods working with DOM Listener */ var ListenerAPI = function (_Module) { _inherits(ListenerAPI, _Module); /** * Save Editor config. API provides passed configuration to the Blocks */ function ListenerAPI(_ref) { var config = _ref.config; _classCallCheck(this, ListenerAPI); return _possibleConstructorReturn(this, (ListenerAPI.__proto__ || Object.getPrototypeOf(ListenerAPI)).call(this, { config: config })); } /** * Available methods * @return {IToolbarAPI} */ _createClass(ListenerAPI, [{ key: "on", /** * adds DOM event listener * * @param {HTMLElement} element * @param {string} eventType * @param {() => void} handler * @param {boolean} useCapture */ value: function on(element, eventType, handler, useCapture) { this.Editor.Listeners.on(element, eventType, handler, useCapture); } /** * Removes DOM listener from element * * @param element * @param eventType * @param handler */ }, { key: "off", value: function off(element, eventType, handler) { this.Editor.Listeners.off(element, eventType, handler); } }, { key: "methods", get: function get() { var _this2 = this; return { on: function on(element, eventType, handler, useCapture) { return _this2.on(element, eventType, handler, useCapture); }, off: function off(element, eventType, handler) { return _this2.off(element, eventType, handler); } }; } }]); return ListenerAPI; }(Module); ListenerAPI.displayName = "ListenerAPI"; exports.default = ListenerAPI; module.exports = exports["default"]; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../__module.ts */ "./src/components/__module.ts"))) /***/ }), /***/ "./src/components/modules/api-sanitizer.ts": /*!*************************************************!*\ !*** ./src/components/modules/api-sanitizer.ts ***! \*************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(Module) { 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; }; }(); 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; } /** * @class API * Provides CodeX Editor Sanitizer that allows developers to clean their HTML */ var SanitizerAPI = function (_Module) { _inherits(SanitizerAPI, _Module); /** * Save Editor config. API provides passed configuration to the Blocks */ function SanitizerAPI(_ref) { var config = _ref.config; _classCallCheck(this, SanitizerAPI); return _possibleConstructorReturn(this, (SanitizerAPI.__proto__ || Object.getPrototypeOf(SanitizerAPI)).call(this, { config: config })); } /** * Available methods * @return {ISanitizerAPI} */ _createClass(SanitizerAPI, [{ key: "clean", value: function clean(taintString, config) { return this.Editor.Sanitizer.clean(taintString, config); } }, { key: "methods", get: function get() { var _this2 = this; return { clean: function clean(taintString, config) { return _this2.clean(taintString, config); } }; } }]); return SanitizerAPI; }(Module); SanitizerAPI.displayName = "SanitizerAPI"; exports.default = SanitizerAPI; module.exports = exports["default"]; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../__module.ts */ "./src/components/__module.ts"))) /***/ }), /***/ "./src/components/modules/api-saver.ts": /*!*********************************************!*\ !*** ./src/components/modules/api-saver.ts ***! \*********************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(Module) { 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; }; }(); 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; } /** * @class SaverAPI * provides with methods to save data */ var SaverAPI = function (_Module) { _inherits(SaverAPI, _Module); /** * Save Editor config. API provides passed configuration to the Blocks */ function SaverAPI(_ref) { var config = _ref.config; _classCallCheck(this, SaverAPI); return _possibleConstructorReturn(this, (SaverAPI.__proto__ || Object.getPrototypeOf(SaverAPI)).call(this, { config: config })); } /** * Available methods * @return {ISaverAPI} */ _createClass(SaverAPI, [{ key: "save", /** * Return Editor's data */ value: function save() { return this.Editor.Saver.save(); } }, { key: "methods", get: function get() { var _this2 = this; return { save: function save() { return _this2.save(); } }; } }]); return SaverAPI; }(Module); SaverAPI.displayName = "SaverAPI"; exports.default = SaverAPI; module.exports = exports["default"]; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../__module.ts */ "./src/components/__module.ts"))) /***/ }), /***/ "./src/components/modules/api-selection.ts": /*!*************************************************!*\ !*** ./src/components/modules/api-selection.ts ***! \*************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(Module) { 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 _selection = __webpack_require__(/*! ../selection */ "./src/components/selection.js"); var _selection2 = _interopRequireDefault(_selection); 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; } /** * @class API * Provides with methods working with Selection */ var SelectionAPI = function (_Module) { _inherits(SelectionAPI, _Module); /** * Save Editor config. API provides passed configuration to the Blocks */ function SelectionAPI(_ref) { var config = _ref.config; _classCallCheck(this, SelectionAPI); return _possibleConstructorReturn(this, (SelectionAPI.__proto__ || Object.getPrototypeOf(SelectionAPI)).call(this, { config: config })); } /** * Available methods * @return {ISelectionAPI} */ _createClass(SelectionAPI, [{ key: 'findParentTag', /** * Looks ahead from selection and find passed tag with class name * @param {string} tagName - tag to find * @param {string} className - tag's class name * @return {HTMLElement|null} */ value: function findParentTag(tagName, className) { return new _selection2.default().findParentTag(tagName, className); } /** * Expand selection to passed tag * @param {HTMLElement} node - tag that should contain selection */ }, { key: 'expandToTag', value: function expandToTag(node) { new _selection2.default().expandToTag(node); } }, { key: 'methods', get: function get() { var _this2 = this; return { findParentTag: function findParentTag(tagName, className) { return _this2.findParentTag(tagName, className); }, expandToTag: function expandToTag(node) { return _this2.expandToTag(node); } }; } }]); return SelectionAPI; }(Module); SelectionAPI.displayName = 'SelectionAPI'; exports.default = SelectionAPI; module.exports = exports['default']; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../__module.ts */ "./src/components/__module.ts"))) /***/ }), /***/ "./src/components/modules/api-toolbar.ts": /*!***********************************************!*\ !*** ./src/components/modules/api-toolbar.ts ***! \***********************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(Module) { 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; }; }(); 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; } /** * @class ToolbarsAPI * provides with methods working with Toolbar */ var ToolbarAPI = function (_Module) { _inherits(ToolbarAPI, _Module); /** * Save Editor config. API provides passed configuration to the Blocks */ function ToolbarAPI(_ref) { var config = _ref.config; _classCallCheck(this, ToolbarAPI); return _possibleConstructorReturn(this, (ToolbarAPI.__proto__ || Object.getPrototypeOf(ToolbarAPI)).call(this, { config: config })); } /** * Available methods * @return {IToolbarAPI} */ _createClass(ToolbarAPI, [{ key: "open", /** * Open toolbar */ value: function open() { this.Editor.Toolbar.open(); } /** * Close toolbar and all included elements */ }, { key: "close", value: function close() { this.Editor.Toolbar.close(); } }, { key: "methods", get: function get() { var _this2 = this; return { close: function close() { return _this2.close(); }, open: function open() { return _this2.open(); } }; } }]); return ToolbarAPI; }(Module); ToolbarAPI.displayName = "ToolbarAPI"; exports.default = ToolbarAPI; module.exports = exports["default"]; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../__module.ts */ "./src/components/__module.ts"))) /***/ }), /***/ "./src/components/modules/api.ts": /*!***************************************!*\ !*** ./src/components/modules/api.ts ***! \***************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(Module) { 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; }; }(); 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; } /** * @class API */ var API = function (_Module) { _inherits(API, _Module); /** * Save Editor config. API provides passed configuration to the Blocks * @param {EditorConfig} config */ function API(_ref) { var config = _ref.config; _classCallCheck(this, API); return _possibleConstructorReturn(this, (API.__proto__ || Object.getPrototypeOf(API)).call(this, { config: config })); } _createClass(API, [{ key: "methods", get: function get() { return { blocks: this.Editor.BlocksAPI.methods, caret: {}, events: this.Editor.EventsAPI.methods, listener: this.Editor.ListenerAPI.methods, sanitizer: this.Editor.SanitizerAPI.methods, saver: this.Editor.SaverAPI.methods, selection: this.Editor.SelectionAPI.methods, toolbar: this.Editor.ToolbarAPI.methods }; } }]); return API; }(Module); API.displayName = "API"; exports.default = API; module.exports = exports["default"]; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../__module.ts */ "./src/components/__module.ts"))) /***/ }), /***/ "./src/components/modules/block-events.ts": /*!************************************************!*\ !*** ./src/components/modules/block-events.ts ***! \************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(Module, _) { 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; }; }(); 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 BlockEvents = function (_Module) { _inherits(BlockEvents, _Module); /** * @constructor */ function BlockEvents(_ref) { var config = _ref.config; _classCallCheck(this, BlockEvents); return _possibleConstructorReturn(this, (BlockEvents.__proto__ || Object.getPrototypeOf(BlockEvents)).call(this, { config: config })); } /** * All keydowns on Block * @param {KeyboardEvent} event - keydown */ _createClass(BlockEvents, [{ key: "keydown", value: function keydown(event) { /** * Run common method for all keydown events */ this.beforeKeydownProcessing(); /** * Fire keydown processor by event.keyCode */ switch (event.keyCode) { case _.keyCodes.BACKSPACE: this.backspace(event); break; case _.keyCodes.ENTER: this.enter(event); break; case _.keyCodes.DOWN: case _.keyCodes.RIGHT: this.arrowRightAndDown(); break; case _.keyCodes.UP: case _.keyCodes.LEFT: this.arrowLeftAndUp(); break; default: this.defaultHandler(); break; } } /** * Fires on keydown before event processing */ }, { key: "beforeKeydownProcessing", value: function beforeKeydownProcessing() { /** * Clear all highlightings */ this.Editor.BlockManager.clearHighlightings(); /** * Hide Toolbar */ this.Editor.Toolbar.close(); } /** * Key up on Block: * - shows Inline Toolbar if something selected */ }, { key: "keyup", value: function keyup(event) { this.Editor.InlineToolbar.handleShowingEvent(event); } /** * Mouse up on Block: * - shows Inline Toolbar if something selected */ }, { key: "mouseUp", value: function mouseUp(event) { this.Editor.InlineToolbar.handleShowingEvent(event); } /** * ENTER pressed on block * @param {KeyboardEvent} event - keydown */ }, { key: "enter", value: function enter(event) { var currentBlock = this.Editor.BlockManager.currentBlock, toolsConfig = this.config.toolsConfig[currentBlock.name]; /** * Don't handle Enter keydowns when Tool sets enableLineBreaks to true. * Uses for Tools like where line breaks should be handled by default behaviour. */ if (toolsConfig && toolsConfig[this.Editor.Tools.apiSettings.IS_ENABLED_LINE_BREAKS]) { return; } /** * Allow to create linebreaks by Shift+Enter */ if (event.shiftKey) { return; } /** * Split the Current Block into two blocks */ this.Editor.BlockManager.split(); /** * Renew local current node after split */ var newCurrent = this.Editor.BlockManager.currentBlock; this.Editor.Toolbar.move(); /** * If new Block is empty */ if (this.Editor.Tools.isInitial(newCurrent.tool) && newCurrent.isEmpty) { /** * Show Toolbar */ this.Editor.Toolbar.open(); /** * Show Plus Button */ this.Editor.Toolbar.plusButton.show(); } event.preventDefault(); } /** * Handle backspace keydown on Block * @param {KeyboardEvent} event - keydown */ }, { key: "backspace", value: function backspace(event) { var _this2 = this; var BM = this.Editor.BlockManager; var isFirstBlock = BM.currentBlockIndex === 0, canMergeBlocks = this.Editor.Caret.isAtStart && !isFirstBlock; /** If current Block is empty just remove this Block */ if (this.Editor.BlockManager.currentBlock.isEmpty) { this.Editor.BlockManager.removeBlock(); if (this.Editor.Caret.navigatePrevious(true)) { this.Editor.Toolbar.close(); } return; } if (!canMergeBlocks) { return; } // preventing browser default behaviour event.preventDefault(); var targetBlock = BM.getBlockByIndex(BM.currentBlockIndex - 1), blockToMerge = BM.currentBlock; /** * Blocks that can be merged: * 1) with the same Name * 2) Tool has 'merge' method * * other case will handle as usual ARROW LEFT behaviour */ if (blockToMerge.name !== targetBlock.name || !targetBlock.mergeable) { if (this.Editor.Caret.navigatePrevious()) { this.Editor.Toolbar.close(); } return; } this.Editor.Caret.createShadow(targetBlock.pluginsContent); BM.mergeBlocks(targetBlock, blockToMerge).then(function () { /** Restore caret position after merge */ _this2.Editor.Caret.restoreCaret(targetBlock.pluginsContent); targetBlock.pluginsContent.normalize(); _this2.Editor.Toolbar.close(); }); } /** * Handle right and down keyboard keys */ }, { key: "arrowRightAndDown", value: function arrowRightAndDown() { this.Editor.Caret.navigateNext(); } /** * Handle left and up keyboard keys */ }, { key: "arrowLeftAndUp", value: function arrowLeftAndUp() { this.Editor.Caret.navigatePrevious(); } /** * Default keydown handler */ }, { key: "defaultHandler", value: function defaultHandler() {} }]); return BlockEvents; }(Module); BlockEvents.displayName = "BlockEvents"; exports.default = BlockEvents; module.exports = exports["default"]; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../__module.ts */ "./src/components/__module.ts"), __webpack_require__(/*! utils */ "./src/components/utils.js"))) /***/ }), /***/ "./src/components/modules/blockManager.js": /*!************************************************!*\ !*** ./src/components/modules/blockManager.js ***! \************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(Module, $, _) { 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 _block = __webpack_require__(/*! ../block */ "./src/components/block.js"); var _block2 = _interopRequireDefault(_block); 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; } /** * @class BlockManager * @classdesc Manage editor`s blocks storage and appearance * * @module BlockManager * * @version 2.0.0 */ /** * @typedef {BlockManager} BlockManager * @property {Number} currentBlockIndex - Index of current working block * @property {Proxy} _blocks - Proxy for Blocks instance {@link Blocks} */ var BlockManager = function (_Module) { _inherits(BlockManager, _Module); /** * @constructor * @param {EditorConfig} config */ function BlockManager(_ref) { var config = _ref.config; _classCallCheck(this, BlockManager); /** * Proxy for Blocks instance {@link Blocks} * * @type {Proxy} * @private */ var _this = _possibleConstructorReturn(this, (BlockManager.__proto__ || Object.getPrototypeOf(BlockManager)).call(this, { config: config })); _this._blocks = null; /** * Index of current working block * * @type {number} * @private */ _this.currentBlockIndex = -1; return _this; } /** * Should be called after Editor.UI preparation * Define this._blocks property * * @returns {Promise} */ _createClass(BlockManager, [{ key: 'prepare', value: function prepare() { var _this2 = this; return new Promise(function (resolve) { var blocks = new Blocks(_this2.Editor.UI.nodes.redactor); /** * We need to use Proxy to overload set/get [] operator. * So we can use array-like syntax to access blocks * * @example * this._blocks[0] = new Block(...); * * block = this._blocks[0]; * * @todo proxy the enumerate method * * @type {Proxy} * @private */ _this2._blocks = new Proxy(blocks, { set: Blocks.set, get: Blocks.get }); resolve(); }); } /** * Creates Block instance by tool name * * @param {String} toolName - tools passed in editor config {@link EditorConfig#tools} * @param {Object} data - constructor params * @param {Object} settings - block settings * * @return {Block} */ }, { key: 'composeBlock', value: function composeBlock(toolName, data, settings) { var toolInstance = this.Editor.Tools.construct(toolName, data), block = new _block2.default(toolName, toolInstance, settings, this.Editor.API.methods); this.bindEvents(block); /** * Apply callback before inserting html */ block.call('appendCallback', {}); return block; } /** * Bind Events * @param {Object} block */ }, { key: 'bindEvents', value: function bindEvents(block) { var _this3 = this; this.Editor.Listeners.on(block.holder, 'keydown', function (event) { return _this3.Editor.BlockEvents.keydown(event); }); this.Editor.Listeners.on(block.holder, 'mouseup', function (event) { return _this3.Editor.BlockEvents.mouseUp(event); }); this.Editor.Listeners.on(block.holder, 'keyup', function (event) { return _this3.Editor.BlockEvents.keyup(event); }); } /** * Insert new block into _blocks * * @param {String} toolName — plugin name, by default method inserts initial block type * @param {Object} data — plugin data * @param {Object} settings - default settings * * @return {Block} */ }, { key: 'insert', value: function insert() { var toolName = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.config.initialBlock; var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var settings = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var block = this.composeBlock(toolName, data, settings); this._blocks[++this.currentBlockIndex] = block; this.Editor.Caret.setToBlock(block); return block; } /** * Always inserts at the end */ }, { key: 'insertAtEnd', value: function insertAtEnd() { /** * Define new value for current block index */ this.currentBlockIndex = this.blocks.length - 1; /** * Insert initial typed block */ this.insert(); } /** * Merge two blocks * @param {Block} targetBlock - previous block will be append to this block * @param {Block} blockToMerge - block that will be merged with target block * * @return {Promise} - the sequence that can be continued */ }, { key: 'mergeBlocks', value: function mergeBlocks(targetBlock, blockToMerge) { var _this4 = this; var blockToMergeIndex = this._blocks.indexOf(blockToMerge); return Promise.resolve().then(function () { if (blockToMerge.isEmpty) { return; } return blockToMerge.data.then(function (blockToMergeInfo) { targetBlock.mergeWith(blockToMergeInfo.data); }); }).then(function () { _this4.removeBlock(blockToMergeIndex); _this4.currentBlockIndex = _this4._blocks.indexOf(targetBlock); }); } /** * Remove block with passed index or remove last * @param {Number|null} index */ }, { key: 'removeBlock', value: function removeBlock(index) { if (!index) { index = this.currentBlockIndex; } this._blocks.remove(index); } /** * Split current Block * 1. Extract content from Caret position to the Block`s end * 2. Insert a new Block below current one with extracted content */ }, { key: 'split', value: function split() { var extractedFragment = this.Editor.Caret.extractFragmentFromCaretPosition(), wrapper = $.make('div'); wrapper.append(extractedFragment); /** * @todo make object in accordance with Tool */ var data = { text: $.isEmpty(wrapper) ? '' : wrapper.innerHTML }; /** * Renew current Block * @type {Block} */ var blockInserted = this.insert(this.config.initialBlock, data); this.currentNode = blockInserted.pluginsContent; } /** * Replace current working block * * @param {String} toolName — plugin name * @param {Object} data — plugin data */ }, { key: 'replace', value: function replace(toolName) { var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var block = this.composeBlock(toolName, data); this._blocks.insert(this.currentBlockIndex, block, true); } /** * returns last Block * @return {Block} */ }, { key: 'getBlockByIndex', /** * Returns Block by passed index * @param {Number} index * @return {Block} */ value: function getBlockByIndex(index) { return this._blocks[index]; } /** * Get Block instance by html element * @param {Node} element * @returns {Block} */ }, { key: 'getBlock', value: function getBlock(element) { if (!$.isElement(element)) { element = element.parentNode; } var nodes = this._blocks.nodes, firstLevelBlock = element.closest('.' + _block2.default.CSS.wrapper), index = nodes.indexOf(firstLevelBlock); if (index >= 0) { return this._blocks[index]; } } /** * Get current Block instance * * @return {Block} */ }, { key: 'highlightCurrentNode', /** * Remove selection from all Blocks then highlight only Current Block */ value: function highlightCurrentNode() { /** * Remove previous selected Block's state */ this.clearHighlightings(); /** * Mark current Block as selected * @type {boolean} */ this.currentBlock.selected = true; } /** * Remove selection from all Blocks */ }, { key: 'clearHighlightings', value: function clearHighlightings() { this.blocks.forEach(function (block) { return block.selected = false; }); } /** * Get array of Block instances * * @returns {Block[]} {@link Blocks#array} */ }, { key: 'setCurrentBlockByChildNode', /** * 1) Find first-level Block from passed child Node * 2) Mark it as current * * @param {Element|Text} childNode - look ahead from this node. * @throws Error - when passed Node is not included at the Block */ value: function setCurrentBlockByChildNode(childNode) { /** * If node is Text TextNode */ if (!$.isElement(childNode)) { childNode = childNode.parentNode; } var parentFirstLevelBlock = childNode.closest('.' + _block2.default.CSS.wrapper); if (parentFirstLevelBlock) { this.currentNode = parentFirstLevelBlock; } else { throw new Error('Can not find a Block from this child Node'); } } /** * Swap Blocks Position * @param {Number} fromIndex * @param {Number} toIndex */ }, { key: 'swap', value: function swap(fromIndex, toIndex) { /** Move up current Block */ this._blocks.swap(fromIndex, toIndex); /** Now actual block moved up so that current block index decreased */ this.currentBlockIndex = toIndex; } /** * Sets current Block Index -1 which means unknown * and clear highlightings */ }, { key: 'dropPointer', value: function dropPointer() { this.currentBlockIndex = -1; this.clearHighlightings(); } /** * Clears Editor * @param {boolean} needAddInitialBlock - 1) in internal calls (for example, in api.blocks.render) * we don't need to add empty initial block * 2) in api.blocks.clear we should add empty block */ }, { key: 'clear', value: function clear() { var needAddInitialBlock = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; this._blocks.removeAll(); this.dropPointer(); if (needAddInitialBlock) { this.insert(this.config.initialBlock); } } }, { key: 'lastBlock', get: function get() { return this._blocks[this._blocks.length - 1]; } }, { key: 'currentBlock', get: function get() { return this._blocks[this.currentBlockIndex]; } /** * Returns next Block instance * @return {Block|null} */ }, { key: 'nextBlock', get: function get() { var isLastBlock = this.currentBlockIndex === this._blocks.length - 1; if (isLastBlock) { return null; } return this._blocks[this.currentBlockIndex + 1]; } /** * Returns previous Block instance * @return {Block|null} */ }, { key: 'previousBlock', get: function get() { var isFirstBlock = this.currentBlockIndex === 0; if (isFirstBlock) { return null; } return this._blocks[this.currentBlockIndex - 1]; } /** * Get working html element * * @return {HTMLElement} */ }, { key: 'currentNode', get: function get() { return this._blocks.nodes[this.currentBlockIndex]; } /** * Set currentBlockIndex to passed block * @param {Node} element */ , set: function set(element) { var nodes = this._blocks.nodes, firstLevelBlock = element.closest('.' + _block2.default.CSS.wrapper); /** * Update current Block's index * @type {number} */ this.currentBlockIndex = nodes.indexOf(firstLevelBlock); } }, { key: 'blocks', get: function get() { return this._blocks.array; } }]); return BlockManager; }(Module); BlockManager.displayName = 'BlockManager'; exports.default = BlockManager; ; /** * @class Blocks * @classdesc Class to work with Block instances array * * @private * * @property {HTMLElement} workingArea — editor`s working node * */ var Blocks = function () { /** * @constructor * * @param {HTMLElement} workingArea — editor`s working node */ function Blocks(workingArea) { _classCallCheck(this, Blocks); this.blocks = []; this.workingArea = workingArea; } /** * Push back new Block * * @param {Block} block */ _createClass(Blocks, [{ key: 'push', value: function push(block) { this.blocks.push(block); this.workingArea.appendChild(block.holder); } /** * Swaps blocks with indexes first and second * @param {Number} first - first block index * @param {Number} second - second block index */ }, { key: 'swap', value: function swap(first, second) { var secondBlock = this.blocks[second]; /** * Change in DOM */ $.swap(this.blocks[first].holder, secondBlock.holder); /** * Change in array */ this.blocks[second] = this.blocks[first]; this.blocks[first] = secondBlock; } /** * Insert new Block at passed index * * @param {Number} index — index to insert Block * @param {Block} block — Block to insert * @param {Boolean} replace — it true, replace block on given index */ }, { key: 'insert', value: function insert(index, block) { var replace = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; if (!this.length) { this.push(block); return; } if (index > this.length) { index = this.length; } if (replace) { this.blocks[index].holder.remove(); } var deleteCount = replace ? 1 : 0; this.blocks.splice(index, deleteCount, block); if (index > 0) { var previousBlock = this.blocks[index - 1]; previousBlock.holder.insertAdjacentElement('afterend', block.holder); } else { var nextBlock = this.blocks[index + 1]; if (nextBlock) { nextBlock.holder.insertAdjacentElement('beforebegin', block.holder); } else { this.workingArea.appendChild(block.holder); } } } /** * Remove block * @param {Number|null} index */ }, { key: 'remove', value: function remove(index) { if (isNaN(index)) { index = this.length - 1; } this.blocks[index].holder.remove(); this.blocks.splice(index, 1); } /** * Remove all blocks */ }, { key: 'removeAll', value: function removeAll() { this.workingArea.innerHTML = ''; this.blocks.length = 0; } /** * Insert Block after passed target * * @todo decide if this method is necessary * * @param {Block} targetBlock — target after wich Block should be inserted * @param {Block} newBlock — Block to insert */ }, { key: 'insertAfter', value: function insertAfter(targetBlock, newBlock) { var index = this.blocks.indexOf(targetBlock); this.insert(index + 1, newBlock); } /** * Get Block by index * * @param {Number} index — Block index * @returns {Block} */ }, { key: 'get', value: function get(index) { return this.blocks[index]; } /** * Return index of passed Block * * @param {Block} block * @returns {Number} */ }, { key: 'indexOf', value: function indexOf(block) { return this.blocks.indexOf(block); } /** * Get length of Block instances array * * @returns {Number} */ }, { key: 'length', get: function get() { return this.blocks.length; } /** * Get Block instances array * * @returns {Block[]} */ }, { key: 'array', get: function get() { return this.blocks; } /** * Get blocks html elements array * * @returns {HTMLElement[]} */ }, { key: 'nodes', get: function get() { return _.array(this.workingArea.children); } /** * Proxy trap to implement array-like setter * * @example * blocks[0] = new Block(...) * * @param {Blocks} instance — Blocks instance * @param {Number|String} index — block index * @param {Block} block — Block to set * @returns {Boolean} */ }], [{ key: 'set', value: function set(instance, index, block) { if (isNaN(Number(index))) { return false; } instance.insert(index, block); return true; } /** * Proxy trap to implement array-like getter * * @param {Blocks} instance — Blocks instance * @param {Number|String} index — Block index * @returns {Block|*} */ }, { key: 'get', value: function get(instance, index) { if (isNaN(Number(index))) { return instance[index]; } return instance.get(index); } }]); return Blocks; }(); Blocks.displayName = 'Blocks'; module.exports = exports['default']; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../__module.ts */ "./src/components/__module.ts"), __webpack_require__(/*! dom */ "./src/components/dom.js"), __webpack_require__(/*! utils */ "./src/components/utils.js"))) /***/ }), /***/ "./src/components/modules/caret.js": /*!*****************************************!*\ !*** ./src/components/modules/caret.js ***! \*****************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(Module, $, _) { 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 _selection = __webpack_require__(/*! ../selection */ "./src/components/selection.js"); var _selection2 = _interopRequireDefault(_selection); 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; } /** * @class Caret * @classdesc Contains methods for working Caret * * Uses Range methods to manipulate with caret * * @module Caret * * @version 2.0.0 */ /** * @typedef {Caret} Caret */ var Caret = function (_Module) { _inherits(Caret, _Module); /** * @constructor */ function Caret(_ref) { var config = _ref.config; _classCallCheck(this, Caret); return _possibleConstructorReturn(this, (Caret.__proto__ || Object.getPrototypeOf(Caret)).call(this, { config: config })); } /** * Elements styles that can be useful for Caret Module */ _createClass(Caret, [{ key: 'setToBlock', /** * Method gets Block instance and puts caret to the text node with offset * There two ways that method applies caret position: * - first found text node: sets at the beginning, but you can pass an offset * - last found text node: sets at the end of the node. Also, you can customize the behaviour * * @param {Block} block - Block class * @param {Number} offset - caret offset regarding to the text node * @param {Boolean} atEnd - put caret at the end of the text node or not */ value: function setToBlock(block) { var _this2 = this; var offset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; var atEnd = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; var element = block.pluginsContent; /** If Element is INPUT */ if ($.isNativeInput(element)) { element.focus(); return; } var nodeToSet = $.getDeepestNode(element, atEnd); if (atEnd || offset > nodeToSet.length) { offset = nodeToSet.length; } /** if found deepest node is native input */ if ($.isNativeInput(nodeToSet)) { nodeToSet.focus(); return; } /** * @todo try to fix via Promises or use querySelectorAll to not to use timeout */ _.delay(function () { _this2.set(nodeToSet, offset); }, 20)(); this.Editor.BlockManager.currentNode = block.holder; } /** * Creates Document Range and sets caret to the element with offset * @param {Element} element - target node. * @param {Number} offset - offset */ }, { key: 'set', value: function set(element) { var offset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; var range = document.createRange(), selection = _selection2.default.get(); range.setStart(element, offset); range.setEnd(element, offset); selection.removeAllRanges(); selection.addRange(range); } }, { key: 'setToTheLastBlock', /** * Set Caret to the last Block * If last block is not empty, append another empty block */ value: function setToTheLastBlock() { var lastBlock = this.Editor.BlockManager.lastBlock; if (!lastBlock) return; /** * If last block is empty and it is an initialBlock, set to that. * Otherwise, append new empty block and set to that */ if (lastBlock.isEmpty) { this.setToBlock(lastBlock); } else { this.Editor.BlockManager.insertAtEnd(); } } /** * Extract content fragment of current Block from Caret position to the end of the Block */ }, { key: 'extractFragmentFromCaretPosition', value: function extractFragmentFromCaretPosition() { var selection = _selection2.default.get(); if (selection.rangeCount) { var selectRange = selection.getRangeAt(0), blockElem = this.Editor.BlockManager.currentBlock.pluginsContent; selectRange.deleteContents(); if (blockElem) { var range = selectRange.cloneRange(true); range.selectNodeContents(blockElem); range.setStart(selectRange.endContainer, selectRange.endOffset); return range.extractContents(); } } } /** * Get all first-level (first child of [contenteditabel]) siblings from passed node * Then you can check it for emptiness * * @example *
    * * @return {Element[]} */ }, { key: 'getHigherLevelSiblings', value: function getHigherLevelSiblings(from, direction) { var current = from, siblings = []; /** * Find passed node's firs-level parent (in example - blockquote) */ while (current.parentNode && current.parentNode.contentEditable !== 'true') { current = current.parentNode; } var sibling = direction === 'left' ? 'previousSibling' : 'nextSibling'; /** * Find all left/right siblings */ while (current[sibling]) { current = current[sibling]; siblings.push(current); } return siblings; } /** * Set's caret to the next Block * Before moving caret, we should check if caret position is at the end of Plugins node * Using {@link Dom#getDeepestNode} to get a last node and match with current selection * * @param {Boolean} force - force navigation even if caret is not at the end * * @return {Boolean} */ }, { key: 'navigateNext', value: function navigateNext() { var force = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; var nextBlock = this.Editor.BlockManager.nextBlock; if (!nextBlock) { return false; } if (force || this.isAtEnd) { this.setToBlock(nextBlock); return true; } return false; } /** * Set's caret to the previous Block * Before moving caret, we should check if caret position is start of the Plugins node * Using {@link Dom#getDeepestNode} to get a last node and match with current selection * * @param {Boolean} force - force navigation even if caret is not at the start * * @return {Boolean} */ }, { key: 'navigatePrevious', value: function navigatePrevious() { var force = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; var previousBlock = this.Editor.BlockManager.previousBlock; if (!previousBlock) { return false; } if (force || this.isAtStart) { this.setToBlock(previousBlock, 0, true); return true; } return false; } /** * Get's deepest first node and checks if offset is zero * @return {boolean} */ }, { key: 'createShadow', /** * Inserts shadow element after passed element where caret can be placed * @param {Node} element */ value: function createShadow(element) { var shadowCaret = document.createElement('span'); shadowCaret.classList.add(Caret.CSS.shadowCaret); element.insertAdjacentElement('beforeEnd', shadowCaret); } /** * Restores caret position * @param {Node} element */ }, { key: 'restoreCaret', value: function restoreCaret(element) { var shadowCaret = element.querySelector('.' + Caret.CSS.shadowCaret); if (!shadowCaret) { return; } /** * After we set the caret to the required place * we need to clear shadow caret * * - make new range * - select shadowed span * - use extractContent to remove it from DOM */ var sel = new _selection2.default(); sel.expandToTag(shadowCaret); setTimeout(function () { var newRange = document.createRange(); newRange.selectNode(shadowCaret); newRange.extractContents(); }, 50); } }, { key: 'isAtStart', get: function get() { /** * Don't handle ranges */ if (!_selection2.default.isCollapsed) { return false; } var selection = _selection2.default.get(), anchorNode = selection.anchorNode, firstNode = $.getDeepestNode(this.Editor.BlockManager.currentBlock.pluginsContent); /** * Workaround case when caret in the text like " |Hello!" * selection.anchorOffset is 1, but real caret visible position is 0 * @type {number} */ var firstLetterPosition = anchorNode.textContent.search(/\S/); if (firstLetterPosition === -1) { // empty text firstLetterPosition = 0; } /** * In case of *
    *

    <-- first (and deepest) node is * |adaddad <-- anchor node *
    */ if ($.isEmpty(firstNode)) { var leftSiblings = this.getHigherLevelSiblings(anchorNode, 'left'), nothingAtLeft = leftSiblings.every(function (node) { return $.isEmpty(node); }); if (nothingAtLeft && selection.anchorOffset === firstLetterPosition) { return true; } } /** * We use <= comparison for case: * "| Hello" <--- selection.anchorOffset is 0, but firstLetterPosition is 1 */ return firstNode === null || anchorNode === firstNode && selection.anchorOffset <= firstLetterPosition; } /** * Get's deepest last node and checks if offset is last node text length * @return {boolean} */ }, { key: 'isAtEnd', get: function get() { /** * Don't handle ranges */ if (!_selection2.default.isCollapsed) { return false; } var selection = _selection2.default.get(), anchorNode = selection.anchorNode, lastNode = $.getDeepestNode(this.Editor.BlockManager.currentBlock.pluginsContent, true); /** * In case of *
    * adaddad| <-- anchor node *

    <-- first (and deepest) node is *
    */ if ($.isEmpty(lastNode)) { var leftSiblings = this.getHigherLevelSiblings(anchorNode, 'right'), nothingAtRight = leftSiblings.every(function (node) { return $.isEmpty(node); }); if (nothingAtRight && selection.anchorOffset === anchorNode.textContent.length) { return true; } } /** * Workaround case: * hello | <--- anchorOffset will be 5, but textContent.length will be 6. * Why not regular .trim(): * in case of ' hello |' trim() will also remove space at the beginning, so length will be lower than anchorOffset */ var rightTrimmedText = lastNode.textContent.replace(/\s+$/, ''); /** * We use >= comparison for case: * "Hello |" <--- selection.anchorOffset is 7, but rightTrimmedText is 6 */ return anchorNode === lastNode && selection.anchorOffset >= rightTrimmedText.length; } }], [{ key: 'CSS', get: function get() { return { shadowCaret: 'cdx-shadow-caret' }; } }]); return Caret; }(Module); Caret.displayName = 'Caret'; exports.default = Caret; module.exports = exports['default']; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../__module.ts */ "./src/components/__module.ts"), __webpack_require__(/*! dom */ "./src/components/dom.js"), __webpack_require__(/*! utils */ "./src/components/utils.js"))) /***/ }), /***/ "./src/components/modules/events.js": /*!******************************************!*\ !*** ./src/components/modules/events.js ***! \******************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(Module) { 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; }; }(); 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; } /** * @module eventDispatcher * * Has two important methods: * - {Function} on - appends subscriber to the event. If event doesn't exist - creates new one * - {Function} emit - fires all subscribers with data * - {Function off - unsubsribes callback * * @version 1.0.0 * * @typedef {Events} Events * @property {Object} subscribers - all subscribers grouped by event name */ var Events = function (_Module) { _inherits(Events, _Module); /** * @constructor */ function Events(_ref) { var config = _ref.config; _classCallCheck(this, Events); var _this = _possibleConstructorReturn(this, (Events.__proto__ || Object.getPrototypeOf(Events)).call(this, { config: config })); _this.subscribers = {}; return _this; } /** * Subscribe any event on callback * * @param {String} eventName - event name * @param {Function} callback - subscriber */ _createClass(Events, [{ key: "on", value: function on(eventName, callback) { if (!(eventName in this.subscribers)) { this.subscribers[eventName] = []; } // group by events this.subscribers[eventName].push(callback); } /** * Emit callbacks with passed data * * @param {String} eventName - event name * @param {Object} data - subscribers get this data when they were fired */ }, { key: "emit", value: function emit(eventName, data) { if (!this.subscribers[eventName]) { return; } this.subscribers[eventName].reduce(function (previousData, currentHandler) { var newData = currentHandler(previousData); return newData ? newData : previousData; }, data); } /** * Unsubsribe callback from event * * @param eventName * @param callback */ }, { key: "off", value: function off(eventName, callback) { for (var i = 0; i < this.subscribers[eventName].length; i++) { if (this.subscribers[eventName][i] === callback) { delete this.subscribers[eventName][i]; break; } } } /** * Destroyer * clears subsribers list */ }, { key: "destroy", value: function destroy() { this.subscribers = null; } }]); return Events; }(Module); Events.displayName = "Events"; exports.default = Events; module.exports = exports["default"]; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../__module.ts */ "./src/components/__module.ts"))) /***/ }), /***/ "./src/components/modules/listeners.js": /*!*********************************************!*\ !*** ./src/components/modules/listeners.js ***! \*********************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(Module) { 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; }; }(); 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; } /** * Codex Editor Listeners module * * @module Listeners * * Module-decorator for event listeners assignment * * @author Codex Team * @version 2.0.0 */ /** * @typedef {Listeners} Listeners * @property {Array} allListeners */ var Listeners = function (_Module) { _inherits(Listeners, _Module); /** * @constructor * @param {EditorConfig} config */ function Listeners(_ref) { var config = _ref.config; _classCallCheck(this, Listeners); var _this = _possibleConstructorReturn(this, (Listeners.__proto__ || Object.getPrototypeOf(Listeners)).call(this, { config: config })); _this.allListeners = []; return _this; } /** * Assigns event listener on element * * @param {Element} element - DOM element that needs to be listened * @param {String} eventType - event type * @param {Function} handler - method that will be fired on event * @param {Boolean} useCapture - use event bubbling */ _createClass(Listeners, [{ key: "on", value: function on(element, eventType, handler) { var useCapture = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; var assignedEventData = { element: element, eventType: eventType, handler: handler, useCapture: useCapture }; var alreadyExist = this.findOne(element, eventType, handler); if (alreadyExist) return; this.allListeners.push(assignedEventData); element.addEventListener(eventType, handler, useCapture); } /** * Removes event listener from element * * @param {Element} element - DOM element that we removing listener * @param {String} eventType - event type * @param {Function} handler - remove handler, if element listens several handlers on the same event type * @param {Boolean} useCapture - use event bubbling */ }, { key: "off", value: function off(element, eventType, handler) { var useCapture = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; var existingListeners = this.findAll(element, eventType, handler); for (var i = 0; i < existingListeners.length; i++) { var index = this.allListeners.indexOf(existingListeners[i]); if (index > 0) { this.allListeners.splice(index, 1); } } element.removeEventListener(eventType, handler, useCapture); } /** * Search method: looks for listener by passed element * @param {Element} element - searching element * @returns {Array} listeners that found on element */ }, { key: "findByElement", value: function findByElement(element) { var listenersOnElement = []; for (var i = 0; i < this.allListeners.length; i++) { var listener = this.allListeners[i]; if (listener.element === element) { listenersOnElement.push(listener); } } return listenersOnElement; } /** * Search method: looks for listener by passed event type * @param {String} eventType * @return {Array} listeners that found on element */ }, { key: "findByType", value: function findByType(eventType) { var listenersWithType = []; for (var i = 0; i < this.allListeners.length; i++) { var listener = this.allListeners[i]; if (listener.type === eventType) { listenersWithType.push(listener); } } return listenersWithType; } /** * Search method: looks for listener by passed handler * @param {Function} handler * @return {Array} listeners that found on element */ }, { key: "findByHandler", value: function findByHandler(handler) { var listenersWithHandler = []; for (var i = 0; i < this.allListeners.length; i++) { var listener = this.allListeners[i]; if (listener.handler === handler) { listenersWithHandler.push(listener); } } return listenersWithHandler; } /** * @param {Element} element * @param {String} eventType * @param {Function} handler * @return {Element|null} */ }, { key: "findOne", value: function findOne(element, eventType, handler) { var foundListeners = this.findAll(element, eventType, handler); return foundListeners.length > 0 ? foundListeners[0] : null; } /** * @param {Element} element * @param {String} eventType * @param {Function} handler * @return {Array} */ }, { key: "findAll", value: function findAll(element, eventType, handler) { var found = void 0, foundByElements = element ? this.findByElement(element) : []; // foundByEventType = eventType ? this.findByType(eventType) : [], // foundByHandler = handler ? this.findByHandler(handler) : []; if (element && eventType && handler) { found = foundByElements.filter(function (event) { return event.eventType === eventType && event.handler === handler; }); } else if (element && eventType) { found = foundByElements.filter(function (event) { return event.eventType === eventType; }); } else { found = foundByElements; } return found; } /** * Removes all listeners */ }, { key: "removeAll", value: function removeAll() { this.allListeners.map(function (current) { current.element.removeEventListener(current.eventType, current.handler); }); this.allListeners = []; } }]); return Listeners; }(Module); Listeners.displayName = "Listeners"; exports.default = Listeners; module.exports = exports["default"]; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../__module.ts */ "./src/components/__module.ts"))) /***/ }), /***/ "./src/components/modules/renderer.js": /*!********************************************!*\ !*** ./src/components/modules/renderer.js ***! \********************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(Module, _) { 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; }; }(); 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; } /** * Codex Editor Renderer Module * * @module Renderer * @author CodeX Team * * @version 2.0.0 */ var Renderer = function (_Module) { _inherits(Renderer, _Module); /** * @constructor * @param {EditorConfig} config */ function Renderer(_ref) { var config = _ref.config; _classCallCheck(this, Renderer); return _possibleConstructorReturn(this, (Renderer.__proto__ || Object.getPrototypeOf(Renderer)).call(this, { config: config })); } /** * @typedef {Object} RendererItems * @property {String} type - tool name * @property {Object} data - tool data */ /** * @example * * items: [ * { * type : 'paragraph', * data : { * text : 'Hello from Codex!' * } * }, * { * type : 'paragraph', * data : { * text : 'Leave feedback if you like it!' * } * }, * ] * */ /** * Make plugin blocks from array of plugin`s data * @param {RendererItems[]} items */ _createClass(Renderer, [{ key: 'render', value: function render(items) { var _this2 = this; var chainData = []; var _loop = function _loop(i) { chainData.push({ function: function _function() { return _this2.insertBlock(items[i]); } }); }; for (var i = 0; i < items.length; i++) { _loop(i); } return _.sequence(chainData); } /** * Get plugin instance * Add plugin instance to BlockManager * Insert block to working zone * * @param {Object} item * @returns {Promise.} * @private */ }, { key: 'insertBlock', value: function insertBlock(item) { var tool = item.type, data = item.data, settings = item.settings; if (tool in this.Editor.Tools.available) { this.Editor.BlockManager.insert(tool, data, settings); } else { /** * @todo show warning notification message * * `${tool} blocks was skipped.` */ _.log('Tool \xAB' + tool + '\xBB is not found. Check \'tools\' property at your initial CodeX Editor config.', 'warn'); } return Promise.resolve(); } }]); return Renderer; }(Module); Renderer.displayName = 'Renderer'; exports.default = Renderer; module.exports = exports['default']; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../__module.ts */ "./src/components/__module.ts"), __webpack_require__(/*! utils */ "./src/components/utils.js"))) /***/ }), /***/ "./src/components/modules/sanitizer.js": /*!*********************************************!*\ !*** ./src/components/modules/sanitizer.js ***! \*********************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(Module, _) { 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; }; }(); 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; } /** * CodeX Sanitizer * * @module Sanitizer * Clears HTML from taint tags * * @version 2.0.0 * * @example * Module can be used within two ways: * 1) When you have an instance * - this.Editor.Sanitizer.clean(yourTaintString); * 2) As static method * - CodexEditor.Sanitizer.clean(yourTaintString, yourCustomConfiguration); * * {@link SanitizerConfig} */ /** * @typedef {Object} SanitizerConfig * @property {Object} tags - define tags restrictions * * @example * * tags : { * p: true, * a: { * href: true, * rel: "nofollow", * target: "_blank" * } * } */ var Sanitizer = function (_Module) { _inherits(Sanitizer, _Module); /** * Initializes Sanitizer module * Sets default configuration if custom not exists * * @property {SanitizerConfig} this.defaultConfig * @property {HTMLJanitor} this._sanitizerInstance - Sanitizer library * * @param {SanitizerConfig} config */ function Sanitizer(_ref) { var config = _ref.config; _classCallCheck(this, Sanitizer); // default config var _this = _possibleConstructorReturn(this, (Sanitizer.__proto__ || Object.getPrototypeOf(Sanitizer)).call(this, { config: config })); _this.defaultConfig = null; _this._sanitizerInstance = null; /** Custom configuration */ _this.sanitizerConfig = config.settings ? config.settings.sanitizer : {}; /** HTML Janitor library */ _this.sanitizerInstance = __webpack_require__(/*! html-janitor */ "./node_modules/html-janitor/src/html-janitor.js"); return _this; } /** * If developer uses editor's API, then he can customize sanitize restrictions. * Or, sanitizing config can be defined globally in editors initialization. That config will be used everywhere * At least, if there is no config overrides, that API uses Default configuration * * @uses https://www.npmjs.com/package/html-janitor * * @param {HTMLJanitor} library - sanitizer extension */ _createClass(Sanitizer, [{ key: 'clean', /** * Cleans string from unwanted tags * @param {String} taintString - HTML string * @param {Object} customConfig - custom sanitizer configuration. Method uses default if param is empty * @return {String} clean HTML */ value: function clean(taintString) { var customConfig = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; if (_.isEmpty(customConfig)) { return this._sanitizerInstance.clean(taintString); } else { return Sanitizer.clean(taintString, customConfig); } } /** * Cleans string from unwanted tags * @static * * Method allows to use default config * * @param {String} taintString - taint string * @param {SanitizerConfig} customConfig - allowed tags * * @return {String} clean HTML */ }, { key: 'sanitizerInstance', set: function set(library) { this._sanitizerInstance = new library(this.defaultConfig); } /** * Sets sanitizer configuration. Uses default config if user didn't pass the restriction * @param {SanitizerConfig} config */ }, { key: 'sanitizerConfig', set: function set(config) { if (_.isEmpty(config)) { this.defaultConfig = { tags: { p: {}, a: { href: true, target: '_blank', rel: 'nofollow' } } }; } else { this.defaultConfig = config; } } }], [{ key: 'clean', value: function clean(taintString, customConfig) { var newInstance = Sanitizer(customConfig); return newInstance.clean(taintString); } }]); return Sanitizer; }(Module); Sanitizer.displayName = 'Sanitizer'; exports.default = Sanitizer; module.exports = exports['default']; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../__module.ts */ "./src/components/__module.ts"), __webpack_require__(/*! utils */ "./src/components/utils.js"))) /***/ }), /***/ "./src/components/modules/saver.js": /*!*****************************************!*\ !*** ./src/components/modules/saver.js ***! \*****************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(Module) { 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; }; }(); 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; } /** * Codex Editor Saver * * @module Saver * @author Codex Team * @version 2.0.0 */ /** * @typedef {Object} SavedData * @property {Date} time - saving proccess time * @property {Object} items - extracted data * @property {String} version - CodexEditor version */ /** * @classdesc This method reduces all Blocks asyncronically and calls Block's save method to extract data * * @typedef {Saver} Saver * @property {Element} html - Editor HTML content * @property {String} json - Editor JSON output */ var Saver = function (_Module) { _inherits(Saver, _Module); /** * @constructor * @param config */ function Saver(_ref) { var config = _ref.config; _classCallCheck(this, Saver); var _this = _possibleConstructorReturn(this, (Saver.__proto__ || Object.getPrototypeOf(Saver)).call(this, { config: config })); _this.output = null; _this.blocksData = []; return _this; } /** * Composes new chain of Promises to fire them alternatelly * @return {SavedData} */ _createClass(Saver, [{ key: 'save', value: function save() { var _this2 = this; var blocks = this.Editor.BlockManager.blocks, chainData = []; blocks.forEach(function (block) { chainData.push(block.data); }); return Promise.all(chainData).then(function (allExtractedData) { return _this2.makeOutput(allExtractedData); }).then(function (outputData) { return outputData; }); } /** * Creates output object with saved data, time and version of editor * @param {Object} allExtractedData * @return {SavedData} */ }, { key: 'makeOutput', value: function makeOutput(allExtractedData) { var items = [], totalTime = 0; console.groupCollapsed('[CodexEditor saving]:'); allExtractedData.forEach(function (extraction) { /** Group process info */ console.log('\xAB' + extraction.tool + '\xBB saving info', extraction); totalTime += extraction.time; items.push({ type: extraction.tool, data: extraction.data }); }); console.log('Total', totalTime); console.groupEnd(); return { time: +new Date(), items: items, version: "2.0.0" }; } }]); return Saver; }(Module); // module.exports = (function (saver) { // // let editor = codex.editor; // // /** // * @public // * Save blocks // */ // saver.save = function () { // // /** Save html content of redactor to memory */ // editor.state.html = editor.nodes.redactor.innerHTML; // // /** Clean jsonOutput state */ // editor.state.jsonOutput = []; // // return saveBlocks(editor.nodes.redactor.childNodes); // // }; // // /** // * @private // * Save each block data // * // * @param blocks // * @returns {Promise.} // */ // let saveBlocks = function (blocks) { // // let data = []; // // for(let index = 0; index < blocks.length; index++) { // // data.push(getBlockData(blocks[index])); // // } // // return Promise.all(data) // .then(makeOutput) // .catch(editor.core.log); // // }; // // /** Save and validate block data */ // let getBlockData = function (block) { // // return saveBlockData(block) // .then(validateBlockData) // .catch(editor.core.log); // // }; // // /** // * @private // * Call block`s plugin save method and return saved data // * // * @param block // * @returns {Object} // */ // let saveBlockData = function (block) { // // let pluginName = block.dataset.tool; // // /** Check for plugin existence */ // if (!editor.tools[pluginName]) { // // editor.core.log(`Plugin «${pluginName}» not found`, 'error'); // return {data: null, pluginName: null}; // // } // // /** Check for plugin having save method */ // if (typeof editor.tools[pluginName].save !== 'function') { // // editor.core.log(`Plugin «${pluginName}» must have save method`, 'error'); // return {data: null, pluginName: null}; // // } // // /** Result saver */ // let blockContent = block.childNodes[0], // pluginsContent = blockContent.childNodes[0], // position = pluginsContent.dataset.inputPosition; // // /** If plugin wasn't available then return data from cache */ // if ( editor.tools[pluginName].available === false ) { // // return Promise.resolve({data: codex.editor.state.blocks.items[position].data, pluginName}); // // } // // return Promise.resolve(pluginsContent) // .then(editor.tools[pluginName].save) // .then(data => Object({data, pluginName})); // // }; // // /** // * Call plugin`s validate method. Return false if validation failed // * // * @param data // * @param pluginName // * @returns {Object|Boolean} // */ // let validateBlockData = function ({data, pluginName}) { // // if (!data || !pluginName) { // // return false; // // } // // if (editor.tools[pluginName].validate) { // // let result = editor.tools[pluginName].validate(data); // // /** // * Do not allow invalid data // */ // if (!result) { // // return false; // // } // // } // // return {data, pluginName}; // // // }; // // /** // * Compile article output // * // * @param savedData // * @returns {{time: number, version, items: (*|Array)}} // */ // let makeOutput = function (savedData) { // // savedData = savedData.filter(blockData => blockData); // // let items = savedData.map(blockData => Object({type: blockData.pluginName, data: blockData.data})); // // editor.state.jsonOutput = items; // // return { // id: editor.state.blocks.id || null, // time: +new Date(), // version: editor.version, // items // }; // // }; // // return saver; // // })({}); Saver.displayName = 'Saver'; exports.default = Saver; module.exports = exports['default']; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../__module.ts */ "./src/components/__module.ts"))) /***/ }), /***/ "./src/components/modules/toolbar-blockSettings.js": /*!*********************************************************!*\ !*** ./src/components/modules/toolbar-blockSettings.js ***! \*********************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(Module, $) { 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; }; }(); 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; } /** * Block Settings * * ____ Settings Panel ____ * | ...................... | * | . Tool Settings . | * | ...................... | * | . Default Settings . | * | ...................... | * |________________________| */ var BlockSettings = function (_Module) { _inherits(BlockSettings, _Module); /** * @constructor */ function BlockSettings(_ref) { var config = _ref.config; _classCallCheck(this, BlockSettings); var _this = _possibleConstructorReturn(this, (BlockSettings.__proto__ || Object.getPrototypeOf(BlockSettings)).call(this, { config: config })); _this.nodes = { wrapper: null, toolSettings: null, defaultSettings: null }; return _this; } /** * Module Events * @return {{opened: string, closed: string}} */ _createClass(BlockSettings, [{ key: 'make', /** * Panel with block settings with 2 sections: * - Tool's Settings * - Default Settings [Move, Remove, etc] * * @return {Element} */ value: function make() { this.nodes.wrapper = $.make('div', BlockSettings.CSS.wrapper); this.nodes.toolSettings = $.make('div', BlockSettings.CSS.toolSettings); this.nodes.defaultSettings = $.make('div', BlockSettings.CSS.defaultSettings); $.append(this.nodes.wrapper, [this.nodes.toolSettings, this.nodes.defaultSettings]); } /** * Add Tool's settings */ }, { key: 'addToolSettings', value: function addToolSettings() { if (typeof this.Editor.BlockManager.currentBlock.tool.renderSettings === 'function') { $.append(this.nodes.toolSettings, this.Editor.BlockManager.currentBlock.tool.renderSettings()); } } /** * Add default settings */ }, { key: 'addDefaultSettings', value: function addDefaultSettings() { $.append(this.nodes.defaultSettings, this.Editor.BlockManager.currentBlock.renderTunes()); } /** * Is Block Settings opened or not * @returns {boolean} */ }, { key: 'open', /** * Open Block Settings pane */ value: function open() { this.nodes.wrapper.classList.add(BlockSettings.CSS.wrapperOpened); /** * Fill Tool's settings */ this.addToolSettings(); /** * Add default settings that presents for all Blocks */ this.addDefaultSettings(); /** Tell to subscribers that block settings is opened */ this.Editor.Events.emit(this.events.opened); } /** * Close Block Settings pane */ }, { key: 'close', value: function close() { this.nodes.wrapper.classList.remove(BlockSettings.CSS.wrapperOpened); /** Clear settings */ this.nodes.toolSettings.innerHTML = ''; this.nodes.defaultSettings.innerHTML = ''; /** Tell to subscribers that block settings is closed */ this.Editor.Events.emit(this.events.closed); } }, { key: 'events', get: function get() { return { opened: 'block-settings-opened', closed: 'block-settings-closed' }; } /** * Block Settings CSS * @return {{wrapper, wrapperOpened, toolSettings, defaultSettings, button}} */ }, { key: 'opened', get: function get() { return this.nodes.wrapper.classList.contains(BlockSettings.CSS.wrapperOpened); } }], [{ key: 'CSS', get: function get() { return { // Settings Panel wrapper: 'ce-settings', wrapperOpened: 'ce-settings--opened', toolSettings: 'ce-settings__plugin-zone', defaultSettings: 'ce-settings__default-zone', button: 'ce-settings__button' }; } }]); return BlockSettings; }(Module); BlockSettings.displayName = 'BlockSettings'; exports.default = BlockSettings; module.exports = exports['default']; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../__module.ts */ "./src/components/__module.ts"), __webpack_require__(/*! dom */ "./src/components/dom.js"))) /***/ }), /***/ "./src/components/modules/toolbar-inline.ts": /*!**************************************************!*\ !*** ./src/components/modules/toolbar-inline.ts ***! \**************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(Module, $, _) { 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 _inlineToolBold = __webpack_require__(/*! ../inline-tools/inline-tool-bold */ "./src/components/inline-tools/inline-tool-bold.ts"); var _inlineToolBold2 = _interopRequireDefault(_inlineToolBold); var _inlineToolItalic = __webpack_require__(/*! ../inline-tools/inline-tool-italic */ "./src/components/inline-tools/inline-tool-italic.ts"); var _inlineToolItalic2 = _interopRequireDefault(_inlineToolItalic); var _inlineToolLink = __webpack_require__(/*! ../inline-tools/inline-tool-link */ "./src/components/inline-tools/inline-tool-link.ts"); var _inlineToolLink2 = _interopRequireDefault(_inlineToolLink); var _selection = __webpack_require__(/*! ../selection */ "./src/components/selection.js"); var _selection2 = _interopRequireDefault(_selection); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } 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 InlineToolbar = function (_Module) { _inherits(InlineToolbar, _Module); /** * @constructor */ function InlineToolbar(_ref) { var config = _ref.config; _classCallCheck(this, InlineToolbar); /** * CSS styles */ var _this = _possibleConstructorReturn(this, (InlineToolbar.__proto__ || Object.getPrototypeOf(InlineToolbar)).call(this, { config: config })); _this.CSS = { inlineToolbar: 'ce-inline-toolbar', inlineToolbarShowed: 'ce-inline-toolbar--showed', buttonsWrapper: 'ce-inline-toolbar__buttons', actionsWrapper: 'ce-inline-toolbar__actions' }; /** * Inline Toolbar elements */ _this.nodes = { wrapper: null, buttons: null, /** * Zone below the buttons where Tools can create additional actions by 'renderActions()' method * For example, input for the 'link' tool or textarea for the 'comment' tool */ actions: null }; /** * Margin above/below the Toolbar */ _this.toolbarVerticalMargin = 20; return _this; } /** * Inline Toolbar Tools * @todo Merge internal tools with external */ _createClass(InlineToolbar, [{ key: 'make', /** * Making DOM */ value: function make() { this.nodes.wrapper = $.make('div', this.CSS.inlineToolbar); this.nodes.buttons = $.make('div', this.CSS.buttonsWrapper); this.nodes.actions = $.make('div', this.CSS.actionsWrapper); /** * Append Inline Toolbar to the Editor */ $.append(this.nodes.wrapper, [this.nodes.buttons, this.nodes.actions]); $.append(this.Editor.UI.nodes.wrapper, this.nodes.wrapper); /** * Append Inline Toolbar Tools */ this.addTools(); } /** * * * Moving / appearance * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * */ /** * Shows Inline Toolbar by keyup/mouseup * @param {KeyboardEvent|MouseEvent} event */ }, { key: 'handleShowingEvent', value: function handleShowingEvent(event) { if (!this.allowedToShow(event)) { this.close(); return; } this.move(); this.open(); /** Check Tools state for selected fragment */ this.checkToolsState(); } /** * Move Toolbar to the selected text */ }, { key: 'move', value: function move() { var selectionRect = _selection2.default.rect; var wrapperOffset = this.Editor.UI.nodes.wrapper.getBoundingClientRect(); var newCoords = { x: selectionRect.x - wrapperOffset.left, y: selectionRect.y + selectionRect.height // + window.scrollY - wrapperOffset.top + this.toolbarVerticalMargin }; /** * If we know selections width, place InlineToolbar to center */ if (selectionRect.width) { newCoords.x += Math.floor(selectionRect.width / 2); } this.nodes.wrapper.style.left = Math.floor(newCoords.x) + 'px'; this.nodes.wrapper.style.top = Math.floor(newCoords.y) + 'px'; } /** * Shows Inline Toolbar */ }, { key: 'open', value: function open() { this.nodes.wrapper.classList.add(this.CSS.inlineToolbarShowed); this.tools.forEach(function (tool) { if (typeof tool.clear === 'function') { tool.clear(); } }); } /** * Hides Inline Toolbar */ }, { key: 'close', value: function close() { this.nodes.wrapper.classList.remove(this.CSS.inlineToolbarShowed); this.tools.forEach(function (tool) { if (typeof tool.clear === 'function') { tool.clear(); } }); } /** * Need to show Inline Toolbar or not * @param {KeyboardEvent|MouseEvent} event */ }, { key: 'allowedToShow', value: function allowedToShow(event) { /** * Tags conflicts with window.selection function. * Ex. IMG tag returns null (Firefox) or Redactors wrapper (Chrome) */ var tagsConflictsWithSelection = ['IMG', 'INPUT']; if (event && tagsConflictsWithSelection.includes(event.target.tagName)) { return false; } var currentSelection = _selection2.default.get(), selectedText = _selection2.default.text; // old browsers if (!currentSelection || !currentSelection.anchorNode) { return false; } // empty selection if (currentSelection.isCollapsed || selectedText.length < 1) { return false; } // is enabled by current Block's Tool var currentBlock = this.Editor.BlockManager.getBlock(currentSelection.anchorNode); if (!currentBlock) { return false; } var toolConfig = this.config.toolsConfig[currentBlock.name]; return toolConfig && toolConfig[this.Editor.Tools.apiSettings.IS_ENABLED_INLINE_TOOLBAR]; } /** * * * Working with Tools * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * */ /** * Fill Inline Toolbar with Tools */ }, { key: 'addTools', value: function addTools() { var _this2 = this; this.tools.forEach(function (tool) { _this2.addTool(tool); }); } /** * Add tool button and activate clicks * @param {InlineTool} tool - Tool's instance */ }, { key: 'addTool', value: function addTool(tool) { var _this3 = this; var button = tool.render(); if (!button) { _.log('Render method must return an instance of Node', 'warn', tool); return; } this.nodes.buttons.appendChild(button); if (typeof tool.renderActions === 'function') { var actions = tool.renderActions(); this.nodes.actions.appendChild(actions); } this.Editor.Listeners.on(button, 'click', function () { _this3.toolClicked(tool); }); } /** * Inline Tool button clicks * @param {InlineTool} tool - Tool's instance */ }, { key: 'toolClicked', value: function toolClicked(tool) { var range = _selection2.default.range; tool.surround(range); this.checkToolsState(); } /** * Check Tools` state by selection */ }, { key: 'checkToolsState', value: function checkToolsState() { this.tools.forEach(function (tool) { tool.checkState(_selection2.default.get()); }); } }, { key: 'tools', get: function get() { var _this4 = this; if (!this.toolsInstances) { this.toolsInstances = [new _inlineToolBold2.default(this.Editor.API.methods), new _inlineToolItalic2.default(this.Editor.API.methods), new _inlineToolLink2.default(this.Editor.API.methods)].concat(_toConsumableArray(this.Editor.Tools.inline.map(function (Tool) { return new Tool(_this4.Editor.API.methods); }))); } return this.toolsInstances; } }]); return InlineToolbar; }(Module); InlineToolbar.displayName = 'InlineToolbar'; exports.default = InlineToolbar; module.exports = exports['default']; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../__module.ts */ "./src/components/__module.ts"), __webpack_require__(/*! dom */ "./src/components/dom.js"), __webpack_require__(/*! utils */ "./src/components/utils.js"))) /***/ }), /***/ "./src/components/modules/toolbar-toolbox.js": /*!***************************************************!*\ !*** ./src/components/modules/toolbar-toolbox.js ***! \***************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(Module, $, _) { 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; }; }(); 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; } /** * @class Toolbox * @classdesc Holder for Tools * * @typedef {Toolbox} Toolbox * @property {Boolean} opened - opening state * @property {Object} nodes - Toolbox nodes * @property {Object} CSS - CSS class names * */ var Toolbox = function (_Module) { _inherits(Toolbox, _Module); /** * @constructor */ function Toolbox(_ref) { var config = _ref.config; _classCallCheck(this, Toolbox); var _this = _possibleConstructorReturn(this, (Toolbox.__proto__ || Object.getPrototypeOf(Toolbox)).call(this, { config: config })); _this.nodes = { toolbox: null, buttons: [] }; /** * Opening state * @type {boolean} */ _this.opened = false; return _this; } /** * CSS styles * @return {{toolbox: string, toolboxButton: string, toolboxOpened: string}} */ _createClass(Toolbox, [{ key: 'make', /** * Makes the Toolbox */ value: function make() { this.nodes.toolbox = $.make('div', Toolbox.CSS.toolbox); $.append(this.Editor.Toolbar.nodes.content, this.nodes.toolbox); this.addTools(); } /** * Iterates available tools and appends them to the Toolbox */ }, { key: 'addTools', value: function addTools() { var tools = this.Editor.Tools.toolsAvailable; for (var toolName in tools) { this.addTool(toolName, tools[toolName]); } } /** * Append Tool to the Toolbox * * @param {string} toolName - tool name * @param {Tool} tool - tool class */ }, { key: 'addTool', value: function addTool(toolName, tool) { var _this2 = this; var api = this.Editor.Tools.apiSettings; if (tool[api.IS_DISPLAYED_IN_TOOLBOX] && !tool[api.TOOLBAR_ICON_CLASS]) { _.log('Toolbar icon class name is missed. Tool %o skipped', 'warn', toolName); return; } /** * @todo Add checkup for the render method */ // if (typeof tool.render !== 'function') { // // _.log('render method missed. Tool %o skipped', 'warn', tool); // return; // // } /** * Skip tools that pass 'displayInToolbox=false' */ if (!tool[api.IS_DISPLAYED_IN_TOOLBOX]) { return; } var button = $.make('li', [Toolbox.CSS.toolboxButton, tool[api.TOOLBAR_ICON_CLASS]], { title: toolName }); /** * Save tool's name in the button data-name */ button.dataset.name = toolName; $.append(this.nodes.toolbox, button); this.nodes.toolbox.appendChild(button); this.nodes.buttons.push(button); /** * @todo add event with module Listeners */ // this.Editor.Listeners.add(); button.addEventListener('click', function (event) { _this2.buttonClicked(event); }, false); } /** * Toolbox button click listener * 1) if block is empty -> replace * 2) if block is not empty -> add new block below * * @param {MouseEvent} event */ }, { key: 'buttonClicked', value: function buttonClicked(event) { var toolButton = event.target, toolName = toolButton.dataset.name, tool = this.Editor.Tools.toolClasses[toolName]; /** * @type {Block} */ var currentBlock = this.Editor.BlockManager.currentBlock; /** * We do replace if: * - block is empty * - block is not irreplaceable * @type {Array} */ if (!tool[this.Editor.Tools.apiSettings.IS_IRREPLACEBLE_TOOL] && currentBlock.isEmpty) { this.Editor.BlockManager.replace(toolName); } else { this.Editor.BlockManager.insert(toolName); } /** * @todo set caret to the new block */ // window.setTimeout(function () { /** Set caret to current block */ // editor.caret.setToBlock(currentInputIndex); // }, 10); /** * Move toolbar when node is changed */ this.Editor.Toolbar.move(); } /** * Open Toolbox with Tools */ }, { key: 'open', value: function open() { this.nodes.toolbox.classList.add(Toolbox.CSS.toolboxOpened); this.opened = true; } /** * Close Toolbox */ }, { key: 'close', value: function close() { this.nodes.toolbox.classList.remove(Toolbox.CSS.toolboxOpened); this.opened = false; } /** * Close Toolbox */ }, { key: 'toggle', value: function toggle() { if (!this.opened) { this.open(); } else { this.close(); } } }], [{ key: 'CSS', get: function get() { return { toolbox: 'ce-toolbox', toolboxButton: 'ce-toolbox__button', toolboxOpened: 'ce-toolbox--opened' }; } }]); return Toolbox; }(Module); Toolbox.displayName = 'Toolbox'; exports.default = Toolbox; module.exports = exports['default']; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../__module.ts */ "./src/components/__module.ts"), __webpack_require__(/*! dom */ "./src/components/dom.js"), __webpack_require__(/*! utils */ "./src/components/utils.js"))) /***/ }), /***/ "./src/components/modules/toolbar.js": /*!*******************************************!*\ !*** ./src/components/modules/toolbar.js ***! \*******************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(Module, $) { 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; }; }(); 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; } /** * * «Toolbar» is the node that moves up/down over current block * * ______________________________________ Toolbar ____________________________________________ * | | * | ..................... Content .................... ......... Block Actions .......... | * | . . . . | * | . . . [Open Settings] . | * | . [Plus Button] [Toolbox: {Tool1}, {Tool2}] . . . | * | . . . [Settings Panel] . | * | .................................................. .................................. | * | | * |___________________________________________________________________________________________| * * * Toolbox — its an Element contains tools buttons. Can be shown by Plus Button. * * _______________ Toolbox _______________ * | | * | [Header] [Image] [List] [Quote] ... | * |_______________________________________| * * * Settings Panel — is an Element with block settings: * * ____ Settings Panel ____ * | ...................... | * | . Tool Settings . | * | ...................... | * | . Default Settings . | * | ...................... | * |________________________| * * * @class * @classdesc Toolbar module * * @typedef {Toolbar} Toolbar * @property {Object} nodes * @property {Element} nodes.wrapper - Toolbar main element * @property {Element} nodes.content - Zone with Plus button and toolbox. * @property {Element} nodes.actions - Zone with Block Settings and Remove Button * @property {Element} nodes.blockActionsButtons - Zone with Block Buttons: [Settings] * @property {Element} nodes.plusButton - Button that opens or closes Toolbox * @property {Element} nodes.toolbox - Container for tools * @property {Element} nodes.settingsToggler - open/close Settings Panel button * @property {Element} nodes.settings - Settings Panel * @property {Element} nodes.pluginSettings - Plugin Settings section of Settings Panel * @property {Element} nodes.defaultSettings - Default Settings section of Settings Panel */ var Toolbar = function (_Module) { _inherits(Toolbar, _Module); /** * @constructor */ function Toolbar(_ref) { var config = _ref.config; _classCallCheck(this, Toolbar); var _this = _possibleConstructorReturn(this, (Toolbar.__proto__ || Object.getPrototypeOf(Toolbar)).call(this, { config: config })); _this.nodes = { wrapper: null, content: null, actions: null, // Content Zone plusButton: null, // Actions Zone blockActionsButtons: null, settingsToggler: null }; return _this; } /** * CSS styles * @return {Object} * @constructor */ _createClass(Toolbar, [{ key: 'make', /** * Makes toolbar */ value: function make() { var _this2 = this; this.nodes.wrapper = $.make('div', Toolbar.CSS.toolbar); /** * Make Content Zone and Actions Zone */ ['content', 'actions'].forEach(function (el) { _this2.nodes[el] = $.make('div', Toolbar.CSS[el]); $.append(_this2.nodes.wrapper, _this2.nodes[el]); }); /** * Fill Content Zone: * - Plus Button * - Toolbox */ this.nodes.plusButton = $.make('div', Toolbar.CSS.plusButton); $.append(this.nodes.plusButton, $.svg('plus', 14, 14)); $.append(this.nodes.content, this.nodes.plusButton); this.nodes.plusButton.addEventListener('click', function (event) { return _this2.plusButtonClicked(event); }, false); /** * Make a Toolbox */ this.Editor.Toolbox.make(); /** * Fill Actions Zone: * - Settings Toggler * - Remove Block Button * - Settings Panel */ this.nodes.blockActionsButtons = $.make('div', Toolbar.CSS.blockActionsButtons); this.nodes.settingsToggler = $.make('span', Toolbar.CSS.settingsToggler); var settingsIcon = $.svg('dots', 18, 4); $.append(this.nodes.settingsToggler, settingsIcon); $.append(this.nodes.blockActionsButtons, this.nodes.settingsToggler); $.append(this.nodes.actions, this.nodes.blockActionsButtons); /** * Make and append Settings Panel */ this.Editor.BlockSettings.make(); $.append(this.nodes.actions, this.Editor.BlockSettings.nodes.wrapper); /** * Append toolbar to the Editor */ $.append(this.Editor.UI.nodes.wrapper, this.nodes.wrapper); /** * Bind events on the Toolbar elements */ this.bindEvents(); } /** * Move Toolbar to the Current Block * @param {Boolean} forceClose - force close Toolbar Settings and Toolbar */ }, { key: 'move', value: function move() { var forceClose = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; if (forceClose) { /** Close Toolbox when we move toolbar */ this.Editor.Toolbox.close(); this.Editor.BlockSettings.close(); } var currentNode = this.Editor.BlockManager.currentNode; /** * If no one Block selected as a Current */ if (!currentNode) { return; } /** * @todo Compute dynamically on prepare * @type {number} */ var defaultToolbarHeight = 49; var defaultOffset = 34; var newYCoordinate = currentNode.offsetTop - defaultToolbarHeight / 2 + defaultOffset; this.nodes.wrapper.style.transform = 'translate3D(0, ' + Math.floor(newYCoordinate) + 'px, 0)'; } /** * Open Toolbar with Plus Button */ }, { key: 'open', value: function open() { this.nodes.wrapper.classList.add(Toolbar.CSS.toolbarOpened); } /** * Close the Toolbar */ }, { key: 'close', value: function close() { this.nodes.wrapper.classList.remove(Toolbar.CSS.toolbarOpened); } /** * Plus Button public methods * @return {{hide: function(): void, show: function(): void}} */ }, { key: 'plusButtonClicked', /** * Handler for Plus Button * @param {MouseEvent} event */ value: function plusButtonClicked() { this.Editor.Toolbox.toggle(); } /** * Bind events on the Toolbar Elements: * - Block Settings */ }, { key: 'bindEvents', value: function bindEvents() { var _this3 = this; /** * Settings toggler */ this.Editor.Listeners.on(this.nodes.settingsToggler, 'click', function (event) { _this3.settingsTogglerClicked(event); }); } /** * Clicks on the Block Settings toggler */ }, { key: 'settingsTogglerClicked', value: function settingsTogglerClicked() { if (this.Editor.BlockSettings.opened) { this.Editor.BlockSettings.close(); } else { this.Editor.BlockSettings.open(); } } }, { key: 'plusButton', get: function get() { var _this4 = this; return { hide: function hide() { return _this4.nodes.plusButton.classList.add(Toolbar.CSS.plusButtonHidden); }, show: function show() { return _this4.nodes.plusButton.classList.remove(Toolbar.CSS.plusButtonHidden); } }; } }], [{ key: 'CSS', get: function get() { return { toolbar: 'ce-toolbar', content: 'ce-toolbar__content', actions: 'ce-toolbar__actions', toolbarOpened: 'ce-toolbar--opened', // Content Zone plusButton: 'ce-toolbar__plus', plusButtonHidden: 'ce-toolbar__plus--hidden', // Actions Zone blockActionsButtons: 'ce-toolbar__actions-buttons', settingsToggler: 'ce-toolbar__settings-btn' }; } }]); return Toolbar; }(Module); Toolbar.displayName = 'Toolbar'; exports.default = Toolbar; module.exports = exports['default']; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../__module.ts */ "./src/components/__module.ts"), __webpack_require__(/*! dom */ "./src/components/dom.js"))) /***/ }), /***/ "./src/components/modules/toolbar/inline.js": /*!**************************************************!*\ !*** ./src/components/modules/toolbar/inline.js ***! \**************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /** * Inline toolbar * * Contains from tools: * Bold, Italic, Underline and Anchor * * @author Codex Team * @version 1.0 */ module.exports = function (inline) { var editor = codex.editor; inline.buttonsOpened = null; inline.actionsOpened = null; inline.wrappersOffset = null; /** * saving selection that need for execCommand for styling * */ inline.storedSelection = null; /** * @protected * * Open inline toobar */ inline.show = function () { var currentNode = editor.content.currentNode, tool = currentNode.dataset.tool, plugin; /** * tool allowed to open inline toolbar */ plugin = editor.tools[tool]; if (!plugin.showInlineToolbar) return; var selectedText = inline.getSelectionText(), toolbar = editor.nodes.inlineToolbar.wrapper; if (selectedText.length > 0) { /** Move toolbar and open */ editor.toolbar.inline.move(); /** Open inline toolbar */ toolbar.classList.add('opened'); /** show buttons of inline toolbar */ editor.toolbar.inline.showButtons(); } }; /** * @protected * * Closes inline toolbar */ inline.close = function () { var toolbar = editor.nodes.inlineToolbar.wrapper; toolbar.classList.remove('opened'); }; /** * @private * * Moving toolbar */ inline.move = function () { if (!this.wrappersOffset) { this.wrappersOffset = this.getWrappersOffset(); } var coords = this.getSelectionCoords(), defaultOffset = 0, toolbar = editor.nodes.inlineToolbar.wrapper, newCoordinateX, newCoordinateY; if (toolbar.offsetHeight === 0) { defaultOffset = 40; } newCoordinateX = coords.x - this.wrappersOffset.left; newCoordinateY = coords.y + window.scrollY - this.wrappersOffset.top - defaultOffset - toolbar.offsetHeight; toolbar.style.transform = 'translate3D(' + Math.floor(newCoordinateX) + 'px, ' + Math.floor(newCoordinateY) + 'px, 0)'; /** Close everything */ editor.toolbar.inline.closeButtons(); editor.toolbar.inline.closeAction(); }; /** * @private * * Tool Clicked */ inline.toolClicked = function (event, type) { /** * For simple tools we use default browser function * For more complicated tools, we should write our own behavior */ switch (type) { case 'createLink': editor.toolbar.inline.createLinkAction(event, type);break; default: editor.toolbar.inline.defaultToolAction(type);break; } /** * highlight buttons * after making some action */ editor.nodes.inlineToolbar.buttons.childNodes.forEach(editor.toolbar.inline.hightlight); }; /** * @private * * Saving wrappers offset in DOM */ inline.getWrappersOffset = function () { var wrapper = editor.nodes.wrapper, offset = this.getOffset(wrapper); this.wrappersOffset = offset; return offset; }; /** * @private * * Calculates offset of DOM element * * @param el * @returns {{top: number, left: number}} */ inline.getOffset = function (el) { var _x = 0; var _y = 0; while (el && !isNaN(el.offsetLeft) && !isNaN(el.offsetTop)) { _x += el.offsetLeft + el.clientLeft; _y += el.offsetTop + el.clientTop; el = el.offsetParent; } return { top: _y, left: _x }; }; /** * @private * * Calculates position of selected text * @returns {{x: number, y: number}} */ inline.getSelectionCoords = function () { var sel = document.selection, range; var x = 0, y = 0; if (sel) { if (sel.type != 'Control') { range = sel.createRange(); range.collapse(true); x = range.boundingLeft; y = range.boundingTop; } } else if (window.getSelection) { sel = window.getSelection(); if (sel.rangeCount) { range = sel.getRangeAt(0).cloneRange(); if (range.getClientRects) { range.collapse(true); var rect = range.getClientRects()[0]; if (!rect) { return; } x = rect.left; y = rect.top; } } } return { x: x, y: y }; }; /** * @private * * Returns selected text as String * @returns {string} */ inline.getSelectionText = function () { var selectedText = ''; // all modern browsers and IE9+ if (window.getSelection) { selectedText = window.getSelection().toString(); } return selectedText; }; /** Opens buttons block */ inline.showButtons = function () { var buttons = editor.nodes.inlineToolbar.buttons; buttons.classList.add('opened'); editor.toolbar.inline.buttonsOpened = true; /** highlight buttons */ editor.nodes.inlineToolbar.buttons.childNodes.forEach(editor.toolbar.inline.hightlight); }; /** Makes buttons disappear */ inline.closeButtons = function () { var buttons = editor.nodes.inlineToolbar.buttons; buttons.classList.remove('opened'); editor.toolbar.inline.buttonsOpened = false; }; /** Open buttons defined action if exist */ inline.showActions = function () { var action = editor.nodes.inlineToolbar.actions; action.classList.add('opened'); editor.toolbar.inline.actionsOpened = true; }; /** Close actions block */ inline.closeAction = function () { var action = editor.nodes.inlineToolbar.actions; action.innerHTML = ''; action.classList.remove('opened'); editor.toolbar.inline.actionsOpened = false; }; /** * Callback for keydowns in inline toolbar "Insert link..." input */ var inlineToolbarAnchorInputKeydown_ = function inlineToolbarAnchorInputKeydown_(event) { if (event.keyCode != editor.core.keys.ENTER) { return; } var editable = editor.content.currentNode, storedSelection = editor.toolbar.inline.storedSelection; editor.toolbar.inline.restoreSelection(editable, storedSelection); editor.toolbar.inline.setAnchor(this.value); /** * Preventing events that will be able to happen */ event.preventDefault(); event.stopImmediatePropagation(); editor.toolbar.inline.clearRange(); }; /** Action for link creation or for setting anchor */ inline.createLinkAction = function (event) { var isActive = this.isLinkActive(); var editable = editor.content.currentNode, storedSelection = editor.toolbar.inline.saveSelection(editable); /** Save globally selection */ editor.toolbar.inline.storedSelection = storedSelection; if (isActive) { /** * Changing stored selection. if we want to remove anchor from word * we should remove anchor from whole word, not only selected part. * The solution is than we get the length of current link * Change start position to - end of selection minus length of anchor */ editor.toolbar.inline.restoreSelection(editable, storedSelection); editor.toolbar.inline.defaultToolAction('unlink'); } else { /** Create input and close buttons */ var action = editor.draw.inputForLink(); editor.nodes.inlineToolbar.actions.appendChild(action); editor.toolbar.inline.closeButtons(); editor.toolbar.inline.showActions(); /** * focus to input * Solution: https://developer.mozilla.org/ru/docs/Web/API/HTMLElement/focus * Prevents event after showing input and when we need to focus an input which is in unexisted form */ action.focus(); event.preventDefault(); /** Callback to link action */ editor.listeners.add(action, 'keydown', inlineToolbarAnchorInputKeydown_, false); } }; inline.isLinkActive = function () { var isActive = false; editor.nodes.inlineToolbar.buttons.childNodes.forEach(function (tool) { var dataType = tool.dataset.type; if (dataType == 'link' && tool.classList.contains('hightlighted')) { isActive = true; } }); return isActive; }; /** default action behavior of tool */ inline.defaultToolAction = function (type) { document.execCommand(type, false, null); }; /** * @private * * Sets URL * * @param {String} url - URL */ inline.setAnchor = function (url) { document.execCommand('createLink', false, url); /** Close after URL inserting */ editor.toolbar.inline.closeAction(); }; /** * @private * * Saves selection */ inline.saveSelection = function (containerEl) { var range = window.getSelection().getRangeAt(0), preSelectionRange = range.cloneRange(), start; preSelectionRange.selectNodeContents(containerEl); preSelectionRange.setEnd(range.startContainer, range.startOffset); start = preSelectionRange.toString().length; return { start: start, end: start + range.toString().length }; }; /** * @private * * Sets to previous selection (Range) * * @param {Element} containerEl - editable element where we restore range * @param {Object} savedSel - range basic information to restore */ inline.restoreSelection = function (containerEl, savedSel) { var range = document.createRange(), charIndex = 0; range.setStart(containerEl, 0); range.collapse(true); var nodeStack = [containerEl], node, foundStart = false, stop = false, nextCharIndex; while (!stop && (node = nodeStack.pop())) { if (node.nodeType == 3) { nextCharIndex = charIndex + node.length; if (!foundStart && savedSel.start >= charIndex && savedSel.start <= nextCharIndex) { range.setStart(node, savedSel.start - charIndex); foundStart = true; } if (foundStart && savedSel.end >= charIndex && savedSel.end <= nextCharIndex) { range.setEnd(node, savedSel.end - charIndex); stop = true; } charIndex = nextCharIndex; } else { var i = node.childNodes.length; while (i--) { nodeStack.push(node.childNodes[i]); } } } var sel = window.getSelection(); sel.removeAllRanges(); sel.addRange(range); }; /** * @private * * Removes all ranges from window selection */ inline.clearRange = function () { var selection = window.getSelection(); selection.removeAllRanges(); }; /** * @private * * sets or removes hightlight */ inline.hightlight = function (tool) { var dataType = tool.dataset.type; if (document.queryCommandState(dataType)) { editor.toolbar.inline.setButtonHighlighted(tool); } else { editor.toolbar.inline.removeButtonsHighLight(tool); } /** * * hightlight for anchors */ var selection = window.getSelection(), tag = selection.anchorNode.parentNode; if (tag.tagName == 'A' && dataType == 'link') { editor.toolbar.inline.setButtonHighlighted(tool); } }; /** * @private * * Mark button if text is already executed */ inline.setButtonHighlighted = function (button) { button.classList.add('hightlighted'); /** At link tool we also change icon */ if (button.dataset.type == 'link') { var icon = button.childNodes[0]; icon.classList.remove('ce-icon-link'); icon.classList.add('ce-icon-unlink'); } }; /** * @private * * Removes hightlight */ inline.removeButtonsHighLight = function (button) { button.classList.remove('hightlighted'); /** At link tool we also change icon */ if (button.dataset.type == 'link') { var icon = button.childNodes[0]; icon.classList.remove('ce-icon-unlink'); icon.classList.add('ce-icon-link'); } }; return inline; }({}); /***/ }), /***/ "./src/components/modules/toolbar/settings.js": /*!****************************************************!*\ !*** ./src/components/modules/toolbar/settings.js ***! \****************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /** * Toolbar settings * * @version 1.0.5 */ module.exports = function (settings) { var editor = codex.editor; settings.opened = false; settings.setting = null; settings.actions = null; /** * Append and open settings */ settings.open = function (toolType) { /** * Append settings content * It's stored in tool.settings */ if (!editor.tools[toolType] || !editor.tools[toolType].renderSettings) { return; } /** * Draw settings block */ var settingsBlock = editor.tools[toolType].renderSettings(); editor.nodes.pluginSettings.appendChild(settingsBlock); /** Open settings block */ editor.nodes.blockSettings.classList.add('opened'); this.opened = true; }; /** * Close and clear settings */ settings.close = function () { editor.nodes.blockSettings.classList.remove('opened'); editor.nodes.pluginSettings.innerHTML = ''; this.opened = false; }; /** * @param {string} toolType - plugin type */ settings.toggle = function (toolType) { if (!this.opened) { this.open(toolType); } else { this.close(); } }; /** * Here we will draw buttons and add listeners to components */ settings.makeRemoveBlockButton = function () { var removeBlockWrapper = editor.draw.node('SPAN', 'ce-toolbar__remove-btn', {}), settingButton = editor.draw.node('SPAN', 'ce-toolbar__remove-setting', { innerHTML: '' }), actionWrapper = editor.draw.node('DIV', 'ce-toolbar__remove-confirmation', {}), confirmAction = editor.draw.node('DIV', 'ce-toolbar__remove-confirm', { textContent: 'Удалить блок' }), cancelAction = editor.draw.node('DIV', 'ce-toolbar__remove-cancel', { textContent: 'Отмена' }); editor.listeners.add(settingButton, 'click', editor.toolbar.settings.removeButtonClicked, false); editor.listeners.add(confirmAction, 'click', editor.toolbar.settings.confirmRemovingRequest, false); editor.listeners.add(cancelAction, 'click', editor.toolbar.settings.cancelRemovingRequest, false); actionWrapper.appendChild(confirmAction); actionWrapper.appendChild(cancelAction); removeBlockWrapper.appendChild(settingButton); removeBlockWrapper.appendChild(actionWrapper); /** Save setting */ editor.toolbar.settings.setting = settingButton; editor.toolbar.settings.actions = actionWrapper; return removeBlockWrapper; }; settings.removeButtonClicked = function () { var action = editor.toolbar.settings.actions; if (action.classList.contains('opened')) { editor.toolbar.settings.hideRemoveActions(); } else { editor.toolbar.settings.showRemoveActions(); } editor.toolbar.toolbox.close(); editor.toolbar.settings.close(); }; settings.cancelRemovingRequest = function () { editor.toolbar.settings.actions.classList.remove('opened'); }; settings.confirmRemovingRequest = function () { var currentBlock = editor.content.currentNode, firstLevelBlocksCount; currentBlock.remove(); firstLevelBlocksCount = editor.nodes.redactor.childNodes.length; /** * If all blocks are removed */ if (firstLevelBlocksCount === 0) { /** update currentNode variable */ editor.content.currentNode = null; /** Inserting new empty initial block */ editor.ui.addInitialBlock(); } editor.ui.saveInputs(); editor.toolbar.close(); }; settings.showRemoveActions = function () { editor.toolbar.settings.actions.classList.add('opened'); }; settings.hideRemoveActions = function () { editor.toolbar.settings.actions.classList.remove('opened'); }; return settings; }({}); /***/ }), /***/ "./src/components/modules/toolbar/toolbar.js": /*!***************************************************!*\ !*** ./src/components/modules/toolbar/toolbar.js ***! \***************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /** * Codex Editor toolbar module * * Contains: * - Inline toolbox * - Toolbox within plus button * - Settings section * * @author Codex Team * @version 1.0 */ module.exports = function (toolbar) { var editor = codex.editor; toolbar.settings = __webpack_require__(/*! ./settings */ "./src/components/modules/toolbar/settings.js"); toolbar.inline = __webpack_require__(/*! ./inline */ "./src/components/modules/toolbar/inline.js"); toolbar.toolbox = __webpack_require__(/*! ./toolbox */ "./src/components/modules/toolbar/toolbox.js"); /** * Margin between focused node and toolbar */ toolbar.defaultToolbarHeight = 49; toolbar.defaultOffset = 34; toolbar.opened = false; toolbar.current = null; /** * @protected */ toolbar.open = function () { if (editor.hideToolbar) { return; } var toolType = editor.content.currentNode.dataset.tool; if (!editor.tools[toolType] || !editor.tools[toolType].renderSettings) { editor.nodes.showSettingsButton.classList.add('hide'); } else { editor.nodes.showSettingsButton.classList.remove('hide'); } editor.nodes.toolbar.classList.add('opened'); this.opened = true; }; /** * @protected */ toolbar.close = function () { editor.nodes.toolbar.classList.remove('opened'); toolbar.opened = false; toolbar.current = null; for (var button in editor.nodes.toolbarButtons) { editor.nodes.toolbarButtons[button].classList.remove('selected'); } /** Close toolbox when toolbar is not displayed */ editor.toolbar.toolbox.close(); editor.toolbar.settings.close(); }; toolbar.toggle = function () { if (!this.opened) { this.open(); } else { this.close(); } }; toolbar.hidePlusButton = function () { editor.nodes.plusButton.classList.add('hide'); }; toolbar.showPlusButton = function () { editor.nodes.plusButton.classList.remove('hide'); }; /** * Moving toolbar to the specified node */ toolbar.move = function () { /** Close Toolbox when we move toolbar */ editor.toolbar.toolbox.close(); if (!editor.content.currentNode) { return; } var newYCoordinate = editor.content.currentNode.offsetTop - editor.toolbar.defaultToolbarHeight / 2 + editor.toolbar.defaultOffset; editor.nodes.toolbar.style.transform = 'translate3D(0, ' + Math.floor(newYCoordinate) + 'px, 0)'; /** Close trash actions */ editor.toolbar.settings.hideRemoveActions(); }; return toolbar; }({}); /***/ }), /***/ "./src/components/modules/toolbar/toolbox.js": /*!***************************************************!*\ !*** ./src/components/modules/toolbar/toolbox.js ***! \***************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /** * Codex Editor toolbox * * All tools be able to appended here * * @author Codex Team * @version 1.0 */ module.exports = function (toolbox) { var editor = codex.editor; toolbox.opened = false; toolbox.openedOnBlock = null; /** Shows toolbox */ toolbox.open = function () { /** Close setting if toolbox is opened */ if (editor.toolbar.settings.opened) { editor.toolbar.settings.close(); } /** Add 'toolbar-opened' class for current block **/ toolbox.openedOnBlock = editor.content.currentNode; toolbox.openedOnBlock.classList.add('toolbar-opened'); /** display toolbox */ editor.nodes.toolbox.classList.add('opened'); /** Animate plus button */ editor.nodes.plusButton.classList.add('clicked'); /** toolbox state */ editor.toolbar.toolbox.opened = true; }; /** Closes toolbox */ toolbox.close = function () { /** Remove 'toolbar-opened' class from current block **/ if (toolbox.openedOnBlock) toolbox.openedOnBlock.classList.remove('toolbar-opened'); toolbox.openedOnBlock = null; /** Makes toolbox disappear */ editor.nodes.toolbox.classList.remove('opened'); /** Rotate plus button */ editor.nodes.plusButton.classList.remove('clicked'); /** toolbox state */ editor.toolbar.toolbox.opened = false; editor.toolbar.current = null; }; toolbox.leaf = function () { var currentTool = editor.toolbar.current, tools = Object.keys(editor.tools), barButtons = editor.nodes.toolbarButtons, nextToolIndex = 0, toolToSelect = void 0, visibleTool = void 0, tool = void 0; if (!currentTool) { /** Get first tool from object*/ for (tool in editor.tools) { if (editor.tools[tool].displayInToolbox) { break; } nextToolIndex++; } } else { nextToolIndex = (tools.indexOf(currentTool) + 1) % tools.length; visibleTool = tools[nextToolIndex]; while (!editor.tools[visibleTool].displayInToolbox) { nextToolIndex = (nextToolIndex + 1) % tools.length; visibleTool = tools[nextToolIndex]; } } toolToSelect = tools[nextToolIndex]; for (var button in barButtons) { barButtons[button].classList.remove('selected'); } barButtons[toolToSelect].classList.add('selected'); editor.toolbar.current = toolToSelect; }; /** * Transforming selected node type into selected toolbar element type * @param {event} event */ toolbox.toolClicked = function (event) { /** * UNREPLACEBLE_TOOLS this types of tools are forbidden to replace even they are empty */ var UNREPLACEBLE_TOOLS = ['image', 'link', 'list', 'instagram', 'twitter', 'embed'], tool = editor.tools[editor.toolbar.current], workingNode = editor.content.currentNode, currentInputIndex = editor.caret.inputIndex, newBlockContent, appendCallback, blockData; /** Make block from plugin */ newBlockContent = tool.render(); /** information about block */ blockData = { block: newBlockContent, type: tool.type, stretched: false }; if (workingNode && UNREPLACEBLE_TOOLS.indexOf(workingNode.dataset.tool) === -1 && workingNode.textContent.trim() === '') { /** Replace current block */ editor.content.switchBlock(workingNode, newBlockContent, tool.type); } else { /** Insert new Block from plugin */ editor.content.insertBlock(blockData); /** increase input index */ currentInputIndex++; } /** Fire tool append callback */ appendCallback = tool.appendCallback; if (appendCallback && typeof appendCallback == 'function') { appendCallback.call(event); } window.setTimeout(function () { /** Set caret to current block */ editor.caret.setToBlock(currentInputIndex); }, 10); /** * Changing current Node */ editor.content.workingNodeChanged(); /** * Move toolbar when node is changed */ editor.toolbar.move(); }; return toolbox; }({}); /***/ }), /***/ "./src/components/modules/tools.js": /*!*****************************************!*\ !*** ./src/components/modules/tools.js ***! \*****************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(Module, _) { 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; }; }(); function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return 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; } /** * @module Codex Editor Tools Submodule * * Creates Instances from Plugins and binds external config to the instances */ /** * Each Tool must contain the following important objects: * * @typedef {Object} ToolConfig {@link docs/tools.md} * @property {String} iconClassname - this a icon in toolbar * @property {Boolean} displayInToolbox - will be displayed in toolbox. Default value is TRUE * @property {Boolean} enableLineBreaks - inserts new block or break lines. Default value is FALSE * @property {Boolean|String[]} inlineToolbar - Pass `true` to enable the Inline Toolbar with all Tools, all pass an array with specified Tools list | * @property render @todo add description * @property save @todo add description * @property settings @todo add description * @property validate - method that validates output data before saving */ /** * @typedef {Function} Tool {@link docs/tools.md} * @property {Boolean} displayInToolbox - By default, tools won't be added in the Toolbox. Pass true to add. * @property {String} iconClassName - CSS class name for the Toolbox button * @property {Boolean} irreplaceable - Toolbox behaviour: replace or add new block below * @property render * @property save * @property settings * @property validate * * @todo update according to current API * @todo describe Tool in the {@link docs/tools.md} */ /** * Class properties: * * @typedef {Tools} Tools * @property {Tools[]} toolsAvailable - available Tools * @property {Tools[]} toolsUnavailable - unavailable Tools * @property {Object} toolsClasses - all classes * @property {EditorConfig} config - Editor config */ var Tools = function (_Module) { _inherits(Tools, _Module); _createClass(Tools, [{ key: 'available', /** * Returns available Tools * @return {Tool[]} */ get: function get() { return this.toolsAvailable; } /** * Returns unavailable Tools * @return {Tool[]} */ }, { key: 'unavailable', get: function get() { return this.toolsUnavailable; } /** * Return Tools for the Inline Toolbar * @return {Array} - array of Inline Tool's classes */ }, { key: 'inline', get: function get() { var _this2 = this; return Object.values(this.available).filter(function (tool) { if (!tool[_this2.apiSettings.IS_INLINE]) { return false; } /** * Some Tools validation */ var inlineToolRequiredMethods = ['render', 'surround', 'checkState']; var notImplementedMethods = inlineToolRequiredMethods.filter(function (method) { return !new tool()[method]; }); if (notImplementedMethods.length) { _.log('Incorrect Inline Tool: ' + tool.name + '. Some of required methods is not implemented %o', 'warn', notImplementedMethods); return false; } return true; }); } /** * Constant for available Tools Settings * @return {object} */ }, { key: 'apiSettings', get: function get() { return { IS_INLINE: 'isInline', TOOLBAR_ICON_CLASS: 'iconClassName', IS_DISPLAYED_IN_TOOLBOX: 'displayInToolbox', IS_ENABLED_LINE_BREAKS: 'enableLineBreaks', IS_IRREPLACEBLE_TOOL: 'irreplaceable', IS_ENABLED_INLINE_TOOLBAR: 'inlineToolbar' }; } /** * Static getter for default Tool config fields * @return {ToolConfig} */ }, { key: 'defaultConfig', get: function get() { var _ref; return _ref = {}, _defineProperty(_ref, this.apiSettings.TOOLBAR_ICON_CLASS, false), _defineProperty(_ref, this.apiSettings.IS_DISPLAYED_IN_TOOLBOX, false), _defineProperty(_ref, this.apiSettings.IS_ENABLED_LINE_BREAKS, false), _defineProperty(_ref, this.apiSettings.IS_IRREPLACEBLE_TOOL, false), _defineProperty(_ref, this.apiSettings.IS_ENABLED_INLINE_TOOLBAR, false), _ref; } /** * @constructor * * @param {EditorConfig} config */ }]); function Tools(_ref2) { var config = _ref2.config; _classCallCheck(this, Tools); /** * Map {name: Class, ...} where: * name — block type name in JSON. Got from EditorConfig.tools keys * @type {Object} */ var _this = _possibleConstructorReturn(this, (Tools.__proto__ || Object.getPrototypeOf(Tools)).call(this, { config: config })); _this.toolClasses = {}; /** * Available tools list * {name: Class, ...} * @type {Object} */ _this.toolsAvailable = {}; /** * Tools that rejected a prepare method * {name: Class, ... } * @type {Object} */ _this.toolsUnavailable = {}; return _this; } /** * Creates instances via passed or default configuration * @return {Promise} */ _createClass(Tools, [{ key: 'prepare', value: function prepare() { var _this3 = this; if (!this.config.hasOwnProperty('tools')) { return Promise.reject("Can't start without tools"); } for (var toolName in this.config.tools) { this.toolClasses[toolName] = this.config.tools[toolName]; } /** * getting classes that has prepare method */ var sequenceData = this.getListOfPrepareFunctions(); /** * if sequence data contains nothing then resolve current chain and run other module prepare */ if (sequenceData.length === 0) { return Promise.resolve(); } /** * to see how it works {@link Util#sequence} */ return _.sequence(sequenceData, function (data) { _this3.success(data); }, function (data) { _this3.fallback(data); }); } /** * Binds prepare function of plugins with user or default config * @return {Array} list of functions that needs to be fired sequentially */ }, { key: 'getListOfPrepareFunctions', value: function getListOfPrepareFunctions() { var toolPreparationList = []; for (var toolName in this.toolClasses) { var toolClass = this.toolClasses[toolName]; if (typeof toolClass.prepare === 'function') { toolPreparationList.push({ function: toolClass.prepare, data: { toolName: toolName } }); } else { /** * If Tool hasn't a prepare method, mark it as available */ this.toolsAvailable[toolName] = toolClass; } } return toolPreparationList; } /** * @param {ChainData.data} data - append tool to available list */ }, { key: 'success', value: function success(data) { this.toolsAvailable[data.toolName] = this.toolClasses[data.toolName]; } /** * @param {ChainData.data} data - append tool to unavailable list */ }, { key: 'fallback', value: function fallback(data) { this.toolsUnavailable[data.toolName] = this.toolClasses[data.toolName]; } /** * Return tool`a instance * * @param {String} tool — tool name * @param {Object} data — initial data * * @todo throw exceptions if tool doesnt exist * */ }, { key: 'construct', value: function construct(tool, data) { var plugin = this.toolClasses[tool], config = this.config.toolsConfig[tool]; var instance = new plugin(data, config || {}); return instance; } /** * Check if passed Tool is an instance of Initial Block Tool * @param {Tool} tool - Tool to check * @return {Boolean} */ }, { key: 'isInitial', value: function isInitial(tool) { return tool instanceof this.available[this.config.initialBlock]; } }]); return Tools; }(Module); Tools.displayName = 'Tools'; exports.default = Tools; module.exports = exports['default']; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../__module.ts */ "./src/components/__module.ts"), __webpack_require__(/*! utils */ "./src/components/utils.js"))) /***/ }), /***/ "./src/components/modules/ui.js": /*!**************************************!*\ !*** ./src/components/modules/ui.js ***! \**************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(Module, $, _) { 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 _sprite = __webpack_require__(/*! ../../../build/sprite.svg */ "./build/sprite.svg"); var _sprite2 = _interopRequireDefault(_sprite); var _selection = __webpack_require__(/*! ../selection */ "./src/components/selection.js"); var _selection2 = _interopRequireDefault(_selection); 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; } /** * Module UI * * @type {UI} */ /** * Prebuilded sprite of SVG icons */ /** * @class * * @classdesc Makes CodeX Editor UI: * * * * * * * @typedef {UI} UI * @property {EditorConfig} config - editor configuration {@link CodexEditor#configuration} * @property {Object} Editor - available editor modules {@link CodexEditor#moduleInstances} * @property {Object} nodes - * @property {Element} nodes.holder - element where we need to append redactor * @property {Element} nodes.wrapper - * @property {Element} nodes.redactor - */ var UI = function (_Module) { _inherits(UI, _Module); /** * @constructor * * @param {EditorConfig} config */ function UI(_ref) { var config = _ref.config; _classCallCheck(this, UI); var _this = _possibleConstructorReturn(this, (UI.__proto__ || Object.getPrototypeOf(UI)).call(this, { config: config })); _this.nodes = { holder: null, wrapper: null, redactor: null }; return _this; } /** * Making main interface */ _createClass(UI, [{ key: 'prepare', value: function prepare() { var _this2 = this; return this.make() /** * Append SVG sprite */ .then(function () { return _this2.appendSVGSprite(); }) /** * Make toolbar */ .then(function () { return _this2.Editor.Toolbar.make(); }) /** * Make the Inline toolbar */ .then(function () { return _this2.Editor.InlineToolbar.make(); }) /** * Load and append CSS */ .then(function () { return _this2.loadStyles(); }) /** * Bind events for the UI elements */ .then(function () { return _this2.bindEvents(); }) /** Make container for inline toolbar */ // .then(makeInlineToolbar_) /** Add inline toolbar tools */ // .then(addInlineToolbarTools_) /** Draw wrapper for notifications */ // .then(makeNotificationHolder_) /** Add eventlisteners to redactor elements */ // .then(bindEvents_) .catch(function (e) { console.error(e); // editor.core.log("Can't draw editor interface"); }); } /** * CodeX Editor UI CSS class names * @return {{editorWrapper: string, editorZone: string, block: string}} */ }, { key: 'make', /** * Makes CodeX Editor interface * @return {Promise} */ value: function make() { var _this3 = this; return new Promise(function (resolve, reject) { /** * Element where we need to append CodeX Editor * @type {Element} */ _this3.nodes.holder = document.getElementById(_this3.config.holderId); if (!_this3.nodes.holder) { reject(Error("Holder wasn't found by ID: #" + _this3.config.holderId)); return; } /** * Create and save main UI elements */ _this3.nodes.wrapper = $.make('div', _this3.CSS.editorWrapper); _this3.nodes.redactor = $.make('div', _this3.CSS.editorZone); _this3.nodes.wrapper.appendChild(_this3.nodes.redactor); _this3.nodes.holder.appendChild(_this3.nodes.wrapper); resolve(); }); } /** * Appends CSS */ }, { key: 'loadStyles', value: function loadStyles() { /** * Load CSS */ var styles = __webpack_require__(/*! ../../styles/main.css */ "./src/styles/main.css"); /** * Make tag */ var tag = $.make('style', null, { textContent: styles.toString() }); /** * Append styles */ $.append(document.head, tag); } /** * Bind events on the CodeX Editor interface */ }, { key: 'bindEvents', value: function bindEvents() { var _this4 = this; this.Editor.Listeners.on(this.nodes.redactor, 'click', function (event) { return _this4.redactorClicked(event); }, false); this.Editor.Listeners.on(document, 'keydown', function (event) { return _this4.documentKeydown(event); }, true); this.Editor.Listeners.on(document, 'click', function (event) { return _this4.documentClicked(event); }, false); } /** * All keydowns on document * @param event */ }, { key: 'documentKeydown', value: function documentKeydown(event) { switch (event.keyCode) { case _.keyCodes.ENTER: this.enterPressed(event); break; default: this.defaultBehaviour(event); break; } } /** * Ignore all other document's keydown events * @param {KeyboardEvent} event */ }, { key: 'defaultBehaviour', value: function defaultBehaviour(event) { var keyDownOnEditor = event.target.closest('.' + this.CSS.editorWrapper); /** * Ignore keydowns on document * clear pointer and close toolbar */ if (!keyDownOnEditor) { /** * Remove all highlights and remove caret */ this.Editor.BlockManager.dropPointer(); /** * Close Toolbar */ this.Editor.Toolbar.close(); } } /** * Enter pressed on document * @param event */ }, { key: 'enterPressed', value: function enterPressed(event) { var hasPointerToBlock = this.Editor.BlockManager.currentBlockIndex >= 0; /** * If Selection is out of Editor and document has some selection */ if (!_selection2.default.isAtEditor && _selection2.default.anchorNode) { return; } /** * If there is no selection (caret is not placed) and BlockManager points some to Block */ if (hasPointerToBlock && !_selection2.default.anchorNode) { /** * Insert initial typed Block */ this.Editor.BlockManager.insert(); this.Editor.BlockManager.highlightCurrentNode(); /** * Move toolbar and show plus button because new Block is empty */ this.Editor.Toolbar.move(); this.Editor.Toolbar.plusButton.show(); } } /** * All clicks on document * @param {MouseEvent} event - Click */ }, { key: 'documentClicked', value: function documentClicked(event) { /** * Close Inline Toolbar when nothing selected * Do not fire check on clicks at the Inline Toolbar buttons */ var clickedOnInlineToolbarButton = event.target.closest('.' + this.Editor.InlineToolbar.CSS.inlineToolbar); var clickedInsideofEditor = event.target.closest('.' + this.CSS.editorWrapper); /** Clear highlightings and pointer on BlockManager */ if (!clickedInsideofEditor) { this.Editor.BlockManager.dropPointer(); this.Editor.Toolbar.close(); } if (!clickedOnInlineToolbarButton) { this.Editor.InlineToolbar.handleShowingEvent(event); } } /** * All clicks on the redactor zone * * @param {MouseEvent} event * * @description * 1. Save clicked Block as a current {@link BlockManager#currentNode} * it uses for the following: * - add CSS modifier for the selected Block * - on Enter press, we make a new Block under that * * 2. Move and show the Toolbar * * 3. Set a Caret * * 4. By clicks on the Editor's bottom zone: * - if last Block is empty, set a Caret to this * - otherwise, add a new empty Block and set a Caret to that * * 5. Hide the Inline Toolbar * * @see selectClickedBlock * */ }, { key: 'redactorClicked', value: function redactorClicked(event) { var clickedNode = event.target; /** * Select clicked Block as Current */ try { /** * Renew Current Block */ this.Editor.BlockManager.setCurrentBlockByChildNode(clickedNode); /** * Highlight Current Node */ this.Editor.BlockManager.highlightCurrentNode(); } catch (e) { /** * If clicked outside first-level Blocks, set Caret to the last empty Block */ this.Editor.Caret.setToTheLastBlock(); } /** * Move toolbar and open */ this.Editor.Toolbar.move(); this.Editor.Toolbar.open(); /** * Hide the Plus Button * */ this.Editor.Toolbar.plusButton.hide(); /** * Show the Plus Button if: * - Block is an initial-block (Text) * - Block is empty */ var isInitialBlock = this.Editor.Tools.isInitial(this.Editor.BlockManager.currentBlock.tool), isEmptyBlock = this.Editor.BlockManager.currentBlock.isEmpty; if (isInitialBlock && isEmptyBlock) { this.Editor.Toolbar.plusButton.show(); } } /** * Append prebuilded sprite with SVG icons */ }, { key: 'appendSVGSprite', value: function appendSVGSprite() { var spriteHolder = $.make('div'); spriteHolder.innerHTML = _sprite2.default; $.append(this.nodes.wrapper, spriteHolder); } }, { key: 'CSS', get: function get() { return { editorWrapper: 'codex-editor', editorZone: 'codex-editor__redactor' }; } }]); return UI; }(Module); // /** // * Codex Editor UI module // * // * @author Codex Team // * @version 1.2.0 // */ // // module.exports = (function (ui) { // // let editor = codex.editor; // // /** // * Basic editor classnames // */ // ui.prepare = function () { // // // }; // // /** Draw notifications holder */ // var makeNotificationHolder_ = function () { // // /** Append block with notifications to the document */ // editor.nodes.notifications = editor.notifications.createHolder(); // // }; // // // var addInlineToolbarTools_ = function () { // // var tools = { // // bold: { // icon : 'ce-icon-bold', // command : 'bold' // }, // // italic: { // icon : 'ce-icon-italic', // command : 'italic' // }, // // link: { // icon : 'ce-icon-link', // command : 'createLink' // } // }; // // var toolButton, // tool; // // for(var name in tools) { // // tool = tools[name]; // // toolButton = editor.draw.toolbarButtonInline(name, tool.icon); // // editor.nodes.inlineToolbar.buttons.appendChild(toolButton); // /** // * Add callbacks to this buttons // */ // editor.ui.setInlineToolbarButtonBehaviour(toolButton, tool.command); // // } // // }; // // /** // * @private // * Bind editor UI events // */ // var bindEvents_ = function () { // // editor.core.log('ui.bindEvents fired', 'info'); // // // window.addEventListener('error', function (errorMsg, url, lineNumber) { // // editor.notifications.errorThrown(errorMsg, event); // // }, false ); // // /** All keydowns on Document */ // editor.listeners.add(document, 'keydown', editor.callback.globalKeydown, false); // // /** All keydowns on Redactor zone */ // editor.listeners.add(editor.nodes.redactor, 'keydown', editor.callback.redactorKeyDown, false); // // /** All keydowns on Document */ // editor.listeners.add(document, 'keyup', editor.callback.globalKeyup, false ); // // /** // * Mouse click to radactor // */ // editor.listeners.add(editor.nodes.redactor, 'click', editor.callback.redactorClicked, false ); // // /** // * Clicks to the Plus button // */ // editor.listeners.add(editor.nodes.plusButton, 'click', editor.callback.plusButtonClicked, false); // // /** // * Clicks to SETTINGS button in toolbar // */ // editor.listeners.add(editor.nodes.showSettingsButton, 'click', editor.callback.showSettingsButtonClicked, false ); // // /** Bind click listeners on toolbar buttons */ // for (var button in editor.nodes.toolbarButtons) { // // editor.listeners.add(editor.nodes.toolbarButtons[button], 'click', editor.callback.toolbarButtonClicked, false); // // } // // }; // // ui.addBlockHandlers = function (block) { // // if (!block) return; // // /** // * Block keydowns // */ // editor.listeners.add(block, 'keydown', editor.callback.blockKeydown, false); // // /** // * Pasting content from another source // * We have two type of sanitization // * First - uses deep-first search algorithm to get sub nodes, // * sanitizes whole Block_content and replaces cleared nodes // * This method is deprecated // * Method is used in editor.callback.blockPaste(event) // * // * Secont - uses Mutation observer. // * Observer "observe" DOM changes and send changings to callback. // * Callback gets changed node, not whole Block_content. // * Inserted or changed node, which we've gotten have been cleared and replaced with diry node // * // * Method is used in editor.callback.blockPasteViaSanitize(event) // * // * @uses html-janitor // * @example editor.callback.blockPasteViaSanitize(event), the second method. // * // */ // editor.listeners.add(block, 'paste', editor.paste.blockPasteCallback, false); // // /** // * Show inline toolbar for selected text // */ // editor.listeners.add(block, 'mouseup', editor.toolbar.inline.show, false); // editor.listeners.add(block, 'keyup', editor.toolbar.inline.show, false); // // }; // // /** getting all contenteditable elements */ // ui.saveInputs = function () { // // var redactor = editor.nodes.redactor; // // editor.state.inputs = []; // // /** Save all inputs in global variable state */ // var inputs = redactor.querySelectorAll('[contenteditable], input, textarea'); // // Array.prototype.map.call(inputs, function (current) { // // if (!current.type || current.type == 'text' || current.type == 'textarea') { // // editor.state.inputs.push(current); // // } // // }); // // }; // // /** // * Adds first initial block on empty redactor // */ // ui.addInitialBlock = function () { // // var initialBlockType = editor.settings.initialBlockPlugin, // initialBlock; // // if ( !editor.tools[initialBlockType] ) { // // editor.core.log('Plugin %o was not implemented and can\'t be used as initial block', 'warn', initialBlockType); // return; // // } // // initialBlock = editor.tools[initialBlockType].render(); // // initialBlock.setAttribute('data-placeholder', editor.settings.placeholder); // // editor.content.insertBlock({ // type : initialBlockType, // block : initialBlock // }); // // editor.content.workingNodeChanged(initialBlock); // // }; // // ui.setInlineToolbarButtonBehaviour = function (button, type) { // // editor.listeners.add(button, 'mousedown', function (event) { // // editor.toolbar.inline.toolClicked(event, type); // // }, false); // // }; // // return ui; // // })({}); UI.displayName = 'UI'; exports.default = UI; module.exports = exports['default']; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../__module.ts */ "./src/components/__module.ts"), __webpack_require__(/*! dom */ "./src/components/dom.js"), __webpack_require__(/*! utils */ "./src/components/utils.js"))) /***/ }), /***/ "./src/components/polyfills.js": /*!*************************************!*\ !*** ./src/components/polyfills.js ***! \*************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /** * Element.closest() * * https://developer.mozilla.org/en-US/docs/Web/API/Element/closest */ if (!Element.prototype.matches) Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector; if (!Element.prototype.closest) Element.prototype.closest = function (s) { var el = this; if (!document.documentElement.contains(el)) return null; do { if (el.matches(s)) return el; el = el.parentElement || el.parentNode; } while (el !== null); return null; }; /***/ }), /***/ "./src/components/selection.js": /*!*************************************!*\ !*** ./src/components/selection.js ***! \*************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(_) { 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; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /** * Working with selection * @typedef {Selection} Selection */ var Selection = function () { /** * @constructor */ function Selection() { _classCallCheck(this, Selection); this.instance = null; this.selection = null; /** * This property can store Selection's range for restoring later * @type {Range|null} */ this.savedSelectionRange = null; } /** * Editor styles * @return {{editorWrapper: string, editorZone: string}} * @constructor */ _createClass(Selection, [{ key: 'save', /** * Save Selection's range */ value: function save() { this.savedSelectionRange = Selection.range; } /** * Restore saved Selection's range */ }, { key: 'restore', value: function restore() { if (!this.savedSelectionRange) { return; } var sel = window.getSelection(); sel.removeAllRanges(); sel.addRange(this.savedSelectionRange); } /** * Clears saved selection */ }, { key: 'clearSaved', value: function clearSaved() { this.savedSelectionRange = null; } /** * Looks ahead to find passed tag from current selection * * @param {String} tagName - tag to found * @param {String} [className] - tag's class name * @param {Number} [searchDepth] - count of tags that can be included. For better performance. * @return {HTMLElement|null} */ }, { key: 'findParentTag', value: function findParentTag(tagName, className) { var searchDepth = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 10; var selection = window.getSelection(), parentTag = null; /** * If selection is missing or no anchorNode or focusNode were found then return null */ if (!selection || !selection.anchorNode || !selection.focusNode) { return null; } /** * Define Nodes for start and end of selection */ var boundNodes = [ /** the Node in which the selection begins */ selection.anchorNode, /** the Node in which the selection ends */ selection.focusNode]; /** * For each selection parent Nodes we try to find target tag [with target class name] * It would be saved in parentTag variable */ boundNodes.forEach(function (parent) { /** Reset tags limit */ var searchDepthIterable = searchDepth; while (searchDepthIterable > 0 && parent.parentNode) { /** * Check tag's name */ if (parent.tagName === tagName) { /** * Optional additional check for class-name matching */ if (className && parent.classList && !parent.classList.contains(className)) { continue; } /** * If we have found required tag with class then save the result and go out from cycle */ parentTag = parent; break; } /** * Target tag was not found. Go up to the parent and check it */ parent = parent.parentNode; searchDepthIterable--; } }); /** * Return found tag or null */ return parentTag; } /** * Expands selection range to the passed parent node * * @param {HTMLElement} node */ }, { key: 'expandToTag', value: function expandToTag(node) { var selection = window.getSelection(); selection.removeAllRanges(); var range = document.createRange(); range.selectNodeContents(node); selection.addRange(range); } }], [{ key: 'get', /** * Returns window Selection * {@link https://developer.mozilla.org/ru/docs/Web/API/Window/getSelection} * @return {Selection} */ value: function get() { return window.getSelection(); } /** * Returns selected anchor * {@link https://developer.mozilla.org/ru/docs/Web/API/Selection/anchorNode} * @return {Node|null} */ }, { key: 'CSS', get: function get() { return { editorWrapper: 'codex-editor', editorZone: 'codex-editor__redactor' }; } }, { key: 'anchorNode', get: function get() { var selection = window.getSelection(); return selection ? selection.anchorNode : null; } /** * Returns selection offset according to the anchor node * {@link https://developer.mozilla.org/ru/docs/Web/API/Selection/anchorOffset} * @return {Number|null} */ }, { key: 'anchorOffset', get: function get() { var selection = window.getSelection(); return selection ? selection.anchorOffset : null; } /** * Is current selection range collapsed * @return {boolean|null} */ }, { key: 'isCollapsed', get: function get() { var selection = window.getSelection(); return selection ? selection.isCollapsed : null; } /** * Check current selection if it is at Editor's zone * @return {boolean} */ }, { key: 'isAtEditor', get: function get() { var selection = Selection.get(), selectedNode = void 0, editorZone = false; /** * Something selected on document */ selectedNode = selection.anchorNode || selection.focusNode; if (selectedNode && selectedNode.nodeType === Node.TEXT_NODE) { selectedNode = selectedNode.parentNode; } if (selectedNode) { editorZone = selectedNode.closest('.' + Selection.CSS.editorZone); } /** * Selection is not out of Editor because Editor's wrapper was found */ return editorZone && editorZone.nodeType === Node.ELEMENT_NODE; } /** * Return first range * @return {Range|null} */ }, { key: 'range', get: function get() { var selection = window.getSelection(); return selection && selection.rangeCount ? selection.getRangeAt(0) : null; } /** * Calculates position and size of selected text * @return {{x, y, width, height, top?, left?, bottom?, right?}} */ }, { key: 'rect', get: function get() { var sel = document.selection, range = void 0; var rect = { x: 0, y: 0, width: 0, height: 0 }; if (sel && sel.type !== 'Control') { range = sel.createRange(); rect.x = range.boundingLeft; rect.y = range.boundingTop; rect.width = range.boundingWidth; rect.height = range.boundingHeight; return rect; } if (!window.getSelection) { _.log('Method window.getSelection is not supported', 'warn'); return rect; } sel = window.getSelection(); if (!sel.rangeCount) { _.log('Method Selection.rangeCount() is not supported', 'warn'); return rect; } range = sel.getRangeAt(0).cloneRange(); if (range.getBoundingClientRect) { rect = range.getBoundingClientRect(); } // Fall back to inserting a temporary element if (rect.x === 0 && rect.y === 0) { var span = document.createElement('span'); if (span.getBoundingClientRect) { // Ensure span has dimensions and position by // adding a zero-width space character span.appendChild(document.createTextNode('\u200B')); range.insertNode(span); rect = span.getBoundingClientRect(); var spanParent = span.parentNode; spanParent.removeChild(span); // Glue any broken text nodes back together spanParent.normalize(); } } return rect; } /** * Returns selected text as String * @returns {string} */ }, { key: 'text', get: function get() { return window.getSelection ? window.getSelection().toString() : ''; } }]); return Selection; }(); Selection.displayName = 'Selection'; exports.default = Selection; module.exports = exports['default']; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! utils */ "./src/components/utils.js"))) /***/ }), /***/ "./src/components/utils.js": /*!*********************************!*\ !*** ./src/components/utils.js ***! \*********************************/ /*! no static exports found */ /***/ (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; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /** * Codex Editor Util */ var Util = function () { function Util() { _classCallCheck(this, Util); } _createClass(Util, null, [{ key: 'log', /** * Custom logger * * @param {string} msg - message * @param {string} type - logging type 'log'|'warn'|'error'|'info' * @param {*} args - argument to log with a message */ value: function log(msg, type, args) { type = type || 'log'; if (!args) { args = msg || 'undefined'; msg = '[codex-editor]: %o'; } else { msg = '[codex-editor]: ' + msg; } try { if ('console' in window && window.console[type]) { if (args) window.console[type](msg, args);else window.console[type](msg); } } catch (e) { // do nothing } } /** * Returns basic keycodes as constants * @return {{}} */ }, { key: 'sequence', /** * @typedef {Object} ChainData * @property {Object} data - data that will be passed to the success or fallback * @property {Function} function - function's that must be called asynchronically */ /** * Fires a promise sequence asyncronically * * @param {Object[]} chains - list or ChainData's * @param {Function} success - success callback * @param {Function} fallback - callback that fires in case of errors * * @return {Promise} */ value: function sequence(chains) { var success = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {}; var fallback = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : function () {}; return new Promise(function (resolve) { /** * pluck each element from queue * First, send resolved Promise as previous value * Each plugins "prepare" method returns a Promise, that's why * reduce current element will not be able to continue while can't get * a resolved Promise */ chains.reduce(function (previousValue, currentValue, iteration) { return previousValue.then(function () { return waitNextBlock(currentValue, success, fallback); }).then(function () { // finished if (iteration === chains.length - 1) { resolve(); } }); }, Promise.resolve()); }); /** * Decorator * * @param {ChainData} chainData * * @param {Function} successCallback * @param {Function} fallbackCallback * * @return {Promise} */ function waitNextBlock(chainData, successCallback, fallbackCallback) { return new Promise(function (resolve) { chainData.function().then(function () { successCallback(chainData.data || {}); }).then(resolve).catch(function () { fallbackCallback(chainData.data || {}); // anyway, go ahead even it falls resolve(); }); }); } } /** * Make array from array-like collection * * @param {*} collection * * @return {Array} */ }, { key: 'array', value: function array(collection) { return Array.prototype.slice.call(collection); } /** * Checks if object is empty * * @param {Object} object * @return {boolean} */ }, { key: 'isEmpty', value: function isEmpty(object) { return Object.keys(object).length === 0 && object.constructor === Object; } /** * Check if passed object is a Promise * @param {*} object - object to check * @return {Boolean} */ }, { key: 'isPromise', value: function isPromise(object) { return Promise.resolve(object) === object; } /** * Check if passed element is contenteditable * @param element * @return {boolean} */ }, { key: 'isContentEditable', value: function isContentEditable(element) { return element.contentEditable === 'true'; } /** * Delays method execution * * @param method * @param timeout */ }, { key: 'delay', value: function delay(method, timeout) { return function () { var context = this, args = arguments; window.setTimeout(function () { return method.apply(context, args); }, timeout); }; } }, { key: 'keyCodes', get: function get() { return { BACKSPACE: 8, TAB: 9, ENTER: 13, SHIFT: 16, CTRL: 17, ALT: 18, ESC: 27, SPACE: 32, LEFT: 37, UP: 38, DOWN: 40, RIGHT: 39, DELETE: 46, META: 91 }; } }]); return Util; }(); Util.displayName = 'Util'; exports.default = Util; ; module.exports = exports['default']; /***/ }), /***/ "./src/styles/main.css": /*!*****************************!*\ !*** ./src/styles/main.css ***! \*****************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { exports = module.exports = __webpack_require__(/*! ../../node_modules/css-loader/lib/css-base.js */ "./node_modules/css-loader/lib/css-base.js")(false); // imports // module exports.push([module.i, ":root {\r\n /**\r\n * Selection color\r\n */\r\n --selectionColor: rgba(61,166,239,0.63);\r\n\r\n /**\r\n * Toolbar buttons\r\n */\r\n --bg-light: #eff2f5;\r\n\r\n /**\r\n * All gray texts: placeholders, settings\r\n */\r\n --grayText: #707684;\r\n\r\n /** Blue icons */\r\n --color-active-icon: #388AE5;\r\n\r\n /**\r\n * Block content width\r\n */\r\n --content-width: 650px;\r\n\r\n /**\r\n * Toolbar buttons height and width\r\n */\r\n --toolbar-buttons-size: 34px;\r\n\r\n /**\r\n * Toolbar Plus Button and Toolbox buttons height and width\r\n */\r\n --toolbox-buttons-size: 20px;\r\n\r\n /**\r\n * Confirm deletion bg\r\n */\r\n --color-confirm: #E24A4A;\r\n}\r\n/**\r\n* Editor wrapper\r\n*/\r\n.codex-editor {\r\n position: relative;\r\n box-sizing: border-box;\r\n}\r\n.codex-editor .hide {\r\n display: none;\r\n }\r\n.codex-editor__redactor {\r\n padding-bottom: 300px;\r\n }\r\n.codex-editor svg {\r\n fill: currentColor;\r\n vertical-align: middle;\r\n max-height: 100%;\r\n }\r\n/**\r\n * Set color for native selection\r\n */\r\n::-moz-selection{\r\n background-color: rgba(61,166,239,0.63);\r\n background-color: var(--selectionColor);\r\n}\r\n::selection{\r\n background-color: rgba(61,166,239,0.63);\r\n background-color: var(--selectionColor);\r\n}\r\n/**\r\n * Add placeholder to content editable elements with data attribute\r\n * data-placeholder=\"Hello world!\"\r\n */\r\n[contentEditable=true][data-placeholder]:empty:not(:focus):before{\r\n content: attr(data-placeholder);\r\n color: #707684;\r\n color: var(--grayText);\r\n}\r\n.ce-toolbar {\r\n position: absolute;\r\n left: 0;\r\n right: 0;\r\n top: 0;\r\n /*opacity: 0;*/\r\n /*visibility: hidden;*/\r\n transition: opacity 100ms ease;\r\n will-change: opacity, transform;\r\n display: none;\r\n}\r\n.ce-toolbar--opened {\r\n display: block;\r\n /*opacity: 1;*/\r\n /*visibility: visible;*/\r\n }\r\n.ce-toolbar__content {\r\n max-width: 650px;\r\n max-width: var(--content-width);\r\n margin: 0 auto;\r\n position: relative;\r\n }\r\n.ce-toolbar__plus {\r\n color: #707684;\r\n color: var(--grayText);\r\n cursor: pointer;\r\n display: inline-block;\r\n width: 20px;\r\n width: var(--toolbox-buttons-size);\r\n height: 20px;\r\n height: var(--toolbox-buttons-size);\r\n line-height: 20px;\r\n line-height: var(--toolbox-buttons-size)\r\n }\r\n.ce-toolbar__plus:not(:last-of-type){\r\n margin-right: 3px;\r\n }\r\n.ce-toolbar__plus:hover {\r\n color: #388AE5;\r\n color: var(--color-active-icon);\r\n }\r\n.ce-toolbar__plus {\r\n\r\n position: absolute;\r\n top: -1px;\r\n left: calc(calc(20px + 10px) * -1);\r\n left: calc(calc(var(--toolbox-buttons-size) + 10px) * -1);\r\n }\r\n.ce-toolbar__plus--hidden {\r\n display: none;\r\n }\r\n/**\r\n * Block actions Zone\r\n * -------------------------\r\n */\r\n.ce-toolbar__actions {\r\n position: absolute;\r\n right: 0;\r\n top: 0;\r\n padding-right: 16px;\r\n }\r\n.ce-toolbar__actions-buttons {\r\n text-align: right;\r\n }\r\n.ce-toolbar__settings-btn {\r\n display: inline-block;\r\n width: 24px;\r\n height: 24px;\r\n color: #707684;\r\n color: var(--grayText);\r\n cursor: pointer;\r\n }\r\n.ce-toolbox {\r\n position: absolute;\r\n visibility: hidden;\r\n transition: opacity 100ms ease;\r\n will-change: opacity;\r\n}\r\n.ce-toolbox--opened {\r\n opacity: 1;\r\n visibility: visible;\r\n }\r\n.ce-toolbox__button {\r\n color: #707684;\r\n color: var(--grayText);\r\n cursor: pointer;\r\n display: inline-block;\r\n width: 20px;\r\n width: var(--toolbox-buttons-size);\r\n height: 20px;\r\n height: var(--toolbox-buttons-size);\r\n line-height: 20px;\r\n line-height: var(--toolbox-buttons-size);\r\n }\r\n.ce-toolbox__button:not(:last-of-type){\r\n margin-right: 3px;\r\n }\r\n.ce-toolbox__button:hover {\r\n color: #388AE5;\r\n color: var(--color-active-icon);\r\n }\r\n.ce-inline-toolbar {\r\n position: absolute;\r\n background-color: #FFFFFF;\r\n box-shadow: 0 8px 23px -6px rgba(21,40,54,0.31), 22px -14px 34px -18px rgba(33,48,73,0.26);\r\n border-radius: 4px;\r\n z-index: 2\r\n}\r\n.ce-inline-toolbar::before {\r\n content: '';\r\n width: 15px;\r\n height: 15px;\r\n position: absolute;\r\n top: -7px;\r\n left: 50%;\r\n margin-left: -7px;\r\n transform: rotate(-45deg);\r\n background-color: #fff;\r\n z-index: -1;\r\n }\r\n.ce-inline-toolbar {\r\n padding: 6px;\r\n transform: translateX(-50%);\r\n display: none;\r\n box-shadow: 0 6px 12px -6px rgba(131, 147, 173, 0.46),\r\n 5px -12px 34px -13px rgba(97, 105, 134, 0.6),\r\n 0 26px 52px 3px rgba(147, 165, 186, 0.24);\r\n}\r\n.ce-inline-toolbar--showed {\r\n display: block;\r\n }\r\n.ce-inline-tool {\r\n display: inline-block;\r\n width: 34px;\r\n height: 34px;\r\n line-height: 34px;\r\n text-align: center;\r\n border-radius: 3px;\r\n cursor: pointer;\r\n border: 0;\r\n outline: none;\r\n background-color: transparent;\r\n vertical-align: bottom;\r\n color: #707684;\r\n color: var(--grayText)\r\n}\r\n.ce-inline-tool:not(:last-of-type){\r\n margin-right: 5px;\r\n }\r\n.ce-inline-tool:hover {\r\n background-color: #eff2f5;\r\n background-color: var(--bg-light);\r\n }\r\n.ce-inline-tool {\r\n line-height: normal;\r\n}\r\n.ce-inline-tool--active {\r\n color: #388AE5;\r\n color: var(--color-active-icon);\r\n }\r\n.ce-inline-tool--link .icon {\r\n margin-top: -2px;\r\n }\r\n.ce-inline-tool--link .icon--unlink {\r\n display: none;\r\n }\r\n.ce-inline-tool--unlink .icon--link {\r\n display: none;\r\n }\r\n.ce-inline-tool--unlink .icon--unlink {\r\n display: inline-block;\r\n }\r\n.ce-inline-tool-input {\r\n background-color: #eff2f5;\r\n background-color: var(--bg-light);\r\n outline: none;\r\n border: 0;\r\n border-radius: 3px;\r\n margin: 6px 0 0;\r\n font-size: 13px;\r\n padding: 8px;\r\n width: 100%;\r\n box-sizing: border-box;\r\n display: none\r\n }\r\n.ce-inline-tool-input::-webkit-input-placeholder {\r\n color: #707684;\r\n color: var(--grayText);\r\n }\r\n.ce-inline-tool-input:-ms-input-placeholder {\r\n color: #707684;\r\n color: var(--grayText);\r\n }\r\n.ce-inline-tool-input::placeholder {\r\n color: #707684;\r\n color: var(--grayText);\r\n }\r\n.ce-inline-tool-input--showed {\r\n display: block;\r\n }\r\n.ce-settings {\r\n position: absolute;\r\n background-color: #FFFFFF;\r\n box-shadow: 0 8px 23px -6px rgba(21,40,54,0.31), 22px -14px 34px -18px rgba(33,48,73,0.26);\r\n border-radius: 4px;\r\n z-index: 2\r\n}\r\n.ce-settings::before {\r\n content: '';\r\n width: 15px;\r\n height: 15px;\r\n position: absolute;\r\n top: -7px;\r\n left: 50%;\r\n margin-left: -7px;\r\n transform: rotate(-45deg);\r\n background-color: #fff;\r\n z-index: -1;\r\n }\r\n.ce-settings {\r\n right: 5px;\r\n top: 35px;\r\n min-width: 124px\r\n}\r\n.ce-settings::before{\r\n left: auto;\r\n right: 12px;\r\n }\r\n.ce-settings {\r\n\r\n display: none;\r\n}\r\n.ce-settings--opened {\r\n display: block;\r\n }\r\n.ce-settings__plugin-zone:not(:empty){\r\n padding: 6px 6px 0;\r\n }\r\n.ce-settings__default-zone:not(:empty){\r\n padding: 6px;\r\n }\r\n.ce-settings__button {\r\n display: inline-block;\r\n width: 34px;\r\n height: 34px;\r\n line-height: 34px;\r\n text-align: center;\r\n border-radius: 3px;\r\n cursor: pointer;\r\n border: 0;\r\n outline: none;\r\n background-color: transparent;\r\n vertical-align: bottom;\r\n color: #707684;\r\n color: var(--grayText)\r\n }\r\n.ce-settings__button:not(:last-of-type){\r\n margin-right: 5px;\r\n }\r\n.ce-settings__button:hover {\r\n background-color: #eff2f5;\r\n background-color: var(--bg-light);\r\n }\r\n.ce-settings__button--active {\r\n color: #388AE5;\r\n color: var(--color-active-icon);\r\n }\r\n.ce-settings__button--disabled {\r\n cursor: not-allowed !important;\r\n opacity: .3;\r\n }\r\n.ce-settings__button--selected {\r\n color: #388AE5;\r\n color: var(--color-active-icon);\r\n }\r\n.ce-settings__button--delete {\r\n transition: background-color 300ms ease;\r\n will-change: background-color;\r\n }\r\n.ce-settings__button--delete .icon {\r\n transition: transform 200ms ease-out;\r\n will-change: transform;\r\n }\r\n.ce-settings__button--confirm {\r\n background-color: #E24A4A;\r\n background-color: var(--color-confirm);\r\n color: #fff\r\n }\r\n.ce-settings__button--confirm:hover {\r\n background-color: rgb(213, 74, 74) !important;\r\n background-color: rgb(213, 74, 74) !important;\r\n }\r\n.ce-settings__button--confirm .icon {\r\n transform: rotate(90deg);\r\n }\r\n.ce-block:first-of-type {\r\n margin-top: 0;\r\n }\r\n.ce-block--selected {\r\n background-image: linear-gradient(17deg, rgba(243, 248, 255, 0.03) 63.45%, rgba(207, 214, 229, 0.27) 98%);\r\n border-radius: 3px;\r\n }\r\n.ce-block__content {\r\n max-width: 650px;\r\n max-width: var(--content-width);\r\n margin: 0 auto;\r\n }\r\n.wobble {\r\n animation-name: wobble;\r\n animation-duration: 400ms;\r\n}\r\n/**\r\n * @author Nick Pettit - https://github.com/nickpettit/glide\r\n */\r\n@keyframes wobble {\r\n from {\r\n transform: translate3d(0, 0, 0);\r\n }\r\n\r\n 15% {\r\n transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -5deg);\r\n }\r\n\r\n 30% {\r\n transform: translate3d(2%, 0, 0) rotate3d(0, 0, 1, 3deg);\r\n }\r\n\r\n 45% {\r\n transform: translate3d(-3%, 0, 0) rotate3d(0, 0, 1, -3deg);\r\n }\r\n\r\n 60% {\r\n transform: translate3d(2%, 0, 0) rotate3d(0, 0, 1, 2deg);\r\n }\r\n\r\n 75% {\r\n transform: translate3d(-1%, 0, 0) rotate3d(0, 0, 1, -1deg);\r\n }\r\n\r\n to {\r\n transform: translate3d(0, 0, 0);\r\n }\r\n}\r\n", ""]); // exports /***/ }) /******/ }); }); //# sourceMappingURL=codex-editor.js.map