• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

worktile / slate-angular / 543eb906-9e4d-47ae-b0a3-bf4f680c78ba

pending completion
543eb906-9e4d-47ae-b0a3-bf4f680c78ba

push

circleci

Maple13
build: optimizing angular 15 upgrade

267 of 876 branches covered (30.48%)

Branch coverage included in aggregate %.

682 of 1485 relevant lines covered (45.93%)

30.73 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

32.56
/packages/src/plugins/with-angular.ts
1
import {
2
  Editor,
3
  Node,
4
  Transforms,
5
  Range,
6
  Path,
7
  Operation,
8
  PathRef
9
} from 'slate';
10
import {
11
  EDITOR_TO_ON_CHANGE,
12
  NODE_TO_KEY,
13
  isDOMText,
14
  getPlainText,
15
  Key,
16
  getSlateFragmentAttribute
17
} from '../utils';
18
import { AngularEditor } from './angular-editor';
19
import { SlateError } from '../types/error';
20
import { findCurrentLineRange } from '../utils/lines';
21

22
export const withAngular = <T extends Editor>(editor: T, clipboardFormatKey = 'x-slate-fragment') => {
1✔
23
  const e = editor as T & AngularEditor;
19✔
24
  const { apply, onChange, deleteBackward } = e;
19✔
25

26
  e.deleteBackward = unit => {
19✔
27
    if (unit !== 'line') {
×
28
      return deleteBackward(unit);
×
29
    }
30

31
    if (editor.selection && Range.isCollapsed(editor.selection)) {
×
32
      const parentBlockEntry = Editor.above(editor, {
×
33
        match: n => Editor.isBlock(editor, n),
×
34
        at: editor.selection,
35
      });
36

37
      if (parentBlockEntry) {
×
38
        const [, parentBlockPath] = parentBlockEntry;
×
39
        const parentElementRange = Editor.range(
×
40
          editor,
41
          parentBlockPath,
42
          editor.selection.anchor
43
        );
44

45
        const currentLineRange = findCurrentLineRange(e, parentElementRange);
×
46

47
        if (!Range.isCollapsed(currentLineRange)) {
×
48
          Transforms.delete(editor, { at: currentLineRange });
×
49
        }
50
      }
51
    }
52
  };
53

54
  e.apply = (op: Operation) => {
19✔
55
    const matches: [Path | PathRef, Key][] = [];
17✔
56

57
    switch (op.type) {
17✔
58
      case 'insert_text':
59
      case 'remove_text':
60
      case 'set_node': {
61
        for (const [node, path] of Editor.levels(e, { at: op.path })) {
1✔
62
          const key = AngularEditor.findKey(e, node);
3✔
63
          matches.push([path, key]);
3✔
64
        }
65

66
        break;
1✔
67
      }
68

69
      case 'insert_node':
70
      case 'remove_node':
71
      case 'merge_node':
72
      case 'split_node': {
73
        for (const [node, path] of Editor.levels(e, {
4✔
74
          at: Path.parent(op.path),
75
        })) {
76
          const key = AngularEditor.findKey(e, node);
5✔
77
          matches.push([path, key]);
5✔
78
        }
79

80
        break;
4✔
81
      }
82

83
      case 'move_node': {
84
        const commonPath = Path.common(Path.parent(op.path), Path.parent(op.newPath));
7✔
85
        for (const [node, path] of Editor.levels(e, { at: Path.parent(op.path) })) {
7✔
86
          const key = AngularEditor.findKey(e, node);
13✔
87
          matches.push([Editor.pathRef(editor, path), key]);
13✔
88
        }
89
        for (const [node, path] of Editor.levels(e, { at: Path.parent(op.newPath) })) {
7✔
90
          if(path.length > commonPath.length){
15✔
91
            const key = AngularEditor.findKey(e, node);
5✔
92
            matches.push([Editor.pathRef(editor, path), key]);
5✔
93
          }
94
        }
95
        break;
7✔
96
      }
97
    }
98

99
    apply(op);
17✔
100

101
    for (const [source, key] of matches) {
17✔
102
      const [node] = Editor.node(e, Path.isPath(source) ? source: source.current);
26✔
103
      NODE_TO_KEY.set(node, key);
26✔
104
    }
105
  };
106

107
  e.onChange = () => {
19✔
108
    const onContextChange = EDITOR_TO_ON_CHANGE.get(e);
11✔
109

110
    if (onContextChange) {
11✔
111
      onContextChange();
9✔
112
    }
113

114
    onChange();
11✔
115
  };
116

117
  e.setFragmentData = (data: Pick<DataTransfer, 'getData' | 'setData'>) => {
19✔
118
    const { selection } = e;
×
119

120
    if (!selection) {
×
121
      return;
×
122
    }
123

124
    const [start, end] = Range.edges(selection);
×
125
    const startVoid = Editor.void(e, { at: start.path });
×
126
    const endVoid = Editor.void(e, { at: end.path });
×
127

128
    if (Range.isCollapsed(selection) && !startVoid) {
×
129
      return;
×
130
    }
131

132
    // Create a fake selection so that we can add a Base64-encoded copy of the
133
    // fragment to the HTML, to decode on future pastes.
134
    const domRange = AngularEditor.toDOMRange(e, selection);
×
135
    let contents = domRange.cloneContents();
×
136
    let attach = contents.childNodes[0] as HTMLElement;
×
137

138
    // Make sure attach is non-empty, since empty nodes will not get copied.
139
    const contentsArray = Array.from(contents.children);
×
140
    contentsArray.forEach(node => {
×
141
      if (node.textContent && node.textContent.trim() !== '') {
×
142
        attach = node as HTMLElement;
×
143
      }
144
    });
145

146
    // COMPAT: If the end node is a void node, we need to move the end of the
147
    // range from the void node's spacer span, to the end of the void node's
148
    // content, since the spacer is before void's content in the DOM.
149
    if (endVoid) {
×
150
      const [voidNode] = endVoid;
×
151
      const r = domRange.cloneRange();
×
152
      const domNode = AngularEditor.toDOMNode(e, voidNode);
×
153
      r.setEndAfter(domNode);
×
154
      contents = r.cloneContents();
×
155
    }
156

157
    // COMPAT: If the start node is a void node, we need to attach the encoded
158
    // fragment to the void node's content node instead of the spacer, because
159
    // attaching it to empty `<div>/<span>` nodes will end up having it erased by
160
    // most browsers. (2018/04/27)
161
    if (startVoid) {
×
162
      attach = contents.querySelector('[data-slate-spacer]')! as HTMLElement;
×
163
    }
164

165
    // Remove any zero-width space spans from the cloned DOM so that they don't
166
    // show up elsewhere when pasted.
167
    Array.from(contents.querySelectorAll('[data-slate-zero-width]')).forEach(
×
168
      zw => {
169
        const isNewline = zw.getAttribute('data-slate-zero-width') === 'n';
×
170
        zw.textContent = isNewline ? '\n' : '';
×
171
      }
172
    );
173

174
    // Set a `data-slate-fragment` attribute on a non-empty node, so it shows up
175
    // in the HTML, and can be used for intra-Slate pasting. If it's a text
176
    // node, wrap it in a `<span>` so we have something to set an attribute on.
177
    if (isDOMText(attach)) {
×
178
      const span = attach.ownerDocument.createElement('span');
×
179
      // COMPAT: In Chrome and Safari, if we don't add the `white-space` style
180
      // then leading and trailing spaces will be ignored. (2017/09/21)
181
      span.style.whiteSpace = 'pre';
×
182
      span.appendChild(attach);
×
183
      contents.appendChild(span);
×
184
      attach = span;
×
185
    }
186

187
    const fragment = e.getFragment();
×
188
    const stringObj = JSON.stringify(fragment);
×
189
    const encoded = window.btoa(encodeURIComponent(stringObj));
×
190
    attach.setAttribute('data-slate-fragment', encoded);
×
191
    data.setData(`application/${clipboardFormatKey}`, encoded);
×
192

193
    // Add the content to a <div> so that we can get its inner HTML.
194
    const div = contents.ownerDocument.createElement('div');
×
195
    div.appendChild(contents);
×
196
    div.setAttribute('hidden', 'true');
×
197
    contents.ownerDocument.body.appendChild(div);
×
198
    data.setData('text/html', div.innerHTML);
×
199
    data.setData('text/plain', getPlainText(div));
×
200
    contents.ownerDocument.body.removeChild(div);
×
201
    return data;
×
202
  };
203

204
  e.deleteCutData = () => {
19✔
205
    const { selection } = editor;
×
206
    if (selection) {
×
207
      if (Range.isExpanded(selection)) {
×
208
        Editor.deleteFragment(editor);
×
209
      } else {
210
        const node = Node.parent(editor, selection.anchor.path);
×
211
        if (Editor.isVoid(editor, node)) {
×
212
          Transforms.delete(editor);
×
213
        }
214
      }
215
    }
216
  };
217

218
  e.insertData = (data: DataTransfer) => {
19✔
219
    if (!e.insertFragmentData(data)) {
×
220
      e.insertTextData(data);
×
221
    }
222
  };
223

224
  e.insertFragmentData = (data: DataTransfer): boolean => {
19✔
225
    /**
226
     * Checking copied fragment from application/x-slate-fragment or data-slate-fragment
227
     */
228
    const fragment =
229
      data.getData(`application/${clipboardFormatKey}`) ||
×
230
      getSlateFragmentAttribute(data);
231

232
    if (fragment) {
×
233
      const decoded = decodeURIComponent(window.atob(fragment));
×
234
      const parsed = JSON.parse(decoded) as Node[];
×
235
      e.insertFragment(parsed);
×
236
      return true;
×
237
    }
238
    return false;
×
239
  };
240

241
  e.insertTextData = (data: DataTransfer): boolean => {
19✔
242
    const text = data.getData('text/plain');
×
243

244
    if (text) {
×
245
      const lines = text.split(/\r\n|\r|\n/);
×
246
      let split = false;
×
247

248
      for (const line of lines) {
×
249
        if (split) {
×
250
          Transforms.splitNodes(e, { always: true });
×
251
        }
252

253
        e.insertText(line);
×
254
        split = true;
×
255
      }
256
      return true;
×
257
    }
258
    return false;
×
259
  };
260

261
  e.onKeydown = () => { };
19✔
262

263
  e.onClick = () => { };
19✔
264

265
  e.isBlockCard = (element) => false;
260✔
266

267
  e.onError = (errorData: SlateError) => {
19✔
268
    if (errorData.nativeError) {
×
269
      console.error(errorData.nativeError);
×
270
    } else {
271
      console.error(errorData);
×
272
    }
273
  };
274

275
  return e;
19✔
276
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc