biso24-editor 1.3.0

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.
Files changed (104) hide show
  1. package/CKEditor4/.eslintrc.json +4 -0
  2. package/CKEditor4/CHANGES.md +2182 -0
  3. package/CKEditor4/LICENSE.md +1436 -0
  4. package/CKEditor4/README.md +39 -0
  5. package/CKEditor4/SECURITY.md +10 -0
  6. package/CKEditor4/adapters/jquery.js +158 -0
  7. package/CKEditor4/bender-runner.config.json +16 -0
  8. package/CKEditor4/build-config.js +100 -0
  9. package/CKEditor4/ckeditor.js +30065 -0
  10. package/CKEditor4/config.js +39 -0
  11. package/CKEditor4/contents.css +161 -0
  12. package/CKEditor4/lang/vi.js +475 -0
  13. package/CKEditor4/plugins/a11yhelp/dialogs/a11yhelp.js +143 -0
  14. package/CKEditor4/plugins/a11yhelp/dialogs/lang/vi.js +122 -0
  15. package/CKEditor4/plugins/ajax/plugin.js +241 -0
  16. package/CKEditor4/plugins/autocomplete/plugin.js +1655 -0
  17. package/CKEditor4/plugins/autocomplete/skins/default.css +38 -0
  18. package/CKEditor4/plugins/custompaste/plugin.js +57 -0
  19. package/CKEditor4/plugins/dialog/dialogDefinition.js +4 -0
  20. package/CKEditor4/plugins/dialog/styles/dialog.css +18 -0
  21. package/CKEditor4/plugins/font/lang/vi.js +14 -0
  22. package/CKEditor4/plugins/font/plugin.js +535 -0
  23. package/CKEditor4/plugins/icons.png +0 -0
  24. package/CKEditor4/plugins/icons_hidpi.png +0 -0
  25. package/CKEditor4/plugins/image2/dialogs/image2.js +558 -0
  26. package/CKEditor4/plugins/image2/icons/hidpi/image.png +0 -0
  27. package/CKEditor4/plugins/image2/icons/image.png +0 -0
  28. package/CKEditor4/plugins/image2/lang/vi.js +21 -0
  29. package/CKEditor4/plugins/image2/plugin.js +1708 -0
  30. package/CKEditor4/plugins/justify/icons/hidpi/justifyblock.png +0 -0
  31. package/CKEditor4/plugins/justify/icons/hidpi/justifycenter.png +0 -0
  32. package/CKEditor4/plugins/justify/icons/hidpi/justifyleft.png +0 -0
  33. package/CKEditor4/plugins/justify/icons/hidpi/justifyright.png +0 -0
  34. package/CKEditor4/plugins/justify/icons/justifyblock.png +0 -0
  35. package/CKEditor4/plugins/justify/icons/justifycenter.png +0 -0
  36. package/CKEditor4/plugins/justify/icons/justifyleft.png +0 -0
  37. package/CKEditor4/plugins/justify/icons/justifyright.png +0 -0
  38. package/CKEditor4/plugins/justify/plugin.js +266 -0
  39. package/CKEditor4/plugins/justify_group/icons/hidpi/justifycenter.png +0 -0
  40. package/CKEditor4/plugins/justify_group/icons/justifycenter.png +0 -0
  41. package/CKEditor4/plugins/justify_group/plugin.js +64 -0
  42. package/CKEditor4/plugins/lineheight/LICENSE +22 -0
  43. package/CKEditor4/plugins/lineheight/README.md +2 -0
  44. package/CKEditor4/plugins/lineheight/lang/vi.js +3 -0
  45. package/CKEditor4/plugins/lineheight/plugin.js +99 -0
  46. package/CKEditor4/plugins/lineheight/readme.txt +30 -0
  47. package/CKEditor4/plugins/link/dialogs/anchor.js +82 -0
  48. package/CKEditor4/plugins/link/dialogs/link.js +777 -0
  49. package/CKEditor4/plugins/link/images/anchor.png +0 -0
  50. package/CKEditor4/plugins/link/images/hidpi/anchor.png +0 -0
  51. package/CKEditor4/plugins/magicline/images/hidpi/icon-rtl.png +0 -0
  52. package/CKEditor4/plugins/magicline/images/hidpi/icon.png +0 -0
  53. package/CKEditor4/plugins/magicline/images/icon-rtl.png +0 -0
  54. package/CKEditor4/plugins/magicline/images/icon.png +0 -0
  55. package/CKEditor4/plugins/mentions/plugin.js +471 -0
  56. package/CKEditor4/plugins/menubutton/plugin.js +99 -0
  57. package/CKEditor4/plugins/pastefromword/filter/default.js +849 -0
  58. package/CKEditor4/plugins/pastetools/filter/common.js +445 -0
  59. package/CKEditor4/plugins/pastetools/filter/image.js +163 -0
  60. package/CKEditor4/plugins/pdffile/icons/exporttopdf.png +0 -0
  61. package/CKEditor4/plugins/pdffile/icons/hidpi/exporttopdf.png +0 -0
  62. package/CKEditor4/plugins/pdffile/plugin.js +36 -0
  63. package/CKEditor4/plugins/placeholder_button/plugin.js +31 -0
  64. package/CKEditor4/plugins/sourcedialog/dialogs/sourcedialog.js +88 -0
  65. package/CKEditor4/plugins/sourcedialog/icons/hidpi/sourcedialog-rtl.png +0 -0
  66. package/CKEditor4/plugins/sourcedialog/icons/hidpi/sourcedialog.png +0 -0
  67. package/CKEditor4/plugins/sourcedialog/icons/sourcedialog-rtl.png +0 -0
  68. package/CKEditor4/plugins/sourcedialog/icons/sourcedialog.png +0 -0
  69. package/CKEditor4/plugins/sourcedialog/lang/vi.js +9 -0
  70. package/CKEditor4/plugins/sourcedialog/plugin.js +30 -0
  71. package/CKEditor4/plugins/textmatch/plugin.js +331 -0
  72. package/CKEditor4/plugins/textwatcher/plugin.js +295 -0
  73. package/CKEditor4/plugins/widget/images/handle.png +0 -0
  74. package/CKEditor4/plugins/wordfile/icons/exporttoword.png +0 -0
  75. package/CKEditor4/plugins/wordfile/icons/hidpi/exporttoword.png +0 -0
  76. package/CKEditor4/plugins/wordfile/icons/hidpi/importfromword.png +0 -0
  77. package/CKEditor4/plugins/wordfile/icons/importfromword.png +0 -0
  78. package/CKEditor4/plugins/wordfile/plugin.js +161 -0
  79. package/CKEditor4/skins/moono-lisa/dialog.css +685 -0
  80. package/CKEditor4/skins/moono-lisa/dialog_ie.css +721 -0
  81. package/CKEditor4/skins/moono-lisa/dialog_ie8.css +746 -0
  82. package/CKEditor4/skins/moono-lisa/dialog_iequirks.css +724 -0
  83. package/CKEditor4/skins/moono-lisa/editor.css +1551 -0
  84. package/CKEditor4/skins/moono-lisa/editor_gecko.css +1556 -0
  85. package/CKEditor4/skins/moono-lisa/editor_ie.css +1588 -0
  86. package/CKEditor4/skins/moono-lisa/editor_ie8.css +1639 -0
  87. package/CKEditor4/skins/moono-lisa/editor_iequirks.css +1632 -0
  88. package/CKEditor4/skins/moono-lisa/icons.png +0 -0
  89. package/CKEditor4/skins/moono-lisa/icons_hidpi.png +0 -0
  90. package/CKEditor4/skins/moono-lisa/images/arrow.png +0 -0
  91. package/CKEditor4/skins/moono-lisa/images/close.png +0 -0
  92. package/CKEditor4/skins/moono-lisa/images/hidpi/close.png +0 -0
  93. package/CKEditor4/skins/moono-lisa/images/hidpi/lock-open.png +0 -0
  94. package/CKEditor4/skins/moono-lisa/images/hidpi/lock.png +0 -0
  95. package/CKEditor4/skins/moono-lisa/images/hidpi/refresh.png +0 -0
  96. package/CKEditor4/skins/moono-lisa/images/lock-open.png +0 -0
  97. package/CKEditor4/skins/moono-lisa/images/lock.png +0 -0
  98. package/CKEditor4/skins/moono-lisa/images/refresh.png +0 -0
  99. package/CKEditor4/skins/moono-lisa/images/spinner.gif +0 -0
  100. package/CKEditor4/skins/moono-lisa/readme.md +46 -0
  101. package/CKEditor4/styles.js +136 -0
  102. package/CKEditor4/vendor/promise.js +385 -0
  103. package/README.md +3 -0
  104. package/package.json +9 -0
@@ -0,0 +1,88 @@
1
+
2
+ /**
3
+ * @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
4
+ * CKEditor 4 LTS ("Long Term Support") is available under the terms of the Extended Support Model.
5
+ */
6
+
7
+ CKEDITOR.dialog.add('sourcedialog', function (editor) {
8
+ var size = CKEDITOR.document.getWindow().getViewPaneSize();
9
+
10
+ // Make it maximum 800px wide, but still fully visible in the viewport.
11
+ var width = Math.min(size.width - 70, 800);
12
+
13
+ // Make it use 2/3 of the viewport height.
14
+ var height = size.height / 1.5;
15
+
16
+ // Store old editor data to avoid unnecessary setData.
17
+ var oldData;
18
+
19
+ return {
20
+ title: editor.lang.sourcedialog.title,
21
+ minWidth: 100,
22
+ minHeight: 100,
23
+
24
+ onShow: function () {
25
+ this.setValueOf('main', 'data', (oldData = editor.getData()));
26
+ },
27
+
28
+ onOk: (function () {
29
+ function setData(dialog, newData) {
30
+ // [IE8] Focus editor before setting selection to avoid setting data on
31
+ // locked selection, because in case of inline editor, it won't be
32
+ // unlocked before editable's HTML is altered. (https://dev.ckeditor.com/ticket/11585)
33
+ editor.focus();
34
+ editor.setData(newData, function () {
35
+ dialog.hide();
36
+
37
+ // Ensure correct selection.
38
+ var range = editor.createRange();
39
+ range.moveToElementEditStart(editor.editable());
40
+ range.select();
41
+ });
42
+ }
43
+
44
+ return function () {
45
+ // Remove CR from input data for reliable comparison with editor data.
46
+ var newData = this.getValueOf('main', 'data').replace(/\r/g, ''),
47
+ that = this;
48
+
49
+ // Avoid unnecessary setData. Also preserve selection
50
+ // when user changed his mind and goes back to wysiwyg editing.
51
+ if (newData === oldData) return true;
52
+
53
+ setTimeout(function () {
54
+ setData(that, newData);
55
+ });
56
+
57
+ // Don't let the dialog close before setData is over, to hide
58
+ // from user blinking caused by selection restoring and setting new data.
59
+ return false;
60
+ };
61
+ })(),
62
+
63
+ contents: [
64
+ {
65
+ id: 'main',
66
+ label: editor.lang.sourcedialog.title,
67
+ elements: [
68
+ {
69
+ type: 'textarea',
70
+ id: 'data',
71
+ dir: 'ltr',
72
+ inputStyle:
73
+ 'cursor:auto;' +
74
+ 'width:' +
75
+ width +
76
+ 'px;' +
77
+ 'height:' +
78
+ height +
79
+ 'px;' +
80
+ 'tab-size:4;' +
81
+ 'text-align:left;',
82
+ class: 'cke_source',
83
+ },
84
+ ],
85
+ },
86
+ ],
87
+ };
88
+ });
@@ -0,0 +1,9 @@
1
+ /*
2
+ Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
3
+ CKEditor 4 LTS ("Long Term Support") is available under the terms of the Extended Support Model.
4
+ */
5
+
6
+ CKEDITOR.plugins.setLang('sourcedialog', 'vi', {
7
+ toolbar: 'Nguồn',
8
+ title: 'Nguồn',
9
+ });
@@ -0,0 +1,30 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
3
+ * CKEditor 4 LTS ("Long Term Support") is available under the terms of the Extended Support Model.
4
+ */
5
+
6
+ CKEDITOR.plugins.add('sourcedialog', {
7
+ // jscs:disable maximumLineLength
8
+ lang: 'af,ar,az,bg,bn,bs,ca,cs,cy,da,de,de-ch,el,en,en-au,en-ca,en-gb,eo,es,es-mx,et,eu,fa,fi,fo,fr,fr-ca,gl,gu,he,hi,hr,hu,id,is,it,ja,ka,km,ko,ku,lt,lv,mn,ms,nb,nl,no,oc,pl,pt,pt-br,ro,ru,si,sk,sl,sq,sr,sr-latn,sv,th,tr,tt,ug,uk,vi,zh,zh-cn', // %REMOVE_LINE_CORE%
9
+ // jscs:enable maximumLineLength
10
+ requires: 'dialog',
11
+ icons: 'sourcedialog,sourcedialog-rtl', // %REMOVE_LINE_CORE%
12
+ hidpi: true, // %REMOVE_LINE_CORE%
13
+
14
+ init: function (editor) {
15
+ // Register the "source" command, which simply opens the "source" dialog.
16
+ editor.addCommand('sourcedialog', new CKEDITOR.dialogCommand('sourcedialog'));
17
+
18
+ // Register the "source" dialog.
19
+ CKEDITOR.dialog.add('sourcedialog', this.path + 'dialogs/sourcedialog.js');
20
+
21
+ // If the toolbar is available, create the "Source" button.
22
+ if (editor.ui.addButton) {
23
+ editor.ui.addButton('Sourcedialog', {
24
+ label: editor.lang.sourcedialog.toolbar,
25
+ command: 'sourcedialog',
26
+ toolbar: 'mode,10',
27
+ });
28
+ }
29
+ },
30
+ });
@@ -0,0 +1,331 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ ( function() {
9
+
10
+ CKEDITOR.plugins.add( 'textmatch', {} );
11
+
12
+ /**
13
+ * A global namespace for methods exposed by the [Text Match](https://ckeditor.com/cke4/addon/textmatch) plugin.
14
+ *
15
+ * The most important function is {@link #match} which performs a text
16
+ * search in the DOM.
17
+ *
18
+ * @singleton
19
+ * @class
20
+ * @since 4.10.0
21
+ */
22
+ CKEDITOR.plugins.textMatch = {};
23
+
24
+ /**
25
+ * Allows to search in the DOM for matching text using a callback which operates on strings instead of text nodes.
26
+ * Returns {@link CKEDITOR.dom.range} and the matching text.
27
+ *
28
+ * ```javascript
29
+ * var range = editor.getSelection().getRanges()[ 0 ];
30
+ *
31
+ * CKEDITOR.plugins.textMatch.match( range, function( text, offset ) {
32
+ * // Let's assume that text is 'Special thanks to #jo.' and offset is 21.
33
+ * // The offset "21" means that the caret is between '#jo' and '.'.
34
+ *
35
+ * // Get the text before the caret.
36
+ * var left = text.slice( 0, offset ),
37
+ * // Will look for a literal '#' character and at least two word characters.
38
+ * match = left.match( /#\w{2,}$/ );
39
+ *
40
+ * if ( !match ) {
41
+ * return null;
42
+ * }
43
+ *
44
+ * // The matching fragment is the '#jo', which can
45
+ * // be identified by the following offsets: { start: 18, end: 21 }.
46
+ * return { start: match.index, end: offset };
47
+ * } );
48
+ * ```
49
+ *
50
+ * @member CKEDITOR.plugins.textMatch
51
+ * @param {CKEDITOR.dom.range} range A collapsed range — the position from which the scanning starts.
52
+ * Usually the caret position.
53
+ * @param {Function} testCallback A callback executed to check if the text matches.
54
+ * @param {String} testCallback.text The full text to check.
55
+ * @param {Number} testCallback.rangeOffset An offset of the `range` in the `text` to be checked.
56
+ * @param {Object} [testCallback.return] The position of the matching fragment (`null` if nothing matches).
57
+ * @param {Number} testCallback.return.start The offset of the start of the matching fragment.
58
+ * @param {Number} testCallback.return.end The offset of the end of the matching fragment.
59
+ *
60
+ * @returns {Object/null} An object with information about the matching text or `null`.
61
+ * @returns {String} return.text The matching text.
62
+ * The text does not reflect the range offsets. The range could contain additional,
63
+ * browser-related characters like {@link CKEDITOR.dom.selection#FILLING_CHAR_SEQUENCE}.
64
+ * @returns {CKEDITOR.dom.range} return.range A range in the DOM for the text that matches.
65
+ */
66
+ CKEDITOR.plugins.textMatch.match = function( range, callback ) {
67
+ var textAndOffset = CKEDITOR.plugins.textMatch.getTextAndOffset( range ),
68
+ fillingCharSequence = CKEDITOR.dom.selection.FILLING_CHAR_SEQUENCE,
69
+ fillingSequenceOffset = 0;
70
+
71
+ if ( !textAndOffset ) {
72
+ return;
73
+ }
74
+
75
+ // Remove filling char sequence for clean query (#2038).
76
+ if ( textAndOffset.text.indexOf( fillingCharSequence ) == 0 ) {
77
+ fillingSequenceOffset = fillingCharSequence.length;
78
+
79
+ textAndOffset.text = textAndOffset.text.replace( fillingCharSequence, '' );
80
+ textAndOffset.offset -= fillingSequenceOffset;
81
+ }
82
+
83
+ var result = callback( textAndOffset.text, textAndOffset.offset );
84
+
85
+ if ( !result ) {
86
+ return null;
87
+ }
88
+
89
+ return {
90
+ range: CKEDITOR.plugins.textMatch.getRangeInText( range, result.start, result.end + fillingSequenceOffset ),
91
+ text: textAndOffset.text.slice( result.start, result.end )
92
+ };
93
+ };
94
+
95
+ /**
96
+ * Returns a text (as a string) in which the DOM range is located (the function scans for adjacent text nodes)
97
+ * and the offset of the caret in that text.
98
+ *
99
+ * ## Examples
100
+ *
101
+ * * `{}` is the range position in the text node (it means that the text node is **not** split at that position).
102
+ * * `[]` is the range position in the element (it means that the text node is split at that position).
103
+ * * `.` is a separator for text nodes (it means that the text node is split at that position).
104
+ *
105
+ * Examples:
106
+ *
107
+ * ```
108
+ * Input: <p>he[]llo</p>
109
+ * Result: { text: 'hello', offset: 2 }
110
+ *
111
+ * Input: <p>he.llo{}</p>
112
+ * Result: { text: 'hello', offset: 5 }
113
+ *
114
+ * Input: <p>{}he.ll<i>o</i></p>
115
+ * Result: { text: 'hell', offset: 0 }
116
+ *
117
+ * Input: <p>he{}<i>ll</i>o</p>
118
+ * Result: { text: 'he', offset: 2 }
119
+ *
120
+ * Input: <p>he<i>ll</i>o.m{}y.friend</p>
121
+ * Result: { text: 'omyfriend', offset: 2 }
122
+ * ```
123
+ *
124
+ * @member CKEDITOR.plugins.textMatch
125
+ * @param {CKEDITOR.dom.range} range
126
+ * @returns {Object/null}
127
+ * @returns {String} return.text The text in which the DOM range is located.
128
+ * @returns {Number} return.offset An offset of the caret.
129
+ */
130
+ CKEDITOR.plugins.textMatch.getTextAndOffset = function( range ) {
131
+ if ( !range.collapsed ) {
132
+ return null;
133
+ }
134
+
135
+ var text = '', offset = 0,
136
+ textNodes = CKEDITOR.plugins.textMatch.getAdjacentTextNodes( range ),
137
+ nodeReached = false,
138
+ elementIndex,
139
+ startContainerIsText = ( range.startContainer.type != CKEDITOR.NODE_ELEMENT );
140
+
141
+ if ( startContainerIsText ) {
142
+ // Determining element index in textNodes array.
143
+ elementIndex = indexOf( textNodes, function( current ) {
144
+ return range.startContainer.equals( current );
145
+ } );
146
+ } else {
147
+ // Based on range startOffset decreased by first text node index.
148
+ elementIndex = range.startOffset - ( textNodes[ 0 ] ? textNodes[ 0 ].getIndex() : 0 );
149
+ }
150
+
151
+ var max = textNodes.length;
152
+ for ( var i = 0; i < max; i += 1 ) {
153
+ var currentNode = textNodes[ i ];
154
+ text += currentNode.getText();
155
+
156
+ // We want to increase text offset only when startContainer is not reached.
157
+ if ( !nodeReached ) {
158
+ if ( startContainerIsText ) {
159
+ if ( i == elementIndex ) {
160
+ nodeReached = true;
161
+ offset += range.startOffset;
162
+ } else {
163
+ offset += currentNode.getText().length;
164
+ }
165
+ } else {
166
+ if ( i == elementIndex ) {
167
+ nodeReached = true;
168
+ }
169
+
170
+ // In below example there are three text nodes in p element and four possible offsets ( 0, 1, 2, 3 )
171
+ // We are going to increase offset while iteration:
172
+ // index 0 ==> 0
173
+ // index 1 ==> 3
174
+ // index 2 ==> 3 + 3
175
+ // index 3 ==> 3 + 3 + 2
176
+
177
+ // <p> foo bar ba </p>
178
+ // 0^^^1^^^2^^3
179
+ if ( i > 0 ) {
180
+ offset += textNodes[ i - 1 ].getText().length;
181
+ }
182
+
183
+ // If element index at last element we also want to increase offset.
184
+ if ( max == elementIndex && i + 1 == max ) {
185
+ offset += currentNode.getText().length;
186
+ }
187
+ }
188
+ }
189
+ }
190
+
191
+ return {
192
+ text: text,
193
+ offset: offset
194
+ };
195
+ };
196
+
197
+ /**
198
+ * Transforms the `start` and `end` offsets in the text generated by the {@link #getTextAndOffset}
199
+ * method into a DOM range.
200
+ *
201
+ * ## Examples
202
+ *
203
+ * * `{}` is the range position in the text node (it means that the text node is **not** split at that position).
204
+ * * `.` is a separator for text nodes (it means that the text node is split at that position).
205
+ *
206
+ * Examples:
207
+ *
208
+ * ```
209
+ * Input: <p>f{}oo.bar</p>, 0, 3
210
+ * Result: <p>{foo}.bar</p>
211
+ *
212
+ * Input: <p>f{}oo.bar</p>, 1, 5
213
+ * Result: <p>f{oo.ba}r</p>
214
+ * ```
215
+ *
216
+ * @member CKEDITOR.plugins.textMatch
217
+ * @param {CKEDITOR.dom.range} range
218
+ * @param {Number} start A start offset.
219
+ * @param {Number} end An end offset.
220
+ * @returns {CKEDITOR.dom.range} Transformed range.
221
+ */
222
+ CKEDITOR.plugins.textMatch.getRangeInText = function( range, start, end ) {
223
+ var resultRange = new CKEDITOR.dom.range( range.root ),
224
+ elements = CKEDITOR.plugins.textMatch.getAdjacentTextNodes( range ),
225
+ startData = findElementAtOffset( elements, start ),
226
+ endData = findElementAtOffset( elements, end );
227
+
228
+ resultRange.setStart( startData.element, startData.offset );
229
+ resultRange.setEnd( endData.element, endData.offset );
230
+
231
+ return resultRange;
232
+ };
233
+
234
+ /**
235
+ * Creates a collection of adjacent text nodes which are between DOM elements, starting from the given range.
236
+ * This function works only for collapsed ranges.
237
+ *
238
+ * ## Examples
239
+ *
240
+ * * `{}` is the range position in the text node (it means that the text node is **not** split at that position).
241
+ * * `.` is a separator for text nodes (it means that the text node is split at that position).
242
+ *
243
+ * Examples:
244
+ *
245
+ * ```
246
+ * Input: <p>he.llo{}</p>
247
+ * Result: [ 'he', 'llo' ]
248
+ *
249
+ * Input: <p>{}he.ll<i>o</i></p>
250
+ * Result: [ 'he', 'll' ]
251
+ *
252
+ * Input: <p>he{}<i>ll</i>o.</p>
253
+ * Result: [ 'he' ]
254
+ *
255
+ * Input: <p>he<i>ll</i>{}o.my.friend</p>
256
+ * Result: [ 'o', 'my', 'friend' ]
257
+ * ```
258
+ *
259
+ * @member CKEDITOR.plugins.textMatch
260
+ * @param {CKEDITOR.dom.range} range
261
+ * @return {CKEDITOR.dom.text[]} An array of text nodes.
262
+ */
263
+ CKEDITOR.plugins.textMatch.getAdjacentTextNodes = function( range ) {
264
+ if ( !range.collapsed ) {
265
+ throw new Error( 'Range must be collapsed.' ); // %REMOVE_LINE%
266
+ // Reachable in prod mode.
267
+ return []; // jshint ignore:line
268
+ }
269
+
270
+ var collection = [],
271
+ siblings,
272
+ elementIndex,
273
+ node, i;
274
+
275
+ if ( range.startContainer.type != CKEDITOR.NODE_ELEMENT ) {
276
+ siblings = range.startContainer.getParent().getChildren();
277
+ elementIndex = range.startContainer.getIndex();
278
+ } else {
279
+ siblings = range.startContainer.getChildren();
280
+ elementIndex = range.startOffset;
281
+ }
282
+
283
+ i = elementIndex;
284
+ while ( node = siblings.getItem( --i ) ) {
285
+ if ( node.type == CKEDITOR.NODE_TEXT ) {
286
+ collection.unshift( node );
287
+ } else {
288
+ break;
289
+ }
290
+ }
291
+
292
+ i = elementIndex;
293
+ while ( node = siblings.getItem( i++ ) ) {
294
+ if ( node.type == CKEDITOR.NODE_TEXT ) {
295
+ collection.push( node );
296
+ } else {
297
+ break;
298
+ }
299
+ }
300
+
301
+ return collection;
302
+ };
303
+
304
+ function findElementAtOffset( elements, offset ) {
305
+ var max = elements.length,
306
+ currentOffset = 0;
307
+ for ( var i = 0; i < max; i += 1 ) {
308
+ var current = elements[ i ];
309
+ if ( offset >= currentOffset && currentOffset + current.getText().length >= offset ) {
310
+ return {
311
+ element: current,
312
+ offset: offset - currentOffset
313
+ };
314
+ }
315
+
316
+ currentOffset += current.getText().length;
317
+ }
318
+
319
+ return null;
320
+ }
321
+
322
+ function indexOf( arr, checker ) {
323
+ for ( var i = 0; i < arr.length; i++ ) {
324
+ if ( checker( arr[ i ] ) ) {
325
+ return i;
326
+ }
327
+ }
328
+
329
+ return -1;
330
+ }
331
+ } )();