]> git.sur5r.net Git - bacula/bacula/blob - gui/baculum/debian/missing-sources/framework/Web/Javascripts/source/tinymce-405/plugins/spellchecker/classes/DomTextMatcher.js
baculum: Add missing-sources directory in debian metadata structure
[bacula/bacula] / gui / baculum / debian / missing-sources / framework / Web / Javascripts / source / tinymce-405 / plugins / spellchecker / classes / DomTextMatcher.js
1 /**
2  * DomTextMatcher.js
3  *
4  * Copyright, Moxiecode Systems AB
5  * Released under LGPL License.
6  *
7  * License: http://www.tinymce.com/license
8  * Contributing: http://www.tinymce.com/contributing
9  */
10
11 /**
12  * This class logic for filtering text and matching words.
13  *
14  * @class tinymce.spellcheckerplugin.TextFilter
15  * @private
16  */
17 define("tinymce/spellcheckerplugin/DomTextMatcher", [], function() {
18         // Based on work developed by: James Padolsey http://james.padolsey.com
19         // released under UNLICENSE that is compatible with LGPL
20         // TODO: Handle contentEditable edgecase:
21         // <p>text<span contentEditable="false">text<span contentEditable="true">text</span>text</span>text</p>
22         return function(regex, node, schema) {
23                 var m, matches = [], text, count = 0, doc;
24                 var blockElementsMap, hiddenTextElementsMap, shortEndedElementsMap;
25
26                 doc = node.ownerDocument;
27                 blockElementsMap = schema.getBlockElements(); // H1-H6, P, TD etc
28                 hiddenTextElementsMap = schema.getWhiteSpaceElements(); // TEXTAREA, PRE, STYLE, SCRIPT
29                 shortEndedElementsMap = schema.getShortEndedElements(); // BR, IMG, INPUT
30
31                 function getMatchIndexes(m) {
32                         if (!m[0]) {
33                                 throw 'findAndReplaceDOMText cannot handle zero-length matches';
34                         }
35
36                         var index = m.index;
37
38                         return [index, index + m[0].length, [m[0]]];
39                 }
40
41                 function getText(node) {
42                         var txt;
43
44                         if (node.nodeType === 3) {
45                                 return node.data;
46                         }
47
48                         if (hiddenTextElementsMap[node.nodeName]) {
49                                 return '';
50                         }
51
52                         txt = '';
53
54                         if (blockElementsMap[node.nodeName] || shortEndedElementsMap[node.nodeName]) {
55                                 txt += '\n';
56                         }
57
58                         if ((node = node.firstChild)) {
59                                 do {
60                                         txt += getText(node);
61                                 } while ((node = node.nextSibling));
62                         }
63
64                         return txt;
65                 }
66
67                 function stepThroughMatches(node, matches, replaceFn) {
68                         var startNode, endNode, startNodeIndex,
69                                 endNodeIndex, innerNodes = [], atIndex = 0, curNode = node,
70                                 matchLocation = matches.shift(), matchIndex = 0;
71
72                         out: while (true) {
73                                 if (blockElementsMap[curNode.nodeName] || shortEndedElementsMap[curNode.nodeName]) {
74                                         atIndex++;
75                                 }
76
77                                 if (curNode.nodeType === 3) {
78                                         if (!endNode && curNode.length + atIndex >= matchLocation[1]) {
79                                                 // We've found the ending
80                                                 endNode = curNode;
81                                                 endNodeIndex = matchLocation[1] - atIndex;
82                                         } else if (startNode) {
83                                                 // Intersecting node
84                                                 innerNodes.push(curNode);
85                                         }
86
87                                         if (!startNode && curNode.length + atIndex > matchLocation[0]) {
88                                                 // We've found the match start
89                                                 startNode = curNode;
90                                                 startNodeIndex = matchLocation[0] - atIndex;
91                                         }
92
93                                         atIndex += curNode.length;
94                                 }
95
96                                 if (startNode && endNode) {
97                                         curNode = replaceFn({
98                                                 startNode: startNode,
99                                                 startNodeIndex: startNodeIndex,
100                                                 endNode: endNode,
101                                                 endNodeIndex: endNodeIndex,
102                                                 innerNodes: innerNodes,
103                                                 match: matchLocation[2],
104                                                 matchIndex: matchIndex
105                                         });
106
107                                         // replaceFn has to return the node that replaced the endNode
108                                         // and then we step back so we can continue from the end of the
109                                         // match:
110                                         atIndex -= (endNode.length - endNodeIndex);
111                                         startNode = null;
112                                         endNode = null;
113                                         innerNodes = [];
114                                         matchLocation = matches.shift();
115                                         matchIndex++;
116
117                                         if (!matchLocation) {
118                                                 break; // no more matches
119                                         }
120                                 } else if (!hiddenTextElementsMap[curNode.nodeName] && curNode.firstChild) {
121                                         // Move down
122                                         curNode = curNode.firstChild;
123                                         continue;
124                                 } else if (curNode.nextSibling) {
125                                         // Move forward:
126                                         curNode = curNode.nextSibling;
127                                         continue;
128                                 }
129
130                                 // Move forward or up:
131                                 while (true) {
132                                         if (curNode.nextSibling) {
133                                                 curNode = curNode.nextSibling;
134                                                 break;
135                                         } else if (curNode.parentNode !== node) {
136                                                 curNode = curNode.parentNode;
137                                         } else {
138                                                 break out;
139                                         }
140                                 }
141                         }
142                 }
143
144                 /**
145                 * Generates the actual replaceFn which splits up text nodes
146                 * and inserts the replacement element.
147                 */
148                 function genReplacer(nodeName) {
149                         var makeReplacementNode;
150
151                         if (typeof nodeName != 'function') {
152                                 var stencilNode = nodeName.nodeType ? nodeName : doc.createElement(nodeName);
153
154                                 makeReplacementNode = function(fill, matchIndex) {
155                                         var clone = stencilNode.cloneNode(false);
156
157                                         clone.setAttribute('data-mce-index', matchIndex);
158
159                                         if (fill) {
160                                                 clone.appendChild(doc.createTextNode(fill));
161                                         }
162
163                                         return clone;
164                                 };
165                         } else {
166                                 makeReplacementNode = nodeName;
167                         }
168
169                         return function replace(range) {
170                                 var before, after, parentNode, startNode = range.startNode,
171                                         endNode = range.endNode, matchIndex = range.matchIndex;
172
173                                 if (startNode === endNode) {
174                                         var node = startNode;
175
176                                         parentNode = node.parentNode;
177                                         if (range.startNodeIndex > 0) {
178                                                 // Add `before` text node (before the match)
179                                                 before = doc.createTextNode(node.data.substring(0, range.startNodeIndex));
180                                                 parentNode.insertBefore(before, node);
181                                         }
182
183                                         // Create the replacement node:
184                                         var el = makeReplacementNode(range.match[0], matchIndex);
185                                         parentNode.insertBefore(el, node);
186                                         if (range.endNodeIndex < node.length) {
187                                                 // Add `after` text node (after the match)
188                                                 after = doc.createTextNode(node.data.substring(range.endNodeIndex));
189                                                 parentNode.insertBefore(after, node);
190                                         }
191
192                                         node.parentNode.removeChild(node);
193
194                                         return el;
195                                 } else {
196                                         // Replace startNode -> [innerNodes...] -> endNode (in that order)
197                                         before = doc.createTextNode(startNode.data.substring(0, range.startNodeIndex));
198                                         after = doc.createTextNode(endNode.data.substring(range.endNodeIndex));
199                                         var elA = makeReplacementNode(startNode.data.substring(range.startNodeIndex), matchIndex);
200                                         var innerEls = [];
201
202                                         for (var i = 0, l = range.innerNodes.length; i < l; ++i) {
203                                                 var innerNode = range.innerNodes[i];
204                                                 var innerEl = makeReplacementNode(innerNode.data, matchIndex);
205                                                 innerNode.parentNode.replaceChild(innerEl, innerNode);
206                                                 innerEls.push(innerEl);
207                                         }
208
209                                         var elB = makeReplacementNode(endNode.data.substring(0, range.endNodeIndex), matchIndex);
210
211                                         parentNode = startNode.parentNode;
212                                         parentNode.insertBefore(before, startNode);
213                                         parentNode.insertBefore(elA, startNode);
214                                         parentNode.removeChild(startNode);
215
216                                         parentNode = endNode.parentNode;
217                                         parentNode.insertBefore(elB, endNode);
218                                         parentNode.insertBefore(after, endNode);
219                                         parentNode.removeChild(endNode);
220
221                                         return elB;
222                                 }
223                         };
224                 }
225
226                 text = getText(node);
227                 if (text && regex.global) {
228                         while ((m = regex.exec(text))) {
229                                 matches.push(getMatchIndexes(m));
230                         }
231                 }
232
233                 function filter(callback) {
234                         var filteredMatches = [];
235
236                         each(function(match, i) {
237                                 if (callback(match, i)) {
238                                         filteredMatches.push(match);
239                                 }
240                         });
241
242                         matches = filteredMatches;
243
244                         /*jshint validthis:true*/
245                         return this;
246                 }
247
248                 function each(callback) {
249                         for (var i = 0, l = matches.length; i < l; i++) {
250                                 if (callback(matches[i], i) === false) {
251                                         break;
252                                 }
253                         }
254
255                         /*jshint validthis:true*/
256                         return this;
257                 }
258
259                 function mark(replacementNode) {
260                         if (matches.length) {
261                                 count = matches.length;
262                                 stepThroughMatches(node, matches, genReplacer(replacementNode));
263                         }
264
265                         /*jshint validthis:true*/
266                         return this;
267                 }
268
269                 return {
270                         text: text,
271                         count: count,
272                         matches: matches,
273                         each: each,
274                         filter: filter,
275                         mark: mark
276                 };
277         };
278 });