UNPKG

@atlaskit/editor-common

Version:

A package that contains common classes and components for editor and renderer

213 lines (207 loc) • 8.79 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.wholeSelectionHasMarks = exports.isMarkExcluded = exports.isMarkAllowedInRange = exports.anyMarkActive = void 0; var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); var _model = require("@atlaskit/editor-prosemirror/model"); var _cellSelection = require("@atlaskit/editor-tables/cell-selection"); function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } /** * Determine if a mark of a specific type exists anywhere in the selection. */ var anyMarkActive = exports.anyMarkActive = function anyMarkActive(state, markType) { var _state$selection = state.selection, $from = _state$selection.$from, from = _state$selection.from, to = _state$selection.to, empty = _state$selection.empty; if (empty) { return !!markType.isInSet(state.storedMarks || $from.marks()); } var rangeHasMark = false; if (state.selection instanceof _cellSelection.CellSelection) { state.selection.forEachCell(function (cell, cellPos) { var from = cellPos; var to = cellPos + cell.nodeSize; if (!rangeHasMark) { rangeHasMark = state.doc.rangeHasMark(from, to, markType); } }); } else { rangeHasMark = state.doc.rangeHasMark(from, to, markType); } return rangeHasMark; }; var allMarksAreRuledOut = function allMarksAreRuledOut(marks) { return Array.from(marks.values()).every(function (mark) { return !mark; }); }; /** * Check if a selection contains any of the specifiged marks. The whole selection * must have the mark for it to be considered active. * @param state The editor state * @param markTypes The marks to check for * @returns A map indicating which marks are present in the selection * @example * const markTypes = editorState.schema.marks; * const activeMarks = wholeSelectionHasMarks(editorState, [markTypes.strong, markTypes.em, markTypes.etc]); */ var wholeSelectionHasMarks = exports.wholeSelectionHasMarks = function wholeSelectionHasMarks(state, markTypes) { var _state$selection2 = state.selection, $from = _state$selection2.$from, from = _state$selection2.from, to = _state$selection2.to, empty = _state$selection2.empty; if (empty) { return new Map(markTypes.map(function (markType) { return [markType, !!markType.isInSet(state.storedMarks || $from.marks())]; })); } if (state.selection instanceof _cellSelection.CellSelection) { return cellSelectionHasMarks(state.doc, state.selection, markTypes); } return wholeRangeHasMarks(from, to, state.doc, markTypes); }; var cellSelectionHasMarks = function cellSelectionHasMarks(doc, selection, markTypes) { // Warning: This is micro-optimized and is a total pain to actually read. // Will attempt to explain in comments. // Start with map of booleans for each mark var cellsHaveMarks = new Map(markTypes.map(function (markType) { return [markType, true]; })); selection.forEachCell(function (cell, cellPos) { // Early exit if all marks are already ruled out if (allMarksAreRuledOut(cellsHaveMarks)) { return; } var from = cellPos; var to = cellPos + cell.nodeSize; // On first cell just do a regular check if (!cellsHaveMarks) { cellsHaveMarks = wholeRangeHasMarks(from, to, doc, markTypes); } else { // Find the marks that are still true ie the ones that haven't been ruled out in // previous cells. The idea here is to whittle down the list of marks so that we check // less and less as we go, giving `wholeRangeHasMarks` more opportunities to exit early var marksToCheck = []; var _iterator = _createForOfIteratorHelper(cellsHaveMarks), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var _step$value = (0, _slicedToArray2.default)(_step.value, 2), markType = _step$value[0], hasMark = _step$value[1]; if (hasMark) { marksToCheck.push(markType); } } // Look specifically for the marks that are not yet ruled out. } catch (err) { _iterator.e(err); } finally { _iterator.f(); } var cellHasMarks = wholeRangeHasMarks(from, to, doc, marksToCheck); // Map these results back into the original array of results and repeat! var _iterator2 = _createForOfIteratorHelper(cellHasMarks), _step2; try { for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { var _step2$value = (0, _slicedToArray2.default)(_step2.value, 2), _markType = _step2$value[0], _hasMark = _step2$value[1]; cellsHaveMarks.set(_markType, _hasMark); } } catch (err) { _iterator2.e(err); } finally { _iterator2.f(); } } }); return cellsHaveMarks; }; var wholeRangeHasMarks = function wholeRangeHasMarks(from, to, doc, markTypes) { var hasMarks = new Map(markTypes.map(function (markType) { return [markType, true]; })); var hasNoMarks = new Map(markTypes.map(function (markType) { return [markType, false]; })); var isTextContent = false; doc.nodesBetween(from, to, function (node) { if (allMarksAreRuledOut(hasMarks)) { // This won't be a true early exit, but will prevent diving into nodes and // any checks further down the function. return false; } if (!node.type.isText) { return true; // continue traversing } isTextContent = true; var _iterator3 = _createForOfIteratorHelper(hasMarks), _step3; try { var _loop = function _loop() { var _step3$value = (0, _slicedToArray2.default)(_step3.value, 2), markType = _step3$value[0], hasMark = _step3$value[1]; if (!hasMark) { return 1; // continue // already ruled out the mark, skip further checks } var value = markType instanceof _model.Mark ? markType.isInSet(node.marks) : node.marks.some(function (mark) { return mark.type === markType; }); hasMarks.set(markType, value); }; for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) { if (_loop()) continue; } } catch (err) { _iterator3.e(err); } finally { _iterator3.f(); } }); return isTextContent ? hasMarks : hasNoMarks; }; var isMarkAllowedInRange = exports.isMarkAllowedInRange = function isMarkAllowedInRange(doc, ranges, type) { var _loop2 = function _loop2() { var _ranges$i = ranges[i], $from = _ranges$i.$from, $to = _ranges$i.$to; var can = $from.depth === 0 ? doc.type.allowsMarkType(type) : false; doc.nodesBetween($from.pos, $to.pos, function (node) { if (can) { return false; } can = node.inlineContent && node.type.allowsMarkType(type); return; }); if (can) { return { v: can }; } }, _ret; for (var i = 0; i < ranges.length; i++) { _ret = _loop2(); if (_ret) return _ret.v; } return false; }; var isMarkExcluded = exports.isMarkExcluded = function isMarkExcluded(type, marks) { if (marks) { return marks.some(function (mark) { return mark.type !== type && mark.type.excludes(type); }); } return false; };