# all files / src/js/base/editing/ Typing.js

82.35% Statements 42/51
78.57% Branches 22/28
80% Functions 4/5
82.35% Lines 42/51

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    1181×
    1×
    1×
    1×

    1×
    1×

    102×
    102×

    1×
    1×
    1×
    1×

    1×
    1×

    1×
    8×

    8×

    8×

    8×

    8×

    8×

    8×

    8×
    8×
    2×
    6×
    5×

    8×

    5×

    5×
    1×

    5×
    5×
    4×

    1×

    3×

    3×
    3×

    3×

    3×

    8×

    1×
     import $ from 'jquery';
    import dom from '../core/dom';
    import range from '../core/range';
    import Bullet from '../editing/Bullet';

    /**
     * @class editing.Typing
     *
     * Typing
     *
     */
    export default class Typing {
      constructor(context) {
        // a Bullet instance to toggle lists off
        this.bullet = new Bullet();
        this.options = context.options;
      }

      /**
       * insert tab
       *
       * @param {WrappedRange} rng
       * @param {Number} tabsize
       */
      insertTab(rng, tabsize) {
        const tab = dom.createText(new Array(tabsize + 1).join(dom.NBSP_CHAR));
        rng = rng.deleteContents();
        rng.insertNode(tab, true);

        rng = range.create(tab, tabsize);
        rng.select();
      }

      /**
       * insert paragraph
       *
       * @param {jQuery} $editable
       * @param {WrappedRange} rng Can be used in unit tests to "mock" the range
       *
       * blockquoteBreakingLevel
       *   0 - No break, the new paragraph remains inside the quote
       *   1 - Break the first blockquote in the ancestors list
       *   2 - Break all blockquotes, so that the new paragraph is not quoted (this is the default)
       */
      insertParagraph(editable, rng) {
        rng = rng || range.create(editable);

        // deleteContents on range.
        rng = rng.deleteContents();

        // Wrap range if it needs to be wrapped by paragraph
        rng = rng.wrapBodyInlineWithPara();

        // finding paragraph
        const splitRoot = dom.ancestor(rng.sc, dom.isPara);

        let nextPara;
        // on paragraph: split paragraph
        Eif (splitRoot) {
          // if it is an empty line with li
          Iif (dom.isEmpty(splitRoot) && dom.isLi(splitRoot)) {
            // toogle UL/OL and escape
            this.bullet.toggleList(splitRoot.parentNode.nodeName);
            return;
          } else {
            let blockquote = null;
            if (this.options.blockquoteBreakingLevel === 1) {
              blockquote = dom.ancestor(splitRoot, dom.isBlockquote);
            } else if (this.options.blockquoteBreakingLevel === 2) {
              blockquote = dom.lastAncestor(splitRoot, dom.isBlockquote);
            }

            if (blockquote) {
              // We're inside a blockquote and options ask us to break it
              nextPara = $(dom.emptyPara)[0];
              // If the split is right before a <br>, remove it so that there's no "empty line"
              // after the split in the new blockquote created
              if (dom.isRightEdgePoint(rng.getStartPoint()) && dom.isBR(rng.sc.nextSibling)) {
                $(rng.sc.nextSibling).remove();
              }
              const split = dom.splitTree(blockquote, rng.getStartPoint(), { isDiscardEmptySplits: true });
              if (split) {
                split.parentNode.insertBefore(nextPara, split);
              } else {
                dom.insertAfter(nextPara, blockquote); // There's no split if we were at the end of the blockquote
              }
            } else {
              nextPara = dom.splitTree(splitRoot, rng.getStartPoint());

              // not a blockquote, just insert the paragraph
              let emptyAnchors = dom.listDescendant(splitRoot, dom.isEmptyAnchor);
              emptyAnchors = emptyAnchors.concat(dom.listDescendant(nextPara, dom.isEmptyAnchor));

              $.each(emptyAnchors, (idx, anchor) => {
                dom.remove(anchor);
              });

              // replace empty heading, pre or custom-made styleTag with P tag
              Iif ((dom.isHeading(nextPara) || dom.isPre(nextPara) || dom.isCustomStyleTag(nextPara)) && dom.isEmpty(nextPara)) {
                nextPara = dom.replace(nextPara, 'p');
              }
            }
          }
        // no paragraph: insert empty paragraph
        } else {
          const next = rng.sc.childNodes[rng.so];
          nextPara = $(dom.emptyPara)[0];
          if (next) {
            rng.sc.insertBefore(nextPara, next);
          } else {
            rng.sc.appendChild(nextPara);
          }
        }

        range.create(nextPara, 0).normalize().select().scrollIntoView(editable);
      }
    }