UNPKG

suneditor

Version:

Vanilla JavaScript based WYSIWYG web editor

332 lines (292 loc) 10.5 kB
import { onlyZeroWidthRegExp } from '../unicode'; const _RE_EXCLUDE_FORMAT = /(\s|^)(katex|MathJax|se-exclude-format)(\s|$)/; /** * @description A method that checks If the text is blank or to see if it contains `ZERO WIDTH SPACE` or empty (`unicode.zeroWidthSpace`) * @param {string|Node} text String value or Node * @returns {boolean} */ export function isZeroWidth(text) { if (text === null || text === undefined) return false; if (typeof text !== 'string') { if (isElement(text)) { const children = text.children; for (let i = 0, len = children.length; i < len; i++) { const child = children[i]; if (!isContentLess(child)) return false; } } text = text.textContent; } return text === '' || onlyZeroWidthRegExp.test(text); } /** * @description Determine if this offset is the edge offset of container * @param {Node} container The node of the selection object. (range.startContainer..) * @param {number} offset The offset of the selection object. (core.getRange().startOffset...) * @param {?("front"|"end")} [dir] Select check point. * - `"front"`: Front edge, `"end"`: End edge, `undefined`: Both edge. * @returns {boolean} */ export function isEdgePoint(container, offset, dir) { return (dir !== 'end' && offset === 0) || ((!dir || dir !== 'front') && !container.nodeValue && offset <= 1) || ((!dir || dir === 'end') && container.nodeValue && offset >= container.nodeValue.length); } /** * @description Check the node is a text node. * @param {?*} node The node to check * @returns {node is Text} */ export function isText(node) { return node?.nodeType === 3; } /** * @description Check the node is an HTMLElement node. * @param {?*} node The node to check * @returns {node is HTMLElement} */ export function isElement(node) { return node?.nodeType === 1; } /** * @description It is judged whether it is the input element (INPUT, TEXTAREA) * @param {?*} node The node to check * @returns {node is HTMLInputElement} */ export function isInputElement(node) { return isElement(node) && /^(INPUT|TEXTAREA|SELECT|OPTION)$/i.test(node.nodeName); } /** * @description It is judged whether it is the button element * @param {?*} node The node to check * @returns {node is HTMLButtonElement} */ export function isButtonElement(node) { return isElement(node) && /^(BUTTON)$/i.test(node.nodeName); } /** * @description Check the node is a list (ol, ul) * @param {?Node|string} node The element or element name to check * @returns {node is HTMLOListElement|HTMLUListElement} */ export function isList(node) { return /^(OL|UL)$/i.test(typeof node === 'string' ? node : node?.nodeName); } /** * @description Check the node is a list cell (li) * @param {?Node|string} node The element or element name to check * @returns {node is HTMLLIElement} */ export function isListCell(node) { return /^LI$/i.test(typeof node === 'string' ? node : node?.nodeName); } /** * @description Check the node is a table * @param {?Node|string} node The element or element name to check * @returns {node is HTMLTableElement} */ export function isTable(node) { return /^TABLE$/i.test(typeof node === 'string' ? node : node?.nodeName); } /** * @description Check the node is a table elements. (table, thead, tbody, tr, th, td) * @param {?Node|string} node The element or element name to check * @returns {node is HTMLTableElement|HTMLTableSectionElement|HTMLTableRowElement|HTMLTableCellElement|HTMLTableColElement|HTMLTableColElement} */ export function isTableElements(node) { return /^(TABLE|THEAD|TBODY|TR|TH|TD|COL)$/i.test(typeof node === 'string' ? node : node?.nodeName); } /** * @description Check the node is a table cell (td, th) * @param {?Node|string} node The element or element name to check * @returns {node is HTMLTableCellElement|HTMLTableColElement} */ export function isTableCell(node) { return /^(TD|TH)$/i.test(typeof node === 'string' ? node : node?.nodeName); } /** * @description Check the node is a table row (tr) * @param {?Node|string} node The element or element name to check * @returns {node is HTMLTableRowElement} */ export function isTableRow(node) { return /^TR$/i.test(typeof node === 'string' ? node : node?.nodeName); } /** * @description Check the node is a break node (BR) * @param {?Node|string} node The element or element name to check * @returns {node is HTMLBRElement} */ export function isBreak(node) { return /^BR$/i.test(typeof node === 'string' ? node : node?.nodeName); } /** * @description Check the node is a anchor node (A) * @param {?Node|string} node The element or element name to check * @returns {node is HTMLAnchorElement} */ export function isAnchor(node) { return /^A$/i.test(typeof node === 'string' ? node : node?.nodeName); } /** * @description Check the node is a media node (img, iframe, audio, video, canvas) * @param {?Node|string} node The element or element name to check * @returns {node is HTMLImageElement|HTMLIFrameElement|HTMLAudioElement|HTMLVideoElement|HTMLCanvasElement} */ export function isMedia(node) { return /^(IMG|IFRAME|AUDIO|VIDEO|CANVAS)$/i.test(typeof node === 'string' ? node : node?.nodeName); } /** * @description Check the node is a iframe tag * @param {?Node|string} node The element or element name to check * @returns {node is HTMLIFrameElement} */ export function isIFrame(node) { return /^IFRAME$/i.test(typeof node === 'string' ? node : node?.nodeName); } /** * @description Check the node is a figure tag * @param {?Node|string} node The element or element name to check * @returns {boolean} */ export function isFigure(node) { return /^FIGURE$/i.test(typeof node === 'string' ? node : node?.nodeName); } /** * @description Checks whether the given node is a content-less (void) HTML tag * @param {?Node|string} node The element or element name to check * @returns {boolean} */ export function isContentLess(node) { return /^(BR|COLGROUP|COL|THEAD|TBODY|TFOOT|TR|AREA|BASE|EMBED|HR|IMG|INPUT|KEYGEN|LINK|META|PARAM|SOURCE|TRACK|WBR)$/i.test(typeof node === 'string' ? node : node?.nodeName); } /** * @description Check the `line` element is empty. * @param {Node} node `line` element node * @returns {boolean} */ export function isEmptyLine(node) { if (!node?.parentNode) return true; const el = /** @type {HTMLElement} */ (node); return !el.querySelector('IMG, IFRAME, AUDIO, VIDEO, CANVAS, TABLE') && (el.children.length <= 1 || isBreak(el.firstElementChild)) && isZeroWidth(el.textContent); } /** * @description Checks if the given node is a container component (class `se-component-container`). * @param {Node} element * @returns {boolean} `true` if the node is a container component, otherwise `false`. */ export function isComponentContainer(element) { if (element?.nodeType !== 1) return false; const el = /** @type {Element} */ (element); const cls = el.classList; if (!cls) return false; return cls.contains('se-component') || cls.contains('se-flex-component'); } /** * @description It is judged whether it is the edit region top `div` element or `iframe`'s `body` tag. * @param {?Node} node The node to check * @returns {node is HTMLElement} */ export function isWysiwygFrame(node) { if (node?.nodeType !== 1) return false; const el = /** @type {Element} */ (node); const cls = el.classList; if (!cls) return false; return node.nodeName.toUpperCase() === 'BODY' || cls.contains('se-wrapper-wysiwyg') || cls.contains('sun-editor-carrier-wrapper') || cls.contains('se-wrapper'); } /** * @description It is judged whether it is the `contenteditable` property is `false`. * @param {?Node} node The node to check * @returns {node is HTMLElement} */ export function isNonEditable(node) { return node?.nodeType === 1 && /** @type {HTMLElement} */ (node).getAttribute('contenteditable') === 'false'; } /** * @description Check the `span`'s attributes are empty. * @param {?Node} node Element node * @returns {boolean} */ export function isSpanWithoutAttr(node) { if (node?.nodeType !== 1) return false; const el = /** @type {HTMLElement} */ (node); return /^SPAN$/i.test(el.nodeName) && !el.className && el.style.length === 0; } /** * @description Compares the style and class for equal values. * @param {Node} a Node to compare * @param {Node} b Node to compare * @returns {boolean} Returns `true` if both are text nodes. */ export function isSameAttributes(a, b) { if (a.nodeType === 3 && b.nodeType === 3) return true; if (a.nodeType === 3 || b.nodeType === 3) return false; const aEl = /** @type {HTMLElement} */ (a); const bEl = /** @type {HTMLElement} */ (b); const style_a = aEl.style; const style_b = bEl.style; let compStyle = 0; for (let i = 0, len = style_a.length; i < len; i++) { if (style_a[style_a[i]] === style_b[style_a[i]]) compStyle++; } const class_a = aEl.classList; const class_b = bEl.classList; let compClass = 0; for (let i = 0, len = class_a.length; i < len; i++) { if (class_b.contains(class_a[i])) compClass++; } return compStyle === style_b.length && compStyle === style_a.length && compClass === class_b.length && compClass === class_a.length; } /** * @description It is judged whether it is the not checking node. (class=`katex`, `MathJax`, `se-exclude-format`) * @param {Node} node The node to check * @returns {node is HTMLElement} */ export function isExcludeFormat(node) { return _RE_EXCLUDE_FORMAT.test(/** @type {HTMLElement} */ (node)?.className); } /** * @description Checks for `__se__uneditable` in the class list. * - Components with class `__se__uneditable` cannot be modified. * @param {Node} node The element to check * @returns {boolean} */ export function isUneditable(node) { return /** @type {HTMLElement} */ (node)?.classList.contains('__se__uneditable'); } /** * @description Checks if element can't be easily enabled * @param {Node} node Element to check for * @returns {boolean} */ export function isImportantDisabled(node) { return /** @type {HTMLElement} */ (node)?.hasAttribute('data-important-disabled'); } const check = { isZeroWidth, isEdgePoint, isText, isElement, isInputElement, isButtonElement, isList, isListCell, isTable, isTableElements, isTableCell, isTableRow, isBreak, isAnchor, isMedia, isIFrame, isFigure, isContentLess, isEmptyLine, isComponentContainer, isWysiwygFrame, isNonEditable, isSpanWithoutAttr, isSameAttributes, isExcludeFormat, isUneditable, isImportantDisabled, }; export default check;