@fiduswriter/editor 0.1.41 → 0.1.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"jump_hidden_nodes.d.ts","sourceRoot":"","sources":["../../src/state_plugins/jump_hidden_nodes.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,MAAM,EAA2B,MAAM,mBAAmB,CAAA;AAmBlE,eAAO,MAAM,qBAAqB,GAAI,UAAU;IAAC,MAAM,EAAE,OAAO,CAAA;CAAC,gBA2E3D,CAAA"}
1
+ {"version":3,"file":"jump_hidden_nodes.d.ts","sourceRoot":"","sources":["../../src/state_plugins/jump_hidden_nodes.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,MAAM,EAA2B,MAAM,mBAAmB,CAAA;AAoBlE,eAAO,MAAM,qBAAqB,GAAI,UAAU;IAAC,MAAM,EAAE,OAAO,CAAA;CAAC,gBA6I3D,CAAA"}
@@ -15,6 +15,61 @@ const posHidden = ($pos) => {
15
15
  const key = new PluginKey("jump-hidden-nodes");
16
16
  export const jumpHiddenNodesPlugin = (_options) => new Plugin({
17
17
  key,
18
+ props: {
19
+ handleKeyDown: (view, event) => {
20
+ if (event.key !== "ArrowLeft") {
21
+ return false;
22
+ }
23
+ const { selection, doc } = view.state;
24
+ if (!selection.empty) {
25
+ return false;
26
+ }
27
+ const $pos = selection.$from;
28
+ if ($pos.depth < 2) {
29
+ return false;
30
+ }
31
+ // Intercept ArrowLeft when at the beginning of a textblock
32
+ // OR inside an empty textblock whose preceding doc-level
33
+ // sibling is hidden. This handles the case where clicking
34
+ // an empty body paragraph and pressing LEFT should land in
35
+ // the abstract (skipping hidden parts like keywords).
36
+ const atStart = $pos.parentOffset === 0;
37
+ const isEmptyTextblock = $pos.parent.type.isTextblock &&
38
+ $pos.parent.content.size === 0;
39
+ if (!atStart && !isEmptyTextblock) {
40
+ return false;
41
+ }
42
+ // Find previous sibling of the parent doc-level node
43
+ const { index } = doc.content.findIndex($pos.pos);
44
+ if (index < 1) {
45
+ return false;
46
+ }
47
+ const prevSibling = doc.child(index - 1);
48
+ if (!prevSibling.attrs.hidden) {
49
+ return false;
50
+ }
51
+ // Walk backward to the previous non-hidden position
52
+ const dir = -1;
53
+ let newPos = selection.from, hidden = true, validTextSelection = false, validGapCursor = false;
54
+ let new$pos = selection.$from;
55
+ while (hidden ||
56
+ (!validGapCursor && !validTextSelection)) {
57
+ newPos += dir;
58
+ if (newPos === 0) {
59
+ return false;
60
+ }
61
+ new$pos = doc.resolve(newPos);
62
+ validTextSelection = new$pos.parent.inlineContent;
63
+ validGapCursor = GapCursor.valid(new$pos);
64
+ hidden = posHidden(new$pos);
65
+ }
66
+ const trSelection = validTextSelection
67
+ ? new TextSelection(new$pos)
68
+ : new GapCursor(new$pos);
69
+ view.dispatch(view.state.tr.setSelection(trSelection));
70
+ return true;
71
+ }
72
+ },
18
73
  appendTransaction: (trs, oldState, state) => {
19
74
  if (!state.selection.empty) {
20
75
  // Only applies to collapsed selection
@@ -43,33 +98,36 @@ export const jumpHiddenNodesPlugin = (_options) => new Plugin({
43
98
  }
44
99
  // Handle boundary crossing: cursor is at the start of a visible
45
100
  // doc-level part whose preceding sibling is hidden. Use
46
- // childBefore to check whether the previous doc child is hidden.
101
+ // findIndex to locate the previous doc child and check if
102
+ // it is hidden.
47
103
  if (selectionSet &&
48
104
  !posHidden(state.selection.$from) &&
49
105
  state.selection.$from.depth >= 2) {
50
106
  const $pos = state.selection.$from;
51
- const prevChild = state.doc.childBefore($pos.pos);
52
- if (prevChild &&
53
- prevChild.node &&
54
- prevChild.node.attrs.hidden) {
55
- const dir = -1;
56
- let newPos = state.selection.from, hidden = true, validTextSelection = false, validGapCursor = false;
57
- let new$pos = state.selection.$from;
58
- while (hidden ||
59
- (!validGapCursor && !validTextSelection)) {
60
- newPos += dir;
61
- if (newPos === 0) {
62
- return;
107
+ const { index } = state.doc.content.findIndex($pos.pos);
108
+ if (index >= 1) {
109
+ const prevSibling = state.doc.child(index - 1);
110
+ if (prevSibling.attrs.hidden) {
111
+ const dir = -1;
112
+ let newPos = state.selection.from, hidden = true, validTextSelection = false, validGapCursor = false;
113
+ let new$pos = state.selection.$from;
114
+ while (hidden ||
115
+ (!validGapCursor && !validTextSelection)) {
116
+ newPos += dir;
117
+ if (newPos === 0) {
118
+ return;
119
+ }
120
+ new$pos = state.doc.resolve(newPos);
121
+ validTextSelection =
122
+ new$pos.parent.inlineContent;
123
+ validGapCursor = GapCursor.valid(new$pos);
124
+ hidden = posHidden(new$pos);
63
125
  }
64
- new$pos = state.doc.resolve(newPos);
65
- validTextSelection = new$pos.parent.inlineContent;
66
- validGapCursor = GapCursor.valid(new$pos);
67
- hidden = posHidden(new$pos);
126
+ const selection = validTextSelection
127
+ ? new TextSelection(new$pos)
128
+ : new GapCursor(new$pos);
129
+ return state.tr.setSelection(selection);
68
130
  }
69
- const selection = validTextSelection
70
- ? new TextSelection(new$pos)
71
- : new GapCursor(new$pos);
72
- return state.tr.setSelection(selection);
73
131
  }
74
132
  }
75
133
  return undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"jump_hidden_nodes.js","sourceRoot":"","sources":["../../src/state_plugins/jump_hidden_nodes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"jump_hidden_nodes.js","sourceRoot":"","sources":["../../src/state_plugins/jump_hidden_nodes.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { Plugin } from "prosemirror-state";
2
2
  import { DecorationSet } from "prosemirror-view";
3
- export declare const placeholdersPlugin: (_options: {
3
+ export declare const placeholdersPlugin: (options: {
4
4
  editor: unknown;
5
- }) => Plugin<DecorationSet | undefined>;
5
+ }) => Plugin<false | DecorationSet>;
6
6
  //# sourceMappingURL=placeholders.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"placeholders.d.ts","sourceRoot":"","sources":["../../src/state_plugins/placeholders.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,MAAM,EAAY,MAAM,mBAAmB,CAAA;AACnD,OAAO,EAAa,aAAa,EAAC,MAAM,kBAAkB,CAAA;AAK1D,eAAO,MAAM,kBAAkB,GAAI,UAAU;IAAC,MAAM,EAAE,OAAO,CAAA;CAAC,sCAuE7D,CAAA"}
1
+ {"version":3,"file":"placeholders.d.ts","sourceRoot":"","sources":["../../src/state_plugins/placeholders.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,MAAM,EAAY,MAAM,mBAAmB,CAAA;AACnD,OAAO,EAAa,aAAa,EAAC,MAAM,kBAAkB,CAAA;AAK1D,eAAO,MAAM,kBAAkB,GAAI,SAAS;IAAC,MAAM,EAAE,OAAO,CAAA;CAAC,kCAqH5D,CAAA"}
@@ -1,17 +1,17 @@
1
1
  import { Plugin, PluginKey } from "prosemirror-state";
2
2
  import { Decoration, DecorationSet } from "prosemirror-view";
3
3
  const key = new PluginKey("placeholders");
4
- export const placeholdersPlugin = (_options) => {
4
+ export const placeholdersPlugin = (options) => {
5
5
  function calculatePlaceHolderDecorations(state) {
6
6
  const anchor = state.selection.$anchor;
7
7
  const head = state.selection.$head;
8
8
  if (!anchor || !head) {
9
- return;
9
+ return false;
10
10
  }
11
11
  const anchorPart = anchor.node(1);
12
12
  const headPart = head.node(1);
13
13
  if (!anchorPart || !headPart) {
14
- return;
14
+ return false;
15
15
  }
16
16
  const currentPart = anchorPart === headPart ? anchorPart : false;
17
17
  const decorations = [];
@@ -22,20 +22,53 @@ export const placeholdersPlugin = (_options) => {
22
22
  state.schema.nodes["tags_part"],
23
23
  state.schema.nodes["contributors_part"]
24
24
  ].includes(partElement.type) &&
25
- partElement !== currentPart) {
25
+ options.editor.docInfo.access_rights === "write") {
26
+ // We don't need to render placeholders for these kinds
27
+ // of nodes in write mode as their nodeviews will take
28
+ // care of that.
26
29
  return;
27
30
  }
28
- const placeholder = document.createElement("span");
29
- placeholder.classList.add("placeholder");
30
- placeholder.setAttribute("data-placeholder", "true");
31
- const widget = Decoration.widget(offset + 1, () => placeholder, {
32
- side: -1,
33
- placeholder: true
31
+ const text = partElement.type === state.schema.nodes["title"]
32
+ ? `${gettext("Title")}...`
33
+ : `${partElement.attrs.title}...`;
34
+ const placeHolder = document.createElement("span");
35
+ placeHolder.classList.add("placeholder");
36
+ placeHolder.setAttribute("data-placeholder", text);
37
+ if (currentPart === partElement) {
38
+ placeHolder.classList.add("selected");
39
+ }
40
+ let position = 1 + offset;
41
+ // position of decorator: 2 to get inside (doc (1))
42
+ if (!partElement.isTextblock) {
43
+ // In block nodes that are not text blocks (body + abstract)
44
+ // place inside the first child node (a paragraph).
45
+ position += 1;
46
+ }
47
+ decorations.push(Decoration.widget(position, placeHolder, {
48
+ side: 1
49
+ }));
50
+ }
51
+ else if (["richtext_part", "table_part"].includes(partElement.type.name)) {
52
+ partElement.descendants((node, pos) => {
53
+ if (["figure", "table"].includes(node.type.name) &&
54
+ !node.attrs.caption) {
55
+ return false;
56
+ }
57
+ if (["figure_caption", "table_caption"].includes(node.type.name) &&
58
+ node.childCount === 0 &&
59
+ state.selection.$anchor.parent !== node) {
60
+ decorations.push(Decoration.node(1 + offset + pos, 1 + offset + pos + node.nodeSize, {
61
+ class: "empty",
62
+ "data-placeholder": `${gettext("Caption")}...`
63
+ }));
64
+ }
65
+ return false;
34
66
  });
35
- decorations.push(widget);
36
67
  }
37
68
  });
38
- return DecorationSet.create(state.doc, decorations);
69
+ return decorations.length
70
+ ? DecorationSet.create(state.doc, decorations)
71
+ : false;
39
72
  }
40
73
  return new Plugin({
41
74
  key,
@@ -43,18 +76,17 @@ export const placeholdersPlugin = (_options) => {
43
76
  init(_config, state) {
44
77
  return calculatePlaceHolderDecorations(state);
45
78
  },
46
- apply(_tr, value, oldState, newState) {
47
- if (_tr.docChanged ||
48
- oldState.selection.$anchor !== newState.selection.$anchor ||
49
- oldState.selection.$head !== newState.selection.$head) {
50
- return calculatePlaceHolderDecorations(newState);
79
+ apply(tr, _prev, oldState, state) {
80
+ if (tr.docChanged || tr.selectionSet) {
81
+ return calculatePlaceHolderDecorations(state);
51
82
  }
52
- return value;
83
+ return key.getState(oldState);
53
84
  }
54
85
  },
55
86
  props: {
56
87
  decorations(state) {
57
- return key.getState(state);
88
+ const decorationSet = key.getState(state);
89
+ return decorationSet || null;
58
90
  }
59
91
  }
60
92
  });
@@ -1 +1 @@
1
- {"version":3,"file":"placeholders.js","sourceRoot":"","sources":["../../src/state_plugins/placeholders.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"placeholders.js","sourceRoot":"","sources":["../../src/state_plugins/placeholders.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fiduswriter/editor",
3
- "version": "0.1.41",
3
+ "version": "0.1.43",
4
4
  "description": "Fidus Writer browser editor",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,6 +1,7 @@
1
1
  import {GapCursor} from "prosemirror-gapcursor"
2
2
  import {Plugin, PluginKey, TextSelection} from "prosemirror-state"
3
3
  import type {ResolvedPos} from "prosemirror-model"
4
+ import type {EditorView} from "prosemirror-view"
4
5
 
5
6
  const posHidden = ($pos: ResolvedPos) => {
6
7
  let hidden = false
@@ -21,6 +22,69 @@ const key = new PluginKey("jump-hidden-nodes")
21
22
  export const jumpHiddenNodesPlugin = (_options: {editor: unknown}) =>
22
23
  new Plugin({
23
24
  key,
25
+ props: {
26
+ handleKeyDown: (view: EditorView, event: KeyboardEvent) => {
27
+ if (event.key !== "ArrowLeft") {
28
+ return false
29
+ }
30
+ const {selection, doc} = view.state
31
+ if (!selection.empty) {
32
+ return false
33
+ }
34
+ const $pos = selection.$from
35
+ if ($pos.depth < 2) {
36
+ return false
37
+ }
38
+ // Intercept ArrowLeft when at the beginning of a textblock
39
+ // OR inside an empty textblock whose preceding doc-level
40
+ // sibling is hidden. This handles the case where clicking
41
+ // an empty body paragraph and pressing LEFT should land in
42
+ // the abstract (skipping hidden parts like keywords).
43
+ const atStart = $pos.parentOffset === 0
44
+ const isEmptyTextblock =
45
+ $pos.parent.type.isTextblock &&
46
+ $pos.parent.content.size === 0
47
+ if (!atStart && !isEmptyTextblock) {
48
+ return false
49
+ }
50
+ // Find previous sibling of the parent doc-level node
51
+ const {index} = (doc.content as any).findIndex($pos.pos)
52
+ if (index < 1) {
53
+ return false
54
+ }
55
+ const prevSibling = doc.child(index - 1)
56
+ if (!prevSibling.attrs.hidden) {
57
+ return false
58
+ }
59
+ // Walk backward to the previous non-hidden position
60
+ const dir = -1
61
+ let newPos = selection.from,
62
+ hidden = true,
63
+ validTextSelection = false,
64
+ validGapCursor = false
65
+ let new$pos: ResolvedPos = selection.$from
66
+ while (
67
+ hidden ||
68
+ (!validGapCursor && !validTextSelection)
69
+ ) {
70
+ newPos += dir
71
+ if (newPos === 0) {
72
+ return false
73
+ }
74
+ new$pos = doc.resolve(newPos)
75
+ validTextSelection = new$pos.parent.inlineContent
76
+ validGapCursor = (GapCursor as any).valid(new$pos)
77
+ hidden = posHidden(new$pos)
78
+ }
79
+ const trSelection = validTextSelection
80
+ ? new TextSelection(new$pos)
81
+ : new GapCursor(new$pos)
82
+ view.dispatch(
83
+ view.state.tr.setSelection(trSelection)
84
+ )
85
+ return true
86
+ }
87
+ },
24
88
  appendTransaction: (trs, oldState, state) => {
25
89
  if (!state.selection.empty) {
26
90
  // Only applies to collapsed selection
@@ -53,42 +117,45 @@ export const jumpHiddenNodesPlugin = (_options: {editor: unknown}) =>
53
117
  }
54
118
  // Handle boundary crossing: cursor is at the start of a visible
55
119
  // doc-level part whose preceding sibling is hidden. Use
56
- // childBefore to check whether the previous doc child is hidden.
120
+ // findIndex to locate the previous doc child and check if
121
+ // it is hidden.
57
122
  if (
58
123
  selectionSet &&
59
124
  !posHidden(state.selection.$from) &&
60
125
  state.selection.$from.depth >= 2
61
126
  ) {
62
127
  const $pos = state.selection.$from
63
- const prevChild = state.doc.childBefore($pos.pos)
64
- if (
65
- prevChild &&
66
- prevChild.node &&
67
- prevChild.node.attrs.hidden
68
- ) {
69
- const dir = -1
70
- let newPos = state.selection.from,
71
- hidden = true,
72
- validTextSelection = false,
73
- validGapCursor = false
74
- let new$pos: ResolvedPos = state.selection.$from
75
- while (
76
- hidden ||
77
- (!validGapCursor && !validTextSelection)
78
- ) {
79
- newPos += dir
80
- if (newPos === 0) {
81
- return
128
+ const {index} = (state.doc.content as any).findIndex($pos.pos)
129
+ if (index >= 1) {
130
+ const prevSibling = state.doc.child(index - 1)
131
+ if (prevSibling.attrs.hidden) {
132
+ const dir = -1
133
+ let newPos = state.selection.from,
134
+ hidden = true,
135
+ validTextSelection = false,
136
+ validGapCursor = false
137
+ let new$pos: ResolvedPos = state.selection.$from
138
+ while (
139
+ hidden ||
140
+ (!validGapCursor && !validTextSelection)
141
+ ) {
142
+ newPos += dir
143
+ if (newPos === 0) {
144
+ return
145
+ }
146
+ new$pos = state.doc.resolve(newPos)
147
+ validTextSelection =
148
+ new$pos.parent.inlineContent
149
+ validGapCursor = (GapCursor as any).valid(
150
+ new$pos
151
+ )
152
+ hidden = posHidden(new$pos)
82
153
  }
83
- new$pos = state.doc.resolve(newPos)
84
- validTextSelection = new$pos.parent.inlineContent
85
- validGapCursor = (GapCursor as any).valid(new$pos)
86
- hidden = posHidden(new$pos)
154
+ const selection = validTextSelection
155
+ ? new TextSelection(new$pos)
156
+ : new GapCursor(new$pos)
157
+ return state.tr.setSelection(selection)
87
158
  }
88
- const selection = validTextSelection
89
- ? new TextSelection(new$pos)
90
- : new GapCursor(new$pos)
91
- return state.tr.setSelection(selection)
92
159
  }
93
160
  }
94
161
  return undefined
@@ -4,21 +4,21 @@ import type {EditorState} from "prosemirror-state"
4
4
 
5
5
  const key = new PluginKey("placeholders")
6
6
 
7
- export const placeholdersPlugin = (_options: {editor: unknown}) => {
8
- function calculatePlaceHolderDecorations(state: EditorState) {
7
+ export const placeholdersPlugin = (options: {editor: unknown}) => {
8
+ function calculatePlaceHolderDecorations(state: EditorState): DecorationSet | false {
9
9
  const anchor = state.selection.$anchor
10
10
  const head = state.selection.$head
11
11
  if (!anchor || !head) {
12
- return
12
+ return false
13
13
  }
14
14
  const anchorPart = anchor.node(1)
15
15
  const headPart = head.node(1)
16
16
  if (!anchorPart || !headPart) {
17
- return
17
+ return false
18
18
  }
19
19
  const currentPart = anchorPart === headPart ? anchorPart : false
20
20
 
21
- const decorations: Array<ReturnType<typeof Decoration.widget>> = []
21
+ const decorations: (ReturnType<typeof Decoration.widget> | ReturnType<typeof Decoration.node>)[] = []
22
22
 
23
23
  state.doc.forEach((partElement, offset) => {
24
24
  if (
@@ -30,26 +30,72 @@ export const placeholdersPlugin = (_options: {editor: unknown}) => {
30
30
  state.schema.nodes["tags_part"],
31
31
  state.schema.nodes["contributors_part"]
32
32
  ].includes(partElement.type) &&
33
- partElement !== currentPart
33
+ (options.editor as any).docInfo.access_rights === "write"
34
34
  ) {
35
+ // We don't need to render placeholders for these kinds
36
+ // of nodes in write mode as their nodeviews will take
37
+ // care of that.
35
38
  return
36
39
  }
37
40
 
38
- const placeholder = document.createElement("span")
39
- placeholder.classList.add("placeholder")
40
- placeholder.setAttribute("data-placeholder", "true")
41
- const widget = Decoration.widget(
42
- offset + 1,
43
- () => placeholder,
44
- {
45
- side: -1,
46
- placeholder: true
47
- }
41
+ const text =
42
+ partElement.type === state.schema.nodes["title"]
43
+ ? `${gettext("Title")}...`
44
+ : `${partElement.attrs.title}...`
45
+ const placeHolder = document.createElement("span")
46
+ placeHolder.classList.add("placeholder")
47
+ placeHolder.setAttribute("data-placeholder", text)
48
+ if (currentPart === partElement) {
49
+ placeHolder.classList.add("selected")
50
+ }
51
+ let position = 1 + offset
52
+ // position of decorator: 2 to get inside (doc (1))
53
+ if (!partElement.isTextblock) {
54
+ // In block nodes that are not text blocks (body + abstract)
55
+ // place inside the first child node (a paragraph).
56
+ position += 1
57
+ }
58
+ decorations.push(
59
+ Decoration.widget(position, placeHolder, {
60
+ side: 1
61
+ })
48
62
  )
49
- decorations.push(widget)
63
+ } else if (
64
+ ["richtext_part", "table_part"].includes(partElement.type.name)
65
+ ) {
66
+ partElement.descendants((node: any, pos: number) => {
67
+ if (
68
+ ["figure", "table"].includes(node.type.name) &&
69
+ !node.attrs.caption
70
+ ) {
71
+ return false
72
+ }
73
+ if (
74
+ ["figure_caption", "table_caption"].includes(
75
+ node.type.name
76
+ ) &&
77
+ node.childCount === 0 &&
78
+ state.selection.$anchor.parent !== node
79
+ ) {
80
+ decorations.push(
81
+ Decoration.node(
82
+ 1 + offset + pos,
83
+ 1 + offset + pos + node.nodeSize,
84
+ {
85
+ class: "empty",
86
+ "data-placeholder": `${gettext("Caption")}...`
87
+ }
88
+ ) as any
89
+ )
90
+ }
91
+ return false
92
+ })
50
93
  }
51
94
  })
52
- return DecorationSet.create(state.doc, decorations)
95
+
96
+ return decorations.length
97
+ ? DecorationSet.create(state.doc, decorations)
98
+ : false
53
99
  }
54
100
 
55
101
  return new Plugin({
@@ -58,20 +104,20 @@ export const placeholdersPlugin = (_options: {editor: unknown}) => {
58
104
  init(_config, state) {
59
105
  return calculatePlaceHolderDecorations(state)
60
106
  },
61
- apply(_tr, value, oldState, newState) {
62
- if (
63
- _tr.docChanged ||
64
- oldState.selection.$anchor !== newState.selection.$anchor ||
65
- oldState.selection.$head !== newState.selection.$head
66
- ) {
67
- return calculatePlaceHolderDecorations(newState)
107
+ apply(tr, _prev, oldState, state) {
108
+ if (tr.docChanged || tr.selectionSet) {
109
+ return calculatePlaceHolderDecorations(state)
68
110
  }
69
- return value
111
+ return key.getState(oldState)
70
112
  }
71
113
  },
72
114
  props: {
73
115
  decorations(state) {
74
- return key.getState(state)
116
+ const decorationSet = key.getState(state) as
117
+ | DecorationSet
118
+ | false
119
+ | undefined
120
+ return decorationSet || null
75
121
  }
76
122
  }
77
123
  })