text: "use strict";
  /**
   *
   * This plugin will change Slate's default marks model (every prop is a mark) with the Portable Text model (marks is an array of strings on prop .marks).
   */
  Object.defineProperty(exports, "__esModule", { value: true });
  exports.createWithPortableTextMarkModel = void 0;
  const slate_1 = require("slate");
  const lodash_1 = require("lodash");
  const debug_1 = require("../../utils/debug");
  const selection_1 = require("../../utils/selection");
  const debug = debug_1.debugWithName('plugin:withPortableTextMarkModel');
  function createWithPortableTextMarkModel(portableTextFeatures, keyGenerator, change$) {
      return function withPortableTextMarkModel(editor) {
          // Extend Slate's default normalization. Merge spans with same set of .marks when doing merge_node operations
          const { normalizeNode } = editor;
          editor.normalizeNode = nodeEntry => {
              normalizeNode(nodeEntry);
              if (editor.operations.some(op => op.type === 'merge_node')) {
                  mergeSpans(editor);
              }
              // Check consistency of markDefs
              if (editor.operations.some(op => ['split_node', 'remove_node', 'remove_text', 'merge_node', 'set_selection'].includes(op.type))) {
                  normalizeMarkDefs(editor);
                  ensureEmptyTextAfterEndingAnnotation(editor);
              }
              // This should not be needed? Commented out for now.
              // // Ensure that every span node has .marks
              // const [node, path] = nodeEntry
              // if (node._type === portableTextFeatures.types.span.name) {
              //   if (!node.marks) {
              //     debug('Adding .marks to span node')
              //     Transforms.setNodes(editor, {marks: []}, {at: path})
              //   }
              // }
          };
          // Override built in addMark function
          editor.addMark = (mark) => {
              if (editor.selection) {
                  if (slate_1.Range.isExpanded(editor.selection)) {
                      // Split if needed
                      slate_1.Transforms.setNodes(editor, {}, { match: slate_1.Text.isText, split: true });
                      // Use new selection
                      const splitTextNodes = [
                          ...slate_1.Editor.nodes(editor, { at: editor.selection, match: slate_1.Text.isText })
                      ];
                      const shouldRemoveMark = lodash_1.flatten(splitTextNodes.map(item => item[0]).map(node => node.marks)).includes(mark);
                      if (shouldRemoveMark) {
                          editor.removeMark(mark);
                          return;
                      }
                      splitTextNodes.forEach(([node, path]) => {
                          const marks = [
                              ...(Array.isArray(node.marks) ? node.marks : []).filter((eMark) => eMark !== mark),
                              mark
                          ];
                          slate_1.Transforms.setNodes(editor, { marks }, { at: path });
                      });
                      mergeSpans(editor);
                  }
                  else {
                      const existingMarks = Object.assign({}, (slate_1.Editor.marks(editor) || {})).marks || [];
                      const marks = Object.assign(Object.assign({}, (slate_1.Editor.marks(editor) || {})), { marks: [...existingMarks, mark] });
                      editor.marks = marks;
                  }
                  editor.onChange();
              }
          };
          // Override built in removeMark function
          editor.removeMark = (mark) => {
              if (editor.selection) {
                  if (slate_1.Range.isExpanded(editor.selection)) {
                      // Split if needed
                      slate_1.Transforms.setNodes(editor, {}, { match: slate_1.Text.isText, split: true });
                      const splitTextNodes = [
                          ...slate_1.Editor.nodes(editor, { at: editor.selection, match: slate_1.Text.isText })
                      ];
                      splitTextNodes.forEach(([node, path]) => {
                          slate_1.Transforms.setNodes(editor, {
                              marks: (Array.isArray(node.marks) ? node.marks : []).filter((eMark) => eMark !== mark)
                          }, { at: path });
                      });
                      mergeSpans(editor);
                  }
                  else {
                      const existingMarks = Object.assign({}, (slate_1.Editor.marks(editor) || {})).marks || [];
                      const marks = Object.assign(Object.assign({}, (slate_1.Editor.marks(editor) || {})), { marks: existingMarks.filter(eMark => eMark !== mark) });
                      editor.marks = marks;
                  }
                  editor.onChange();
              }
          };
          editor.pteIsMarkActive = (mark) => {
              if (!editor.selection) {
                  return false;
              }
              let existingMarks = Object.assign({}, (slate_1.Editor.marks(editor) || {})).marks || [];
              if (slate_1.Range.isExpanded(editor.selection)) {
                  Array.from(slate_1.Editor.nodes(editor, { match: slate_1.Text.isText, at: editor.selection })).forEach(n => {
                      const [node] = n;
                      existingMarks = lodash_1.uniq([...existingMarks, ...(node.marks || [])]);
                  });
              }
              return existingMarks.includes(mark);
          };
          // Custom editor function to toggle a mark
          editor.pteToggleMark = (mark) => {
              const isActive = editor.pteIsMarkActive(mark);
              if (isActive) {
                  debug(`Remove mark '${mark}'`);
                  slate_1.Editor.removeMark(editor, mark);
              }
              else {
                  debug(`Add mark '${mark}'`);
                  slate_1.Editor.addMark(editor, mark, true);
              }
              const newSelection = selection_1.toPortableTextRange(editor);
              if (newSelection !== undefined) {
                  // Emit a new selection here (though it might be the same).
                  // This is for toolbars etc that listens to selection changes to update themselves.
                  change$.next({ type: 'selection', selection: newSelection });
              }
              editor.onChange();
          };
          return editor;
      };
      /**
       * Normalize re-marked spans in selection
       *
       * @param {Editor} editor
       */
      function mergeSpans(editor) {
          const { selection } = editor;
          if (selection) {
              for (const [node, path] of Array.from(slate_1.Editor.nodes(editor, {
                  at: slate_1.Editor.range(editor, [selection.anchor.path[0]], [selection.focus.path[0]]),
                  match: slate_1.Text.isText
              })).reverse()) {
                  const [parent] = slate_1.Editor.node(editor, slate_1.Path.parent(path));
                  const nextPath = [path[0], path[1] + 1];
                  if (slate_1.Editor.isBlock(editor, parent)) {
                      const nextTextNode = parent.children[nextPath[1]];
                      if (nextTextNode && nextTextNode.text && lodash_1.isEqual(nextTextNode.marks, node.marks)) {
                          slate_1.Transforms.mergeNodes(editor, { at: nextPath, voids: true });
                      }
                  }
              }
          }
      }
      /**
       * Normalize markDefs
       *
       * @param {Editor} editor
       */
      function ensureEmptyTextAfterEndingAnnotation(editor) {
          const { selection } = editor;
          if (selection) {
              const [block] = slate_1.Editor.node(editor, selection, { depth: 1 });
              const [span] = slate_1.Editor.node(editor, selection, { depth: 2 });
              if (block &&
                  span &&
                  Array.isArray(block.children) &&
                  span === block.children[block.children.length - 1] &&
                  Array.isArray(span.marks) &&
                  span.marks.some(mark => Array.isArray(block.markDefs) && block.markDefs.map(def => def._key).includes(mark))) {
                  debug('Inserting space after annotation');
                  slate_1.Transforms.insertNodes(editor, [{ _type: 'span', text: '​ ', marks: [], _key: keyGenerator() }], {
                      at: selection.focus
                  });
              }
          }
      }
      /**
       * Normalize markDefs
       *
       * @param {Editor} editor
       */
      function normalizeMarkDefs(editor) {
          const { selection } = editor;
          if (selection) {
              const blocks = slate_1.Editor.nodes(editor, {
                  at: selection,
                  match: n => n._type === portableTextFeatures.types.block.name
              });
              for (const [block, path] of blocks) {
                  if (Array.isArray(block.markDefs) && slate_1.Element.isElement(block)) {
                      const newMarkDefs = block.markDefs.filter(def => {
                          return block.children.find(child => {
                              return Array.isArray(child.marks) && child.marks.includes(def._key);
                          });
                      });
                      const isEmptySingleChild = block.markDefs.length > 0 &&
                          block.children.length === 1 &&
                          block.children[0].text === '';
                      if (!lodash_1.isEqual(newMarkDefs, block.markDefs) || isEmptySingleChild) {
                          debug('Removing markDef not in use');
                          slate_1.Transforms.setNodes(editor, {
                              markDefs: isEmptySingleChild ? [] : newMarkDefs
                          }, { at: path });
                      }
                  }
              }
          }
      }
  }
  exports.createWithPortableTextMarkModel = createWithPortableTextMarkModel;
  //# sourceMappingURL=createWithPortableTextMarkModel.js.map