UNPKG

tui-editor

Version:

GFM Markdown Wysiwyg Editor - Productive and Extensible

1,631 lines (1,324 loc) 973 kB
/*! * tui-editor * @version 1.4.6 * @author NHN FE Development Lab <dl_javascript@nhn.com> (https://nhn.github.io/tui.editor/) * @license MIT */ (function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(require("jquery"), require("tui-code-snippet"), require("codemirror"), require("markdown-it"), require("to-mark"), require("highlight.js"), require("squire-rte")); else if(typeof define === 'function' && define.amd) define(["jquery", "tui-code-snippet", "codemirror", "markdown-it", "to-mark", "highlight.js", "squire-rte"], factory); else if(typeof exports === 'object') exports["Editor"] = factory(require("jquery"), require("tui-code-snippet"), require("codemirror"), require("markdown-it"), require("to-mark"), require("highlight.js"), require("squire-rte")); else root["tui"] = root["tui"] || {}, root["tui"]["Editor"] = factory(root["$"], (root["tui"] && root["tui"]["util"]), root["CodeMirror"], root["markdownit"], root["toMark"], root["hljs"], root["Squire"]); })(typeof self !== 'undefined' ? self : this, function(__WEBPACK_EXTERNAL_MODULE_0__, __WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_6__, __WEBPACK_EXTERNAL_MODULE_22__, __WEBPACK_EXTERNAL_MODULE_23__, __WEBPACK_EXTERNAL_MODULE_32__, __WEBPACK_EXTERNAL_MODULE_67__) { 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, { /******/ configurable: false, /******/ enumerable: true, /******/ get: getter /******/ }); /******/ } /******/ }; /******/ /******/ // 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 = "dist/"; /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = 45); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ (function(module, exports) { module.exports = __WEBPACK_EXTERNAL_MODULE_0__; /***/ }), /* 1 */ /***/ (function(module, exports) { module.exports = __WEBPACK_EXTERNAL_MODULE_1__; /***/ }), /* 2 */ /***/ (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; }; }(); /** * @fileoverview Implements CommandManager * @author NHN FE Development Lab <dl_javascript@nhn.com> */ var _jquery = __webpack_require__(0); var _jquery2 = _interopRequireDefault(_jquery); var _tuiCodeSnippet = __webpack_require__(1); var _tuiCodeSnippet2 = _interopRequireDefault(_tuiCodeSnippet); var _command = __webpack_require__(21); var _command2 = _interopRequireDefault(_command); var _util = __webpack_require__(14); 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"); } } var KEYMAP_OS_INDEX = _util.isMac ? 1 : 0; /** * Class CommandManager */ var CommandManager = function () { /** * @param {ToastUIEditor} base nedInstance * @param {object} [options={}] - option object * @param {boolean} [options.useCommandShortcut=true] - execute command with keyMap */ function CommandManager(base) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; _classCallCheck(this, CommandManager); this._command = new _tuiCodeSnippet2.default.Map(); this._mdCommand = new _tuiCodeSnippet2.default.Map(); this._wwCommand = new _tuiCodeSnippet2.default.Map(); this._options = _jquery2.default.extend({ 'useCommandShortcut': true }, options); this.base = base; this.keyMapCommand = {}; this._initEvent(); } /** * You can change command before command addition by addCommandBefore event. * @param {object} command - command * @returns {object} * @private */ _createClass(CommandManager, [{ key: '_addCommandBefore', value: function _addCommandBefore(command) { var commandWrapper = { command: command }; this.base.eventManager.emit('addCommandBefore', commandWrapper); return commandWrapper.command || command; } /** * Add command * @memberof CommandManager * @param {Command} command Command instance * @returns {Command} Command */ }, { key: 'addCommand', value: function addCommand(command) { for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { args[_key - 1] = arguments[_key]; } if (args.length) { command = CommandManager.command.apply(CommandManager, [command].concat(args)); } command = this._addCommandBefore(command); var name = command.getName(); var commandBase = void 0; if (command.isMDType()) { commandBase = this._mdCommand; } else if (command.isWWType()) { commandBase = this._wwCommand; } else if (command.isGlobalType()) { commandBase = this._command; } commandBase.set(name, command); if (command.keyMap) { this.keyMapCommand[command.keyMap[KEYMAP_OS_INDEX]] = name; } return command; } /** * _initEvent * Bind event handler to eventManager * @private * @memberof CommandManager */ }, { key: '_initEvent', value: function _initEvent() { var _this = this; this.base.eventManager.listen('command', function () { _this.exec.apply(_this, arguments); }); this.base.eventManager.listen('keyMap', function (ev) { if (!_this._options.useCommandShortcut) { return; } var command = _this.keyMapCommand[ev.keyMap]; if (command) { ev.data.preventDefault(); _this.exec(command); } }); } /** * Execute command * @memberof CommandManager * @param {String} name Command name * @param {*} ...args Command argument * @returns {*} */ }, { key: 'exec', value: function exec(name) { var commandToRun = void 0, result = void 0; var context = this.base; commandToRun = this._command.get(name); if (!commandToRun) { if (this.base.isMarkdownMode()) { commandToRun = this._mdCommand.get(name); context = this.base.mdEditor; } else { commandToRun = this._wwCommand.get(name); context = this.base.wwEditor; } } if (commandToRun) { var _commandToRun; for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { args[_key2 - 1] = arguments[_key2]; } args.unshift(context); result = (_commandToRun = commandToRun).exec.apply(_commandToRun, args); } return result; } }]); return CommandManager; }(); /** * Create command by given editor type and property object * @memberof CommandManager * @param {string} type Command type * @param {{name: string, keyMap: Array}} props Property * @returns {*} */ CommandManager.command = function (type, props) { var command = _command2.default.factory(type, props.name, props.keyMap); _tuiCodeSnippet2.default.extend(command, props); return command; }; exports.default = CommandManager; /***/ }), /* 3 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.I18n = undefined; 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; }; }(); /** * @fileoverview Implements i18n * @author NHN FE Development Lab <dl_javascript@nhn.com> */ var _tuiCodeSnippet = __webpack_require__(1); var _tuiCodeSnippet2 = _interopRequireDefault(_tuiCodeSnippet); 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"); } } var sharedInstance = void 0; var DEFAULT_CODE = 'en_US'; /** * Class I18n */ var I18n = function () { /** * Creates an instance of I18n. * @memberof I18n */ function I18n() { _classCallCheck(this, I18n); this._code = DEFAULT_CODE; this._langs = new _tuiCodeSnippet2.default.Map(); } /** * Set locale code * @param {string} code locale code */ _createClass(I18n, [{ key: 'setCode', value: function setCode(code) { this._code = code; } /** * Set language set * @param {string|string[]} codes locale code * @param {object} data language set */ }, { key: 'setLanguage', value: function setLanguage(codes, data) { var _this = this; codes = [].concat(codes); codes.forEach(function (code) { if (!_this._langs.has(code)) { _this._langs.set(code, data); } else { var langData = _this._langs.get(code); _this._langs.set(code, _tuiCodeSnippet2.default.extend(langData, data)); } }); } /** * Get text of key * @param {string} key key of text * @param {string} code locale code * @returns {string} */ }, { key: 'get', value: function get(key, code) { if (!code) { code = this._code; } var langSet = this._langs.get(code); if (!langSet) { langSet = this._langs.get(DEFAULT_CODE); } var text = langSet[key]; if (!text) { throw new Error('There is no text key "' + key + '" in ' + code); } return text; } }], [{ key: 'getSharedInstance', value: function getSharedInstance() { if (!sharedInstance) { sharedInstance = new I18n(); } return sharedInstance; } }]); return I18n; }(); exports.I18n = I18n; exports.default = new I18n(); /***/ }), /* 4 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var _jquery = __webpack_require__(0); var _jquery2 = _interopRequireDefault(_jquery); var _tuiCodeSnippet = __webpack_require__(1); var _tuiCodeSnippet2 = _interopRequireDefault(_tuiCodeSnippet); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * @fileoverview DOM Utils * @author NHN FE Development Lab <dl_javascript@nhn.com> */ var FIND_ZWB = /\u200B/g; /** * isTextNode * Check if node is text node * @param {Node} node node to check * @returns {boolean} result * @ignore */ var isTextNode = function isTextNode(node) { return node && node.nodeType === Node.TEXT_NODE; }; /** * isElemNode * Check if node is element node * @param {Node} node node to check * @returns {boolean} result * @ignore */ var isElemNode = function isElemNode(node) { return node && node.nodeType === Node.ELEMENT_NODE; }; /** * Check that the node is block node * @param {Node} node node * @returns {boolean} * @ignore */ var isBlockNode = function isBlockNode(node) { return (/^(ADDRESS|ARTICLE|ASIDE|BLOCKQUOTE|DETAILS|DIALOG|DD|DIV|DL|DT|FIELDSET|FIGCAPTION|FIGURE|FOOTER|FORM|H[\d]|HEADER|HGROUP|HR|LI|MAIN|NAV|OL|P|PRE|SECTION|UL)$/ig.test(this.getNodeName(node)) ); }; /** * getNodeName * Get node name of node * @param {Node} node node * @returns {string} node name * @ignore */ var getNodeName = function getNodeName(node) { if (isElemNode(node)) { return node.tagName; } return 'TEXT'; }; /** * getTextLength * Get node offset length of node(for Range API) * @param {Node} node node * @returns {number} length * @ignore */ var getTextLength = function getTextLength(node) { var len = void 0; if (isElemNode(node)) { len = node.textContent.replace(FIND_ZWB, '').length; } else if (isTextNode(node)) { len = node.nodeValue.replace(FIND_ZWB, '').length; } return len; }; /** * getOffsetLength * Get node offset length of node(for Range API) * @param {Node} node node * @returns {number} length * @ignore */ var getOffsetLength = function getOffsetLength(node) { var len = void 0; if (isElemNode(node)) { len = node.childNodes.length; } else if (isTextNode(node)) { len = node.nodeValue.replace(FIND_ZWB, '').length; } return len; }; /** * getNodeOffsetOfParent * get node offset between parent's childnodes * @param {Node} node node * @returns {number} offset(index) * @ignore */ var getNodeOffsetOfParent = function getNodeOffsetOfParent(node) { var childNodesOfParent = node.parentNode.childNodes; var i = void 0, t = void 0, found = void 0; for (i = 0, t = childNodesOfParent.length; i < t; i += 1) { if (childNodesOfParent[i] === node) { found = i; break; } } return found; }; /** * getChildNodeByOffset * get child node by offset * @param {Node} node node * @param {number} index offset index * @returns {Node} foudned node * @ignore */ var getChildNodeByOffset = function getChildNodeByOffset(node, index) { var currentNode = void 0; if (isTextNode(node)) { currentNode = node; } else if (node.childNodes.length && index >= 0) { currentNode = node.childNodes[index]; } return currentNode; }; /** * getNodeWithDirectionUntil * find next node from passed node * @param {strong} direction previous or next * @param {Node} node node * @param {string} untilNodeName parent node name to limit * @returns {Node} founded node * @ignore */ var getNodeWithDirectionUntil = function getNodeWithDirectionUntil(direction, node, untilNodeName) { var directionKey = direction + 'Sibling'; var nodeName = void 0, foundedNode = void 0; while (node && !node[directionKey]) { nodeName = getNodeName(node.parentNode); if (nodeName === untilNodeName || nodeName === 'BODY') { break; } node = node.parentNode; } if (node[directionKey]) { foundedNode = node[directionKey]; } return foundedNode; }; /** * getPrevOffsetNodeUntil * get prev node of childnode pointed with index * @param {Node} node node * @param {number} index offset index * @param {string} untilNodeName parent node name to limit * @returns {Node} founded node * @ignore */ var getPrevOffsetNodeUntil = function getPrevOffsetNodeUntil(node, index, untilNodeName) { var prevNode = void 0; if (index > 0) { prevNode = getChildNodeByOffset(node, index - 1); } else { prevNode = getNodeWithDirectionUntil('previous', node, untilNodeName); } return prevNode; }; var getParentUntilBy = function getParentUntilBy(node, matchCondition, stopCondition) { var foundedNode = void 0; while (node.parentNode && !matchCondition(node.parentNode)) { node = node.parentNode; if (stopCondition && stopCondition(node.parentNode)) { break; } } if (matchCondition(node.parentNode)) { foundedNode = node; } return foundedNode; }; /** * getParentUntil * get parent node until paseed node name * @param {Node} node node * @param {string|HTMLNode} untilNode node name or node to limit * @returns {Node} founded node * @ignore */ var getParentUntil = function getParentUntil(node, untilNode) { var foundedNode = void 0; if (_tuiCodeSnippet2.default.isString(untilNode)) { foundedNode = getParentUntilBy(node, function (targetNode) { return untilNode === getNodeName(targetNode); }); } else { foundedNode = getParentUntilBy(node, function (targetNode) { return untilNode === targetNode; }); } return foundedNode; }; /** * getNodeWithDirectionUnderParent * get node on the given direction under given parent * @param {strong} direction previous or next * @param {Node} node node * @param {string|Node} underNode parent node name to limit * @returns {Node} founded node * @ignore */ var getNodeWithDirectionUnderParent = function getNodeWithDirectionUnderParent(direction, node, underNode) { var directionKey = direction + 'Sibling'; var foundedNode = void 0; node = getParentUntil(node, underNode); if (node && node[directionKey]) { foundedNode = node[directionKey]; } return foundedNode; }; /** * getTopPrevNodeUnder * get top previous top level node under given node * @param {Node} node node * @param {Node} underNode underNode * @returns {Node} founded node * @ignore */ var getTopPrevNodeUnder = function getTopPrevNodeUnder(node, underNode) { return getNodeWithDirectionUnderParent('previous', node, underNode); }; /** * getNextTopBlockNode * get next top level block node * @param {Node} node node * @param {Node} underNode underNode * @returns {Node} founded node * @ignore */ var getTopNextNodeUnder = function getTopNextNodeUnder(node, underNode) { return getNodeWithDirectionUnderParent('next', node, underNode); }; /** * Get parent element the body element * @param {Node} node Node for start searching * @returns {Node} * @ignore */ var getTopBlockNode = function getTopBlockNode(node) { return getParentUntil(node, 'BODY'); }; /** * Get previous text node * @param {Node} node Node for start searching * @returns {Node} * @ignore */ var getPrevTextNode = function getPrevTextNode(node) { node = node.previousSibling || node.parentNode; while (!isTextNode(node) && getNodeName(node) !== 'BODY') { if (node.previousSibling) { node = node.previousSibling; while (node.lastChild) { node = node.lastChild; } } else { node = node.parentNode; } } if (getNodeName(node) === 'BODY') { node = null; } return node; }; /** * test whether root contains the given node * @param {HTMLNode} root - root node * @param {HTMLNode} node - node to test * @returns {Boolean} true if root contains node */ var containsNode = function containsNode(root, node) { var walker = document.createTreeWalker(root, 4, null, false); var found = root === node; while (!found && walker.nextNode()) { found = walker.currentNode === node; } return found; }; /** * find node by offset * @param {HTMLElement} root Root element * @param {Array.<number>} offsetList offset list * @param {function} textNodeFilter Text node filter * @returns {Array} * @ignore */ var findOffsetNode = function findOffsetNode(root, offsetList, textNodeFilter) { var result = []; var text = ''; var walkerOffset = 0; var newWalkerOffset = void 0; if (!offsetList.length) { return result; } var offset = offsetList.shift(); var walker = document.createTreeWalker(root, 4, null, false); while (walker.nextNode()) { text = walker.currentNode.nodeValue || ''; if (textNodeFilter) { text = textNodeFilter(text); } newWalkerOffset = walkerOffset + text.length; while (newWalkerOffset >= offset) { result.push({ container: walker.currentNode, offsetInContainer: offset - walkerOffset, offset: offset }); if (!offsetList.length) { return result; } offset = offsetList.shift(); } walkerOffset = newWalkerOffset; } // there should be offset left do { result.push({ container: walker.currentNode, offsetInContainer: text.length, offset: offset }); offset = offsetList.shift(); } while (!_tuiCodeSnippet2.default.isUndefined(offset)); return result; }; var getNodeInfo = function getNodeInfo(node) { var path = {}; path.tagName = node.nodeName; if (node.id) { path.id = node.id; } var className = node.className.trim(); if (className) { path.className = className; } return path; }; var getPath = function getPath(node, root) { var paths = []; while (node && node !== root) { if (isElemNode(node)) { paths.unshift(getNodeInfo(node)); } node = node.parentNode; } return paths; }; /** * Find next, previous TD or TH element by given TE element * @param {HTMLElement} node TD element * @param {string} direction 'next' or 'previous' * @returns {HTMLElement|null} * @ignore */ var getTableCellByDirection = function getTableCellByDirection(node, direction) { var targetElement = null; if (!_tuiCodeSnippet2.default.isUndefined(direction) && (direction === 'next' || direction === 'previous')) { if (direction === 'next') { targetElement = node.nextElementSibling; } else { targetElement = node.previousElementSibling; } } return targetElement; }; /** * Find sibling TR's TD element by given TD and direction * @param {HTMLElement} node TD element * @param {string} direction Boolean value for find first TD in next line * @param {boolean} [needEdgeCell=false] Boolean value for find first TD in next line * @returns {HTMLElement|null} * @ignore */ var getSiblingRowCellByDirection = function getSiblingRowCellByDirection(node, direction, needEdgeCell) { var tableCellElement = null; var $node = void 0, index = void 0, $targetRowElement = void 0, $currentContainer = void 0, $siblingContainer = void 0, isSiblingContainerExists = void 0; if (!_tuiCodeSnippet2.default.isUndefined(direction) && (direction === 'next' || direction === 'previous')) { if (node) { $node = (0, _jquery2.default)(node); if (direction === 'next') { $targetRowElement = $node.parent().next(); $currentContainer = $node.parents('thead'); $siblingContainer = $currentContainer[0] && $currentContainer.next(); isSiblingContainerExists = $siblingContainer && getNodeName($siblingContainer[0]) === 'TBODY'; index = 0; } else { $targetRowElement = $node.parent().prev(); $currentContainer = $node.parents('tbody'); $siblingContainer = $currentContainer[0] && $currentContainer.prev(); isSiblingContainerExists = $siblingContainer && getNodeName($siblingContainer[0]) === 'THEAD'; index = node.parentNode.childNodes.length - 1; } if (_tuiCodeSnippet2.default.isUndefined(needEdgeCell) || !needEdgeCell) { index = getNodeOffsetOfParent(node); } if ($targetRowElement[0]) { tableCellElement = $targetRowElement.children('td,th')[index]; } else if ($currentContainer[0] && isSiblingContainerExists) { tableCellElement = $siblingContainer.find('td,th')[index]; } } } return tableCellElement; }; /** * Check that the inline node is supported by markdown * @param {Node} node TD element * @returns {boolean} * @ignore */ var isMDSupportInlineNode = function isMDSupportInlineNode(node) { return (/^(A|B|BR|CODE|DEL|EM|I|IMG|S|SPAN|STRONG)$/ig.test(node.nodeName) ); }; /** * Check that node is styled node. * Styled node is a node that has text and decorates text. * @param {Node} node TD element * @returns {boolean} * @ignore */ var isStyledNode = function isStyledNode(node) { return (/^(A|ABBR|ACRONYM|B|BDI|BDO|BIG|CITE|CODE|DEL|DFN|EM|I|INS|KBD|MARK|Q|S|SAMP|SMALL|SPAN|STRONG|SUB|SUP|U|VAR)$/ig.test(node.nodeName) ); }; /** * remove node from 'start' node to 'end-1' node inside parent * if 'end' node is null, remove all child nodes after 'start' node. * @param {Node} parent - parent node * @param {Node} start - start node to remove * @param {Node} end - end node to remove * @ignore */ var removeChildFromStartToEndNode = function removeChildFromStartToEndNode(parent, start, end) { var child = start; if (!child || parent !== child.parentNode) { return; } while (child !== end) { var next = child.nextSibling; parent.removeChild(child); child = next; } }; /** * remove nodes along the direction from the node to reach targetParent node * @param {Node} targetParent - stop removing when reach target parent node * @param {Node} node - start node * @param {boolean} isForward - direction * @ignore */ var removeNodesByDirection = function removeNodesByDirection(targetParent, node, isForward) { var parent = node; while (parent !== targetParent) { var nextParent = parent.parentNode; var _parent = parent, nextSibling = _parent.nextSibling, previousSibling = _parent.previousSibling; if (!isForward && nextSibling) { removeChildFromStartToEndNode(nextParent, nextSibling, null); } else if (isForward && previousSibling) { removeChildFromStartToEndNode(nextParent, nextParent.childNodes[0], parent); } parent = nextParent; } }; var getLeafNode = function getLeafNode(node) { var result = node; while (result.childNodes && result.childNodes.length) { var _result = result, nextLeaf = _result.firstChild; // When inline tag have empty text node with other childnodes, ignore empty text node. if (isTextNode(nextLeaf) && !getTextLength(nextLeaf)) { result = nextLeaf.nextSibling || nextLeaf; } else { result = nextLeaf; } } return result; }; /** * check if a coordinates is inside a task box * @param {object} style - computed style of task box * @param {number} offsetX - event x offset * @param {number} offsetY - event y offset * @returns {boolean} * @ignore */ var isInsideTaskBox = function isInsideTaskBox(style, offsetX, offsetY) { var rect = { left: parseInt(style.left, 10), top: parseInt(style.top, 10), width: parseInt(style.width, 10), height: parseInt(style.height, 10) }; return offsetX >= rect.left && offsetX <= rect.left + rect.width && offsetY >= rect.top && offsetY <= rect.top + rect.height; }; /** * Check whether node is OL or UL * @param {node} node - node * @returns {boolean} - whether node is OL or UL * @ignore */ var isListNode = function isListNode(node) { if (!node) { return false; } return node.nodeName === 'UL' || node.nodeName === 'OL'; }; /** * Check whether node is first list item * @param {node} node - node * @returns {boolean} whether node is first list item * @ignore */ var isFirstListItem = function isFirstListItem(node) { var nodeName = node.nodeName, parentNode = node.parentNode; return nodeName === 'LI' && node === parentNode.firstChild; }; /** * Check whether node is first level list item * @param {node} node - node * @returns {boolean} whether node is first level list item * @ignore */ var isFirstLevelListItem = function isFirstLevelListItem(node) { var nodeName = node.nodeName, listNode = node.parentNode; var listParentNode = listNode.parentNode; return nodeName === 'LI' && !isListNode(listParentNode); }; /** * Merge node to target node and detach node * @param {node} node - node * @param {node} targetNode - target node * @ignore */ var mergeNode = function mergeNode(node, targetNode) { if (node.hasChildNodes()) { _tuiCodeSnippet2.default.forEachArray(node.childNodes, function () { targetNode.appendChild(node.firstChild); }); targetNode.normalize(); } if (node.parentNode) { node.parentNode.removeChild(node); } }; /** * Create hr that is not contenteditable * @returns {node} hr is wraped div * @ignore */ var createHorizontalRule = function createHorizontalRule() { var div = document.createElement('div'); var hr = document.createElement('hr'); div.setAttribute('contenteditable', false); hr.setAttribute('contenteditable', false); div.appendChild(hr); return div; }; /** * Create Empty Line * @returns {node} <div><br></div> * @private */ var createEmptyLine = function createEmptyLine() { var div = document.createElement('div'); div.appendChild(document.createElement('br')); return div; }; /** * Find same tagName child node and change wrapping order. * For example, if below node need to optimize 'B' tag. * <i><s><b>test</b></s></i> * should be changed tag's order. * <b><i><s>test</s></i></b> * @param {node} node * @param {string} tagName * @returns {node} * @private */ var changeTagOrder = function changeTagOrder(node, tagName) { if (node.nodeName !== 'SPAN') { var parentNode = node.parentNode; var tempNode = node; while (tempNode.childNodes && tempNode.childNodes.length === 1 && !isTextNode(tempNode.firstChild)) { tempNode = tempNode.firstChild; if (tempNode.nodeName === 'SPAN') { break; } if (tempNode.nodeName === tagName) { var wrapper = document.createElement(tagName); mergeNode(tempNode, tempNode.parentNode); parentNode.replaceChild(wrapper, node); wrapper.appendChild(node); return wrapper; } } } return node; }; /** * Find same tagName nodes and merge from startNode to endNode. * @param {node} startNode * @param {node} endNode * @param {string} tagName * @returns {node} * @private */ var mergeSameNodes = function mergeSameNodes(startNode, endNode, tagName) { var startBlockNode = changeTagOrder(startNode, tagName); if (startBlockNode.nodeName === tagName) { var endBlockNode = changeTagOrder(endNode, tagName); var mergeTargetNode = startBlockNode; var nextNode = startBlockNode.nextSibling; while (nextNode) { var tempNext = nextNode.nextSibling; nextNode = changeTagOrder(nextNode, tagName); if (nextNode.nodeName === tagName) { // eslint-disable-next-line max-depth if (mergeTargetNode) { mergeNode(nextNode, mergeTargetNode); } else { mergeTargetNode = nextNode; } } else { mergeTargetNode = null; } if (nextNode === endBlockNode) { break; } nextNode = tempNext; } } }; /** * Find same tagName nodes in range and merge nodes. * For example range is like this * <s><b>AAA</b></s><b>BBB</b> * nodes is changed below * <b><s>AAA</s>BBB</b> * @param {range} range * @param {string} tagName * @private */ var optimizeRange = function optimizeRange(range, tagName) { var collapsed = range.collapsed, commonAncestorContainer = range.commonAncestorContainer, startContainer = range.startContainer, endContainer = range.endContainer; if (!collapsed) { var optimizedNode = null; if (startContainer !== endContainer) { mergeSameNodes(getParentUntil(startContainer, commonAncestorContainer), getParentUntil(endContainer, commonAncestorContainer), tagName); optimizedNode = commonAncestorContainer; } else if (isTextNode(startContainer)) { optimizedNode = startContainer.parentNode; } if (optimizedNode && optimizedNode.nodeName === tagName) { var _optimizedNode = optimizedNode, previousSibling = _optimizedNode.previousSibling; var tempNode = void 0; if (previousSibling) { tempNode = changeTagOrder(previousSibling); if (tempNode.nodeName === tagName) { mergeNode(optimizedNode, tempNode); } } var _optimizedNode2 = optimizedNode, nextSibling = _optimizedNode2.nextSibling; if (nextSibling) { tempNode = changeTagOrder(nextSibling); if (tempNode.nodeName === tagName) { mergeNode(tempNode, optimizedNode); } } } } }; /** * Gets all text node from root element. * @param {HTMLElement} root Root element * @returns {Array} list of text nodes */ var getAllTextNode = function getAllTextNode(root) { var walker = document.createTreeWalker(root, 4, null, false); var result = []; while (walker.nextNode()) { var node = walker.currentNode; if (isTextNode(node)) { result.push(node); } } return result; }; exports.default = { getNodeName: getNodeName, isTextNode: isTextNode, isElemNode: isElemNode, isBlockNode: isBlockNode, getTextLength: getTextLength, getOffsetLength: getOffsetLength, getPrevOffsetNodeUntil: getPrevOffsetNodeUntil, getNodeOffsetOfParent: getNodeOffsetOfParent, getChildNodeByOffset: getChildNodeByOffset, getNodeWithDirectionUntil: getNodeWithDirectionUntil, containsNode: containsNode, getTopPrevNodeUnder: getTopPrevNodeUnder, getTopNextNodeUnder: getTopNextNodeUnder, getParentUntilBy: getParentUntilBy, getParentUntil: getParentUntil, getTopBlockNode: getTopBlockNode, getPrevTextNode: getPrevTextNode, findOffsetNode: findOffsetNode, getPath: getPath, getNodeInfo: getNodeInfo, getTableCellByDirection: getTableCellByDirection, getSiblingRowCellByDirection: getSiblingRowCellByDirection, isMDSupportInlineNode: isMDSupportInlineNode, isStyledNode: isStyledNode, removeChildFromStartToEndNode: removeChildFromStartToEndNode, removeNodesByDirection: removeNodesByDirection, getLeafNode: getLeafNode, isInsideTaskBox: isInsideTaskBox, isListNode: isListNode, isFirstListItem: isFirstListItem, isFirstLevelListItem: isFirstLevelListItem, mergeNode: mergeNode, createHorizontalRule: createHorizontalRule, createEmptyLine: createEmptyLine, changeTagOrder: changeTagOrder, mergeSameNodes: mergeSameNodes, optimizeRange: optimizeRange, getAllTextNode: getAllTextNode }; /***/ }), /* 5 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _jquery = __webpack_require__(0); var _jquery2 = _interopRequireDefault(_jquery); var _tuiCodeSnippet = __webpack_require__(1); var _tuiCodeSnippet2 = _interopRequireDefault(_tuiCodeSnippet); var _uicontroller = __webpack_require__(8); var _uicontroller2 = _interopRequireDefault(_uicontroller); 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; } /** * @fileoverview Implements LayerPopup * @author NHN FE Development Lab <dl_javascript@nhn.com> */ var CLASS_PREFIX = 'tui-popup-'; var CLASS_FIT_WINDOW = 'fit-window'; var LAYOUT_TEMPLATE_MODELESS = '<div class="' + CLASS_PREFIX + 'header">\n <span class="' + CLASS_PREFIX + 'title"></span>\n <div class="' + CLASS_PREFIX + 'header-buttons">\n <button type="button" class="' + CLASS_PREFIX + 'close-button"></button>\n </div>\n </div>\n <div class="' + CLASS_PREFIX + 'body"></div>'; var LAYOUT_TEMPLATE_MODAL = '<div class="' + CLASS_PREFIX + 'wrapper">\n <div class="' + CLASS_PREFIX + 'header">\n <span class="' + CLASS_PREFIX + 'title"></span>\n <div class="' + CLASS_PREFIX + 'header-buttons">\n <button type="button" class="' + CLASS_PREFIX + 'close-button"></button>\n </div>\n </div>\n <div class="' + CLASS_PREFIX + 'body"></div>\n </div>'; /** * A number, or a string containing a number. * @typedef {Object} LayerPopupOption * @property {string[]} [openerCssQuery] - Css Query list to bind clickevent that open popup * @property {string[]} [closerCssQuery] - Css Query list to bind clickevent that close popup * @property {jQuery} $el - popup root element * @property {jQuery|string} [content] - popup content that html string or jQuery element * @property {string} [textContent] - popup text content * @property {string} title - popup title * @property {boolean} [header] - whether to draw header * @property {jQuery} [$target] - element to append popup * @property {boolean} modal - true: modal, false: modeless * @property {string} [headerButtons] - replace header(close) button */ /** * Class LayerPopup * @extends {UIController} */ var LayerPopup = function (_UIController) { _inherits(LayerPopup, _UIController); /** * Creates an instance of LayerPopup. * @param {LayerPopupOption} options - popup option * @memberof LayerPopup */ function LayerPopup(options) { _classCallCheck(this, LayerPopup); options = _tuiCodeSnippet2.default.extend({ header: true, $target: (0, _jquery2.default)('body'), textContent: '' }, options); var _this = _possibleConstructorReturn(this, (LayerPopup.__proto__ || Object.getPrototypeOf(LayerPopup)).call(this, { tagName: 'div', className: options.modal ? CLASS_PREFIX + 'modal-background' : CLASS_PREFIX + 'wrapper', rootElement: options.$el })); _this._initInstance(options); _this._initDOM(options); _this._initDOMEvent(options); _this._initEditorEvent(options); return _this; } /** * init instance. * store properties & prepare before initialize DOM * @param {LayerPopupOption} options - layer popup options * @memberof LayerPopup * @protected */ _createClass(LayerPopup, [{ key: '_initInstance', value: function _initInstance(options) { this._$target = options.$target; if (options.$el) { this.$el = options.$el; this._isExternalHtmlUse = true; } if (options.content) { this.$content = (0, _jquery2.default)(options.content); } else { this.$content = options.textContent; } this.options = options; } /** * initialize DOM, render popup * @memberof LayerPopup * @protected */ }, { key: '_initDOM', value: function _initDOM() { this._initLayout(); if (!this._isExternalHtmlUse) { if (_tuiCodeSnippet2.default.isExisty(this.options.title)) { this.setTitle(this.options.title); } this.setContent(this.$content); } var buttons = this.options.headerButtons; if (buttons) { this.$el.find('.' + CLASS_PREFIX + 'close-button').remove(); var $buttonWrapper = this.$el.find('.' + CLASS_PREFIX + 'header-buttons'); $buttonWrapper.empty(); $buttonWrapper.append((0, _jquery2.default)(buttons)); } if (this.options.css) { this.$el.css(this.options.css); } } /** * bind DOM events * @memberof LayerPopup * @protected */ }, { key: '_initDOMEvent', value: function _initDOMEvent() { var _this2 = this; var _options = this.options, openerCssQuery = _options.openerCssQuery, closerCssQuery = _options.closerCssQuery; if (openerCssQuery) { (0, _jquery2.default)(openerCssQuery).on('click.' + this._id, function () { return _this2.show(); }); } if (closerCssQuery) { (0, _jquery2.default)(closerCssQuery).on('click.' + this._id, function () { return _this2.hide(); }); } this.on('click .' + CLASS_PREFIX + 'close-button', function () { return _this2.hide(); }); } /** * bind editor events * @memberof LayerPopup * @protected * @abstract */ }, { key: '_initEditorEvent', value: function _initEditorEvent() {} }, { key: '_initLayout', value: function _initLayout() { var options = this.options; if (!this._isExternalHtmlUse) { var layout = options.modal ? LAYOUT_TEMPLATE_MODAL : LAYOUT_TEMPLATE_MODELESS; this.$el.html(layout); this.$el.addClass(options.className); this.hide(); this._$target.append(this.$el); this.$body = this.$el.find('.' + CLASS_PREFIX + 'body'); if (!options.header) { this.$el.find('.' + CLASS_PREFIX + 'header').remove(); } } else { this.hide(); this._$target.append(this.$el); } } /** * set popup content * @param {jQuery|HTMLElement|string} $content - content * @memberof LayerPopup */ }, { key: 'setContent', value: function setContent($content) { this.$body.empty(); this.$body.append($content); } /** * set title * @param {string} title - title text * @memberof LayerPopup */ }, { key: 'setTitle', value: function setTitle(title) { var $title = this.$el.find('.' + CLASS_PREFIX + 'title'); $title.empty(); $title.append(title); } /** * get title element * @memberof LayerPopup * @returns {HTMLElement} - title html element */ }, { key: 'getTitleElement', value: function getTitleElement() { return this.$el.find('.' + CLASS_PREFIX + 'title').get(0); } /** * hide popup * @memberof LayerPopup */ }, { key: 'hide', value: function hide() { this.$el.css('display', 'none'); this._isShow = false; this.trigger('hidden', this); } /** * show popup * @memberof LayerPopup */ }, { key: 'show', value: function show() { this.$el.css('display', 'block'); this._isShow = true; this.trigger('shown', t