PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 7.0.3
Jetpack – WP Security, Backup, Speed, & Growth v7.0.3
15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / modules / after-the-deadline / atd.core.js
jetpack / modules / after-the-deadline Last commit date
rtl 10 years ago tinymce 7 years ago atd-autoproofread.js 10 years ago atd-nonvis-editor-plugin.js 9 years ago atd-rtl.css 9 years ago atd-rtl.min.css 9 years ago atd.core.js 9 years ago atd.css 9 years ago atd.min.css 9 years ago button.gif 15 years ago config-options.php 7 years ago config-unignore.php 7 years ago jquery.atd.js 7 years ago proxy.php 7 years ago
atd.core.js
641 lines
1 /*
2 * atd.core.js - A building block to create a front-end for AtD
3 * Author : Raphael Mudge, Automattic
4 * License : LGPL
5 * Project : http://www.afterthedeadline.com/developers.slp
6 * Contact : raffi@automattic.com
7 */
8
9 /* jshint sub: true, devel: true, onevar: false, smarttabs: true, loopfunc: true */
10 /* exported EXPORTED_SYMBOLS, atd_sprintf */
11
12 /* EXPORTED_SYMBOLS is set so this file can be a JavaScript Module */
13 var EXPORTED_SYMBOLS = ['AtDCore'];
14
15 function AtDCore() {
16 /* these are the categories of errors AtD should ignore */
17 this.ignore_types = ['Bias Language', 'Cliches', 'Complex Expression', 'Diacritical Marks', 'Double Negatives', 'Hidden Verbs', 'Jargon Language', 'Passive voice', 'Phrases to Avoid', 'Redundant Expression'];
18
19 /* these are the phrases AtD should ignore */
20 this.ignore_strings = {};
21
22 /* Localized strings */
23 // Back-compat, not used
24 this.i18n = {};
25 }
26
27 /*
28 * Internationalization Functions
29 */
30
31 AtDCore.prototype.getLang = function( key, defaultk ) {
32 return ( window.AtD_l10n_r0ar && window.AtD_l10n_r0ar[key] ) || defaultk;
33 };
34
35 AtDCore.prototype.addI18n = function( obj ) {
36 // Back-compat
37 window.AtD_l10n_r0ar = obj;
38 };
39
40 /*
41 * Setters
42 */
43
44 AtDCore.prototype.setIgnoreStrings = function(string) {
45 var parent = this;
46
47 this.map(string.split(/,\s*/g), function(string) {
48 parent.ignore_strings[string] = 1;
49 });
50 };
51
52 AtDCore.prototype.showTypes = function(string) {
53 var show_types = string.split(/,\s*/g);
54 var types = {};
55
56 /* set some default types that we want to make optional */
57
58 /* grammar checker options */
59 types['Double Negatives'] = 1;
60 types['Hidden Verbs'] = 1;
61 types['Passive voice'] = 1;
62 types['Bias Language'] = 1;
63
64 /* style checker options */
65 types['Cliches'] = 1;
66 types['Complex Expression'] = 1;
67 types['Diacritical Marks'] = 1;
68 types['Jargon Language'] = 1;
69 types['Phrases to Avoid'] = 1;
70 types['Redundant Expression'] = 1;
71
72 var ignore_types = [];
73
74 this.map(show_types, function(string) {
75 types[string] = undefined;
76 });
77
78 this.map(this.ignore_types, function(string) {
79 if (types[string] !== undefined) {
80 ignore_types.push(string);
81 }
82 });
83
84 this.ignore_types = ignore_types;
85 };
86
87 /*
88 * Error Parsing Code
89 */
90
91 AtDCore.prototype.makeError = function(error_s, tokens, type, seps/*, pre*/) {
92 var struct = {};
93 struct.type = type;
94 struct.string = error_s;
95 struct.tokens = tokens;
96
97 if (new RegExp('\\b' + error_s + '\\b').test(error_s)) {
98 struct.regexp = new RegExp('(?!'+error_s+'<)\\b' + error_s.replace(/\s+/g, seps) + '\\b');
99 }
100 else if (new RegExp(error_s + '\\b').test(error_s)) {
101 struct.regexp = new RegExp('(?!'+error_s+'<)' + error_s.replace(/\s+/g, seps) + '\\b');
102 }
103 else if (new RegExp('\\b' + error_s).test(error_s)) {
104 struct.regexp = new RegExp('(?!'+error_s+'<)\\b' + error_s.replace(/\s+/g, seps));
105 }
106 else {
107 struct.regexp = new RegExp('(?!'+error_s+'<)' + error_s.replace(/\s+/g, seps));
108 }
109
110 struct.used = false; /* flag whether we've used this rule or not */
111
112 return struct;
113 };
114
115 AtDCore.prototype.addToErrorStructure = function(errors, list, type, seps) {
116 var parent = this;
117
118 this.map(list, function(error) {
119 var tokens = error['word'].split(/\s+/);
120 var pre = error['pre'];
121 var first = tokens[0];
122
123 if (errors['__' + first] === undefined) {
124 errors['__' + first] = {};
125 errors['__' + first].pretoks = {};
126 errors['__' + first].defaults = [];
127 }
128
129 if (pre === '') {
130 errors['__' + first].defaults.push(parent.makeError(error['word'], tokens, type, seps, pre));
131 } else {
132 if (errors['__' + first].pretoks['__' + pre] === undefined) {
133 errors['__' + first].pretoks['__' + pre] = [];
134 }
135
136 errors['__' + first].pretoks['__' + pre].push(parent.makeError(error['word'], tokens, type, seps, pre));
137 }
138 });
139 };
140
141 AtDCore.prototype.buildErrorStructure = function(spellingList, enrichmentList, grammarList) {
142 var seps = this._getSeparators();
143 var errors = {};
144
145 this.addToErrorStructure(errors, spellingList, 'hiddenSpellError', seps);
146 this.addToErrorStructure(errors, grammarList, 'hiddenGrammarError', seps);
147 this.addToErrorStructure(errors, enrichmentList, 'hiddenSuggestion', seps);
148 return errors;
149 };
150
151 AtDCore.prototype._getSeparators = function() {
152 var re = '', i;
153 var str = '"s!#$%&()*+,./:;<=>?@[\\]^_{|}';
154
155 // Build word separator regexp
156 for (i=0; i<str.length; i++) {
157 re += '\\' + str.charAt(i);
158 }
159
160 return '(?:(?:[\xa0' + re + '])|(?:\\-\\-))+';
161 };
162
163 AtDCore.prototype.processXML = function(responseXML) {
164
165 /* types of errors to ignore */
166 var types = {};
167
168 this.map(this.ignore_types, function(type) {
169 types[type] = 1;
170 });
171
172 /* save suggestions in the editor object */
173 this.suggestions = [];
174
175 /* process through the errors */
176 var errors = responseXML.getElementsByTagName('error');
177
178 /* words to mark */
179 var grammarErrors = [];
180 var spellingErrors = [];
181 var enrichment = [];
182
183 for (var i = 0; i < errors.length; i++) {
184 if (errors[i].getElementsByTagName('string').item(0).firstChild !== null) {
185 var errorString = errors[i].getElementsByTagName('string').item(0).firstChild.data;
186 var errorType = errors[i].getElementsByTagName('type').item(0).firstChild.data;
187 var errorDescription = errors[i].getElementsByTagName('description').item(0).firstChild.data;
188
189 var errorContext;
190
191 if (errors[i].getElementsByTagName('precontext').item(0).firstChild !== null) {
192 errorContext = errors[i].getElementsByTagName('precontext').item(0).firstChild.data;
193 } else {
194 errorContext = '';
195 }
196
197 /* create a hashtable with information about the error in the editor object, we will use this later
198 to populate a popup menu with information and suggestions about the error */
199
200 if (this.ignore_strings[errorString] === undefined) {
201 var suggestion = {};
202 suggestion['description'] = errorDescription;
203 suggestion['suggestions'] = [];
204
205 /* used to find suggestions when a highlighted error is clicked on */
206 suggestion['matcher'] = new RegExp('^' + errorString.replace(/\s+/, this._getSeparators()) + '$');
207
208 suggestion['context'] = errorContext;
209 suggestion['string'] = errorString;
210 suggestion['type'] = errorType;
211
212 this.suggestions.push(suggestion);
213
214 if (errors[i].getElementsByTagName('suggestions').item(0) !== null) {
215 var suggestions = errors[i].getElementsByTagName('suggestions').item(0).getElementsByTagName('option');
216 for (var j = 0; j < suggestions.length; j++) {
217 suggestion['suggestions'].push(suggestions[j].firstChild.data);
218 }
219 }
220
221 /* setup the more info url */
222 if (errors[i].getElementsByTagName('url').item(0) !== null) {
223 var errorUrl = errors[i].getElementsByTagName('url').item(0).firstChild.data;
224 suggestion['moreinfo'] = errorUrl + '&theme=tinymce';
225 }
226
227 if (types[errorDescription] === undefined) {
228 if (errorType === 'suggestion') {
229 enrichment.push({ word: errorString, pre: errorContext });
230 }
231
232 if (errorType === 'grammar') {
233 grammarErrors.push({ word: errorString, pre: errorContext });
234 }
235 }
236
237 if (errorType === 'spelling' || errorDescription === 'Homophone') {
238 spellingErrors.push({ word: errorString, pre: errorContext });
239 }
240
241 if (errorDescription === 'Cliches') {
242 suggestion['description'] = 'Clichés'; /* done here for backwards compatability with current user settings */
243 }
244
245 if (errorDescription === 'Spelling') {
246 suggestion['description'] = this.getLang('menu_title_spelling', 'Spelling');
247 }
248
249 if (errorDescription === 'Repeated Word') {
250 suggestion['description'] = this.getLang('menu_title_repeated_word', 'Repeated Word');
251 }
252
253 if (errorDescription === 'Did you mean...') {
254 suggestion['description'] = this.getLang('menu_title_confused_word', 'Did you mean...');
255 }
256 } // end if ignore[errorString] == undefined
257 } // end if
258 } // end for loop
259
260 var errorStruct;
261 var ecount = spellingErrors.length + grammarErrors.length + enrichment.length;
262
263 if (ecount > 0) {
264 errorStruct = this.buildErrorStructure(spellingErrors, enrichment, grammarErrors);
265 } else {
266 errorStruct = undefined;
267 }
268
269 /* save some state in this object, for retrieving suggestions later */
270 return { errors: errorStruct, count: ecount, suggestions: this.suggestions };
271 };
272
273 AtDCore.prototype.findSuggestion = function(element) {
274 var text = element.innerHTML;
275 var context = ( this.getAttrib(element, 'pre') + '' ).replace(/[\\,!\\?\\."\s]/g, '');
276 if (this.getAttrib(element, 'pre') === undefined) {
277 alert(element.innerHTML);
278 }
279
280 var errorDescription;
281 var len = this.suggestions.length;
282
283 for (var i = 0; i < len; i++) {
284 if ((context === '' || context === this.suggestions[i]['context']) && this.suggestions[i]['matcher'].test(text)) {
285 errorDescription = this.suggestions[i];
286 break;
287 }
288 }
289 return errorDescription;
290 };
291
292 /*
293 * TokenIterator class
294 */
295
296 function TokenIterator(tokens) {
297 this.tokens = tokens;
298 this.index = 0;
299 this.count = 0;
300 this.last = 0;
301 }
302
303 TokenIterator.prototype.next = function() {
304 var current = this.tokens[this.index];
305 this.count = this.last;
306 this.last += current.length + 1;
307 this.index++;
308
309 /* strip single quotes from token, AtD does this when presenting errors */
310 if (current !== '') {
311 if (current[0] === '\'') {
312 current = current.substring(1, current.length);
313 }
314
315 if (current[current.length - 1] === '\'') {
316 current = current.substring(0, current.length - 1);
317 }
318 }
319
320 return current;
321 };
322
323 TokenIterator.prototype.hasNext = function() {
324 return this.index < this.tokens.length;
325 };
326
327 TokenIterator.prototype.hasNextN = function(n) {
328 return (this.index + n) < this.tokens.length;
329 };
330
331 TokenIterator.prototype.skip = function(m, n) {
332 this.index += m;
333 this.last += n;
334
335 if (this.index < this.tokens.length) {
336 this.count = this.last - this.tokens[this.index].length;
337 }
338 };
339
340 TokenIterator.prototype.getCount = function() {
341 return this.count;
342 };
343
344 TokenIterator.prototype.peek = function(n) {
345 var peepers = [];
346 var end = this.index + n;
347 for (var x = this.index; x < end; x++) {
348 peepers.push(this.tokens[x]);
349 }
350 return peepers;
351 };
352
353 /*
354 * code to manage highlighting of errors
355 */
356 AtDCore.prototype.markMyWords = function(container_nodes, errors) {
357 var seps = new RegExp(this._getSeparators()),
358 nl = [],
359 ecount = 0, /* track number of highlighted errors */
360 parent = this,
361 bogus = this._isTinyMCE ? ' data-mce-bogus="1"' : '',
362 emptySpan = '<span class="mceItemHidden"' + bogus + '>&nbsp;</span>',
363 textOnlyMode;
364
365 /**
366 * Split a text node into an ordered list of siblings:
367 * - text node to the left of the match
368 * - the element replacing the match
369 * - text node to the right of the match
370 *
371 * We have to leave the text to the left and right of the match alone
372 * in order to prevent XSS
373 *
374 * @return array
375 */
376 function splitTextNode( textnode, regexp, replacement ) {
377 var text = textnode.nodeValue,
378 index = text.search( regexp ),
379 match = text.match( regexp ),
380 captured = [],
381 cursor;
382
383 if ( index < 0 || ! match.length ) {
384 return [ textnode ];
385 }
386
387 if ( index > 0 ) {
388 // capture left text node
389 captured.push( document.createTextNode( text.substr( 0, index ) ) );
390 }
391
392 // capture the replacement of the matched string
393 captured.push( parent.create( match[0].replace( regexp, replacement ) ) );
394
395 cursor = index + match[0].length;
396
397 if ( cursor < text.length ) {
398 // capture right text node
399 captured.push( document.createTextNode( text.substr( cursor ) ) );
400 }
401
402 return captured;
403 }
404
405 function _isInPre( node ) {
406 if ( node ) {
407 while ( node.parentNode ) {
408 if ( node.nodeName === 'PRE' ) {
409 return true;
410 }
411 node = node.parentNode;
412 }
413 }
414
415 return false;
416 }
417
418 /* Collect all text nodes */
419 /* Our goal--ignore nodes that are already wrapped */
420
421 this._walk( container_nodes, function( n ) {
422 if ( n.nodeType === 3 && ! parent.isMarkedNode( n ) && ! _isInPre( n ) ) {
423 nl.push( n );
424 }
425 });
426
427 /* walk through the relevant nodes */
428
429 var iterator;
430
431 this.map( nl, function( n ) {
432 var v;
433
434 if ( n.nodeType === 3 ) {
435 v = n.nodeValue; /* we don't want to mangle the HTML so use the actual encoded string */
436 var tokens = n.nodeValue.split( seps ); /* split on the unencoded string so we get access to quotes as " */
437 var previous = '';
438
439 var doReplaces = [];
440
441 iterator = new TokenIterator(tokens);
442
443 while ( iterator.hasNext() ) {
444 var token = iterator.next();
445 var current = errors['__' + token];
446
447 var defaults;
448
449 if ( current !== undefined && current.pretoks !== undefined ) {
450 defaults = current.defaults;
451 current = current.pretoks['__' + previous];
452
453 var done = false;
454 var prev, curr;
455
456 prev = v.substr(0, iterator.getCount());
457 curr = v.substr(prev.length, v.length);
458
459 var checkErrors = function( error ) {
460 if ( error !== undefined && ! error.used && foundStrings[ '__' + error.string ] === undefined && error.regexp.test( curr ) ) {
461 foundStrings[ '__' + error.string ] = 1;
462 doReplaces.push([ error.regexp, '<span class="'+error.type+'" pre="'+previous+'"' + bogus + '>$&</span>' ]);
463
464 error.used = true;
465 done = true;
466 }
467 }; // jshint ignore:line
468
469 var foundStrings = {};
470
471 if (current !== undefined) {
472 previous = previous + ' ';
473 parent.map(current, checkErrors);
474 }
475
476 if (!done) {
477 previous = '';
478 parent.map(defaults, checkErrors);
479 }
480 }
481
482 previous = token;
483 } // end while
484
485 /* do the actual replacements on this span */
486 if ( doReplaces.length > 0 ) {
487 var newNode = n;
488
489 for ( var x = 0; x < doReplaces.length; x++ ) {
490 var regexp = doReplaces[x][0], result = doReplaces[x][1];
491
492 /* it's assumed that this function is only being called on text nodes (nodeType == 3), the iterating is necessary
493 because eventually the whole thing gets wrapped in an mceItemHidden span and from there it's necessary to
494 handle each node individually. */
495 var bringTheHurt = function( node ) {
496 var span, splitNodes;
497
498 if ( node.nodeType === 3 ) {
499 ecount++;
500
501 /* sometimes IE likes to ignore the space between two spans, solution is to insert a placeholder span with
502 a non-breaking space. The markup removal code substitutes this span for a space later */
503 if ( parent.isIE() && node.nodeValue.length > 0 && node.nodeValue.substr(0, 1) === ' ' ) {
504 return parent.create( emptySpan + node.nodeValue.substr( 1, node.nodeValue.length - 1 ).replace( regexp, result ), false );
505 } else {
506 if ( textOnlyMode ) {
507 return parent.create( node.nodeValue.replace( regexp, result ), false );
508 }
509
510 span = parent.create( '<span />' );
511 if ( typeof textOnlyMode === 'undefined' ) {
512 // cache this to avoid adding / removing nodes unnecessarily
513 textOnlyMode = typeof span.appendChild !== 'function';
514 if ( textOnlyMode ) {
515 parent.remove( span );
516 return parent.create( node.nodeValue.replace( regexp, result ), false );
517 }
518 }
519
520 // "Visual" mode
521 splitNodes = splitTextNode( node, regexp, result );
522 for ( var i = 0; i < splitNodes.length; i++ ) {
523 span.appendChild( splitNodes[i] );
524 }
525
526 node = span;
527 return node;
528 }
529 }
530 else {
531 var contents = parent.contents(node);
532
533 for ( var y = 0; y < contents.length; y++ ) {
534 if ( contents[y].nodeType === 3 && regexp.test( contents[y].nodeValue ) ) {
535 var nnode;
536
537 if ( parent.isIE() && contents[y].nodeValue.length > 0 && contents[y].nodeValue.substr(0, 1) === ' ') {
538 nnode = parent.create( emptySpan + contents[y].nodeValue.substr( 1, contents[y].nodeValue.length - 1 ).replace( regexp, result ), true );
539 } else {
540 nnode = parent.create( contents[y].nodeValue.replace( regexp, result ), true );
541 }
542
543 parent.replaceWith( contents[y], nnode );
544 parent.removeParent( nnode );
545
546 ecount++;
547
548 return node; /* we did a replacement so we can call it quits, errors only get used once */
549 }
550 }
551
552 return node;
553 }
554 }; // jshint ignore:line
555
556 newNode = bringTheHurt(newNode);
557 }
558
559 parent.replaceWith(n, newNode);
560 }
561 }
562 });
563
564 return ecount;
565 };
566
567 AtDCore.prototype._walk = function(elements, f) {
568 var i;
569 for (i = 0; i < elements.length; i++) {
570 f.call(f, elements[i]);
571 this._walk(this.contents(elements[i]), f);
572 }
573 };
574
575 AtDCore.prototype.removeWords = function(node, w) {
576 var count = 0;
577 var parent = this;
578
579 this.map(this.findSpans(node).reverse(), function(n) {
580 if (n && (parent.isMarkedNode(n) || parent.hasClass(n, 'mceItemHidden') || parent.isEmptySpan(n)) ) {
581 if (n.innerHTML === '&nbsp;') {
582 var nnode = document.createTextNode(' '); /* hax0r */
583 parent.replaceWith(n, nnode);
584 } else if (!w || n.innerHTML === w) {
585 parent.removeParent(n);
586 count++;
587 }
588 }
589 });
590
591 return count;
592 };
593
594 AtDCore.prototype.isEmptySpan = function(node) {
595 return (this.getAttrib(node, 'class') === '' && this.getAttrib(node, 'style') === '' && this.getAttrib(node, 'id') === '' && !this.hasClass(node, 'Apple-style-span') && this.getAttrib(node, 'mce_name') === '');
596 };
597
598 AtDCore.prototype.isMarkedNode = function(node) {
599 return (this.hasClass(node, 'hiddenGrammarError') || this.hasClass(node, 'hiddenSpellError') || this.hasClass(node, 'hiddenSuggestion'));
600 };
601
602 /*
603 * Context Menu Helpers
604 */
605 AtDCore.prototype.applySuggestion = function(element, suggestion) {
606 if (suggestion === '(omit)') {
607 this.remove(element);
608 }
609 else {
610 var node = this.create(suggestion);
611 this.replaceWith(element, node);
612 this.removeParent(node);
613 }
614 };
615
616 /*
617 * Check for an error
618 */
619 AtDCore.prototype.hasErrorMessage = function(xmlr) {
620 return (xmlr !== undefined && xmlr.getElementsByTagName('message').item(0) !== null);
621 };
622
623 AtDCore.prototype.getErrorMessage = function(xmlr) {
624 return xmlr.getElementsByTagName('message').item(0);
625 };
626
627 /* this should always be an error, alas... not practical */
628 AtDCore.prototype.isIE = function() {
629 return navigator.appName === 'Microsoft Internet Explorer';
630 };
631
632 // TODO: this doesn't seem used anywhere in AtD, moved here from install_atd_l10n.js for eventual back-compat
633 /* a quick poor man's sprintf */
634 function atd_sprintf(format, values) {
635 var result = format;
636 for (var x = 0; x < values.length; x++) {
637 result = result.replace(new RegExp('%' + (x + 1) + '\\$', 'g'), values[x]);
638 }
639 return result;
640 }
641