]> git.immae.eu Git - perso/Immae/Projets/packagist/connexionswing-ckeditor-component.git/blob - sources/plugins/scayt/plugin.js
Upgrade to 4.5.7 and add some plugin
[perso/Immae/Projets/packagist/connexionswing-ckeditor-component.git] / sources / plugins / scayt / plugin.js
1 'use strict';
2 CKEDITOR.plugins.add('scayt', {
3
4 //requires : ['menubutton', 'dialog'],
5 requires: 'menubutton,dialog',
6 lang: 'af,ar,bg,bn,bs,ca,cs,cy,da,de,el,en-au,en-ca,en-gb,en,eo,es,et,eu,fa,fi,fo,fr-ca,fr,gl,gu,he,hi,hr,hu,is,it,ja,ka,km,ko,lt,lv,mk,mn,ms,nb,nl,no,pl,pt-br,pt,ro,ru,sk,sl,sr-latn,sr,sv,th,tr,ug,uk,vi,zh-cn,zh', // %REMOVE_LINE_CORE%
7 icons: 'scayt', // %REMOVE_LINE_CORE%
8 hidpi: true, // %REMOVE_LINE_CORE%
9 tabToOpen : null,
10 dialogName: 'scaytDialog',
11 init: function(editor) {
12 var self = this,
13 plugin = CKEDITOR.plugins.scayt;
14
15 this.bindEvents(editor);
16 this.parseConfig(editor);
17 this.addRule(editor);
18
19 // source mode
20 CKEDITOR.dialog.add(this.dialogName, CKEDITOR.getUrl(this.path + 'dialogs/options.js'));
21 // end source mode
22
23 this.addMenuItems(editor);
24 var config = editor.config,
25 lang = editor.lang.scayt,
26 env = CKEDITOR.env;
27
28 editor.ui.add('Scayt', CKEDITOR.UI_MENUBUTTON, {
29 label : lang.text_title,
30 title : ( editor.plugins.wsc ? editor.lang.wsc.title : lang.text_title ),
31 // SCAYT doesn't work in IE Compatibility Mode and IE (8 & 9) Quirks Mode
32 modes : {wysiwyg: !(env.ie && ( env.version < 8 || env.quirks ) ) },
33 toolbar: 'spellchecker,20',
34 refresh: function() {
35 var buttonState = editor.ui.instances.Scayt.getState();
36
37 // check if scayt is created
38 if(editor.scayt) {
39 // check if scayt is enabled
40 if(plugin.state.scayt[editor.name]) {
41 buttonState = CKEDITOR.TRISTATE_ON;
42 } else {
43 buttonState = CKEDITOR.TRISTATE_OFF;
44 }
45 }
46
47 editor.fire('scaytButtonState', buttonState);
48 },
49 onRender: function() {
50 var that = this;
51
52 editor.on('scaytButtonState', function(ev) {
53 if(typeof ev.data !== undefined) {
54 that.setState(ev.data);
55 }
56 });
57 },
58 onMenu : function() {
59 var scaytInstance = editor.scayt;
60
61 editor.getMenuItem('scaytToggle').label = editor.lang.scayt[(scaytInstance ? plugin.state.scayt[editor.name] : false) ? 'btn_disable' : 'btn_enable'];
62
63 // If UI tab is disabled we shouldn't show menu item
64 var menuDefinition = {
65 scaytToggle : CKEDITOR.TRISTATE_OFF,
66 scaytOptions : scaytInstance ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,
67 scaytLangs : scaytInstance ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,
68 scaytDict : scaytInstance ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,
69 scaytAbout : scaytInstance ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,
70 WSC : editor.plugins.wsc ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED
71 };
72
73 if(!editor.config.scayt_uiTabs[0]) {
74 delete menuDefinition.scaytOptions;
75 }
76
77 if(!editor.config.scayt_uiTabs[1]) {
78 delete menuDefinition.scaytLangs;
79 }
80
81 if(!editor.config.scayt_uiTabs[2]) {
82 delete menuDefinition.scaytDict;
83 }
84
85 return menuDefinition;
86 }
87 });
88
89 // If the 'contextmenu' plugin is loaded, register the listeners.
90 if(editor.contextMenu && editor.addMenuItems) {
91 editor.contextMenu.addListener(function(element, selection) {
92 var scaytInstance = editor.scayt,
93 result, selectionNode;
94
95 if(scaytInstance) {
96 selectionNode = scaytInstance.getSelectionNode();
97
98 // SCAYT shouldn't build context menu if instance isnot created or word is without misspelling or grammar problem
99 if(selectionNode) {
100 var items = self.menuGenerator(editor, selectionNode);
101
102 scaytInstance.showBanner('.' + editor.contextMenu._.definition.panel.className.split(' ').join(' .'));
103 result = items;
104 }
105 }
106
107 return result;
108 });
109
110 editor.contextMenu._.onHide = CKEDITOR.tools.override(editor.contextMenu._.onHide, function(org) {
111 return function() {
112 var scaytInstance = editor.scayt;
113
114 if(scaytInstance) {
115 scaytInstance.hideBanner();
116 }
117
118 return org.apply(this);
119 };
120 });
121 }
122 },
123 addMenuItems: function(editor) {
124 var self = this,
125 plugin = CKEDITOR.plugins.scayt,
126 graytGroups = ['grayt_description', 'grayt_suggest', 'grayt_control'],
127 menuGroup = 'scaytButton';
128
129 editor.addMenuGroup(menuGroup);
130
131 var items_order = editor.config.scayt_contextMenuItemsOrder.split('|');
132
133 for(var pos = 0 ; pos < items_order.length ; pos++) {
134 items_order[pos] = 'scayt_' + items_order[pos];
135 }
136 items_order = graytGroups.concat(items_order);
137
138 if(items_order && items_order.length) {
139 for(var pos = 0 ; pos < items_order.length ; pos++) {
140 editor.addMenuGroup(items_order[pos], pos - 10);
141 }
142 }
143
144 editor.addCommand( 'scaytToggle', {
145 exec: function(editor) {
146 var scaytInstance = editor.scayt;
147
148 plugin.state.scayt[editor.name] = !plugin.state.scayt[editor.name];
149
150 if(plugin.state.scayt[editor.name] === true) {
151 if(!scaytInstance) {
152 plugin.createScayt(editor);
153 }
154 } else {
155 if(scaytInstance) {
156 plugin.destroy(editor);
157 }
158 }
159 }
160 } );
161
162 editor.addCommand( 'scaytAbout', {
163 exec: function(editor) {
164 var scaytInstance = editor.scayt;
165
166 scaytInstance.tabToOpen = 'about';
167 editor.lockSelection();
168 editor.openDialog(self.dialogName);
169 }
170 } );
171
172 editor.addCommand( 'scaytOptions', {
173 exec: function(editor) {
174 var scaytInstance = editor.scayt;
175
176 scaytInstance.tabToOpen = 'options';
177 editor.lockSelection();
178 editor.openDialog(self.dialogName);
179 }
180 } );
181
182 editor.addCommand( 'scaytLangs', {
183 exec: function(editor) {
184 var scaytInstance = editor.scayt;
185
186 scaytInstance.tabToOpen = 'langs';
187 editor.lockSelection();
188 editor.openDialog(self.dialogName);
189 }
190 } );
191
192 editor.addCommand( 'scaytDict', {
193 exec: function(editor) {
194 var scaytInstance = editor.scayt;
195
196 scaytInstance.tabToOpen = 'dictionaries';
197 editor.lockSelection();
198 editor.openDialog(self.dialogName);
199 }
200 } );
201
202 var uiMenuItems = {
203 scaytToggle: {
204 label : editor.lang.scayt.btn_enable,
205 group : menuGroup,
206 command: 'scaytToggle'
207 },
208 scaytAbout: {
209 label : editor.lang.scayt.btn_about,
210 group : menuGroup,
211 command: 'scaytAbout'
212 },
213 scaytOptions: {
214 label : editor.lang.scayt.btn_options,
215 group : menuGroup,
216 command: 'scaytOptions'
217 },
218 scaytLangs: {
219 label : editor.lang.scayt.btn_langs,
220 group : menuGroup,
221 command: 'scaytLangs'
222 },
223 scaytDict: {
224 label : editor.lang.scayt.btn_dictionaries,
225 group : menuGroup,
226 command: 'scaytDict'
227 }
228 };
229
230 if(editor.plugins.wsc) {
231 uiMenuItems.WSC = {
232 label : editor.lang.wsc.toolbar,
233 group : menuGroup,
234 onClick: function() {
235 var inlineMode = (editor.elementMode == CKEDITOR.ELEMENT_MODE_INLINE),
236 plugin = CKEDITOR.plugins.scayt,
237 scaytInstance = editor.scayt,
238 text = inlineMode ? editor.container.getText() : editor.document.getBody().getText();
239
240 text = text.replace(/\s/g, '');
241
242 if(text) {
243 if(scaytInstance && plugin.state.scayt[editor.name] && scaytInstance.setMarkupPaused) {
244 scaytInstance.setMarkupPaused(true);
245 }
246
247 editor.lockSelection();
248 editor.execCommand('checkspell');
249 } else {
250 alert('Nothing to check!');
251 }
252 }
253 }
254 }
255
256 editor.addMenuItems(uiMenuItems);
257 },
258 bindEvents: function(editor) {
259 var self = this,
260 plugin = CKEDITOR.plugins.scayt,
261 inline_mode = (editor.elementMode == CKEDITOR.ELEMENT_MODE_INLINE);
262
263 var scaytDestroy = function() {
264 plugin.destroy(editor);
265 };
266
267 /*
268 * Dirty fix for placeholder drag&drop
269 * Should be fixed with next release
270 */
271 /*
272 editor.on('drop', function(evt) {
273 var dropRange = evt.data.dropRange;
274 var b = dropRange.createBookmark(true);
275 editor.scayt.removeMarkupInSelectionNode({ selectionNode: evt.data.target.$, forceBookmark: false });
276 dropRange.moveToBookmark(b);
277
278 evt.data.dropRange = dropRange;
279 return evt;
280 }, this, null, 0); // We should be sure that we modify dropRange before CKEDITOR.plugins.clipboard calls
281 */
282
283 var contentDomReady = function() {
284 // The event is fired when editable iframe node was reinited so we should restart our service
285 if (plugin.state.scayt[editor.name] && !editor.readOnly && !editor.scayt) {
286 plugin.createScayt(editor);
287 }
288 };
289
290 var addMarkupStateHandlers = function() {
291 var editable = editor.editable();
292
293 editable.attachListener( editable, 'focus', function( evt ) {
294 if( CKEDITOR.plugins.scayt && !editor.scayt ) {
295 setTimeout(contentDomReady, 0); // we need small timeout in order to correctly set initial 'focused' option value in SCAYT core
296 }
297
298 var pluginStatus = CKEDITOR.plugins.scayt && CKEDITOR.plugins.scayt.state.scayt[editor.name] && editor.scayt,
299 selectedElement, ranges, textLength, range;
300
301 if((inline_mode ? true : pluginStatus) && editor._.savedSelection) {
302 selectedElement = editor._.savedSelection.getSelectedElement();
303 ranges = !selectedElement && editor._.savedSelection.getRanges();
304
305 for(var i = 0; i < ranges.length; i++) {
306 range = ranges[i];
307 // we need to check type of node value in order to avoid error in IE when accessing 'nodeValue' property
308 if(typeof range.startContainer.$.nodeValue === 'string') {
309 textLength = range.startContainer.getText().length;
310 if(textLength < range.startOffset || textLength < range.endOffset) {
311 editor.unlockSelection(false);
312 }
313 }
314 }
315 }
316 }, this, null, -10 ); // priority "-10" is set to call SCAYT CKEDITOR.editor#unlockSelection before CKEDITOR.editor#unlockSelection call
317 };
318
319 var contentDomHandler = function() {
320 if(inline_mode) {
321
322 if (!editor.config.scayt_inlineModeImmediateMarkup) {
323 /*
324 * Give an opportunity to CKEditor to perform all needed updates
325 * and only after that call 'scaytDestroy' method (#72725)
326 */
327 editor.on('blur', function () { setTimeout( scaytDestroy, 0 ); } );
328 editor.on('focus', contentDomReady);
329
330 // We need to check if editor has focus(created) right now.
331 // If editor is active - make attempt to create scayt
332 if(editor.focusManager.hasFocus) {
333 contentDomReady();
334 }
335
336 } else {
337 contentDomReady();
338 }
339
340 } else {
341 contentDomReady();
342 }
343
344 addMarkupStateHandlers();
345
346 /*
347 * 'mousedown' handler handle widget selection (click on widget). To
348 * fix the issue when widget#wrapper referenced to element which can
349 * be broken after markup.
350 */
351 var editable = editor.editable();
352 editable.attachListener(editable, 'mousedown', function( evt ) {
353 var target = evt.data.getTarget();
354 var widget = editor.widgets && editor.widgets.getByElement( target );
355 if ( widget ) {
356 widget.wrapper = target.getAscendant( function( el ) {
357 return el.hasAttribute( 'data-cke-widget-wrapper' )
358 }, true );
359 }
360 }, this, null, -10); // '-10': we need to be shure that widget#wrapper updated before any other calls
361 };
362
363 editor.on('contentDom', contentDomHandler);
364
365 editor.on('beforeCommandExec', function(ev) {
366 var scaytInstance = editor.scayt,
367 selectedLangElement = null,
368 forceBookmark = false,
369 removeMarkupInsideSelection = true;
370
371 // TODO: after switching in source mode not recreate SCAYT instance, try to just rerun markuping to don't make requests to server
372 if(ev.data.name in plugin.options.disablingCommandExec && editor.mode == 'wysiwyg') {
373 if(scaytInstance) {
374 plugin.destroy(editor);
375 editor.fire('scaytButtonState', CKEDITOR.TRISTATE_DISABLED);
376 }
377 } else if( ev.data.name === 'bold' || ev.data.name === 'italic' || ev.data.name === 'underline' ||
378 ev.data.name === 'strike' || ev.data.name === 'subscript' || ev.data.name === 'superscript' ||
379 ev.data.name === 'enter' || ev.data.name === 'cut' || ev.data.name === 'language') {
380 if(scaytInstance) {
381 if(ev.data.name === 'cut') {
382 removeMarkupInsideSelection = false;
383 // We need to force bookmark before we remove our markup.
384 // Otherwise we will get issues with cutting text via context menu.
385 forceBookmark = true;
386 }
387
388 // We need to remove all SCAYT markup from 'lang' node before it will be deleted.
389 // We need to remove SCAYT markup from selected text before creating 'lang' node as well.
390 if(ev.data.name === 'language') {
391 selectedLangElement = editor.plugins.language.getCurrentLangElement(editor);
392 selectedLangElement = selectedLangElement && selectedLangElement.$;
393 // We need to force bookmark before we remove our markup.
394 // Otherwise we will get issues with cutting text via language plugin menu.
395 forceBookmark = true;
396 }
397
398 editor.fire('reloadMarkupScayt', {
399 removeOptions: {
400 removeInside: removeMarkupInsideSelection,
401 forceBookmark: forceBookmark,
402 selectionNode: selectedLangElement
403 },
404 timeout: 0
405 });
406 }
407 }
408 });
409
410 editor.on('beforeSetMode', function(ev) {
411 var scaytInstance;
412 // needed when we use:
413 // CKEDITOR.instances.editor_ID.setMode("source")
414 // CKEDITOR.instances.editor_ID.setMode("wysiwyg")
415 // can't be implemented in editor.on('mode', function(ev) {});
416 if (ev.data == 'source') {
417 scaytInstance = editor.scayt;
418 if(scaytInstance) {
419 plugin.destroy(editor);
420 editor.fire('scaytButtonState', CKEDITOR.TRISTATE_DISABLED);
421 }
422
423 // remove custom data from body, to prevent waste properties showing in IE8
424 if(editor.document) { //GitHub #84 : make sure that document exists(e.g. when startup mode set to 'source')
425 editor.document.getBody().removeAttribute('_jquid');
426 }
427 }
428 });
429
430 editor.on('afterCommandExec', function(ev) {
431 if(editor.mode == 'wysiwyg' && (ev.data.name == 'undo' || ev.data.name == 'redo')) {
432 setTimeout(function() {
433 var scaytInstance = editor.scayt,
434 scaytLangList = scaytInstance && scaytInstance.getScaytLangList();
435
436 /*
437 * Checks SCAYT initialization of LangList. To prevent immediate
438 * markup which is triggered by 'startSpellCheck' event.
439 * E.g.: Drop into inline CKEDITOR with scayt_autoStartup = true;
440 */
441 if (!scaytLangList || !(scaytLangList.ltr && scaytLangList.rtl)) return;
442
443 scaytInstance.fire('startSpellCheck, startGrammarCheck');
444 }, 250);
445 }
446 });
447
448 // handle readonly changes
449 editor.on('readOnly', function(ev) {
450 var scaytInstance;
451
452 if(ev) {
453 scaytInstance = editor.scayt;
454
455 if(ev.editor.readOnly === true) {
456 if(scaytInstance) {
457 scaytInstance.fire('removeMarkupInDocument', {});
458 }
459 } else {
460 if(scaytInstance) {
461 scaytInstance.fire('startSpellCheck, startGrammarCheck');
462 } else if(ev.editor.mode == 'wysiwyg' && plugin.state.scayt[ev.editor.name] === true) {
463 plugin.createScayt(editor);
464 ev.editor.fire('scaytButtonState', CKEDITOR.TRISTATE_ON);
465 }
466 }
467 }
468 });
469
470 // we need to destroy SCAYT before CK editor will be completely destroyed
471 editor.on('beforeDestroy', scaytDestroy);
472
473 //#9439 after SetData method fires contentDom event and SCAYT create additional instanse
474 // This way we should destroy SCAYT on setData event when contenteditable Iframe was re-created
475 editor.on('setData', function() {
476 scaytDestroy();
477
478 // in inline mode SetData does not fire contentDom event
479 if(editor.elementMode == CKEDITOR.ELEMENT_MODE_INLINE || editor.plugins.divarea) {
480 contentDomHandler();
481 }
482 }, this, null, 50);
483
484 /*
485 * Main entry point to react on changes in document
486 */
487 editor.on('reloadMarkupScayt', function(ev) {
488 var removeOptions = ev.data && ev.data.removeOptions,
489 timeout = ev.data && ev.data.timeout;
490
491 /*
492 * Perform removeMarkupInSelectionNode and 'startSpellCheck' fire
493 * asynchroniosly and keep CKEDITOR flow as expected
494 */
495 setTimeout(function() {
496 var scaytInstance = editor.scayt,
497 scaytLangList = scaytInstance && scaytInstance.getScaytLangList();
498
499 /*
500 * Checks SCAYT initialization of LangList. To prevent immediate
501 * markup which is triggered by 'startSpellCheck' event.
502 * E.g.: Drop into inline CKEDITOR with scayt_autoStartup = true;
503 */
504 if (!scaytLangList || !(scaytLangList.ltr && scaytLangList.rtl)) return;
505
506 /*
507 * CKEditor can keep \u200B character in document (with selection#selectRanges)
508 * we need to take care about that. For this case we fire
509 * 'keydown' [left arrow], what will trigger 'removeFillingChar' on Webkit
510 * to cleanup the document
511 */
512 editor.document.fire( 'keydown', new CKEDITOR.dom.event( { keyCode: 37 } ) );
513
514 /* trigger remove markup with 'startSpellCheck' */
515 scaytInstance.removeMarkupInSelectionNode(removeOptions);
516 scaytInstance.fire('startSpellCheck, startGrammarCheck');
517 }, timeout || 0 );
518 });
519
520 // Reload spell-checking for current word after insertion completed.
521 editor.on('insertElement', function() {
522 // IE bug: we need wait here to make sure that focus is returned to editor, and we can store the selection before we proceed with markup
523 editor.fire('reloadMarkupScayt', {removeOptions: {forceBookmark: true}});
524 }, this, null, 50);
525
526 editor.on('insertHtml', function() {
527 editor.fire('reloadMarkupScayt');
528 }, this, null, 50);
529
530 editor.on('insertText', function() {
531 editor.fire('reloadMarkupScayt');
532 }, this, null, 50);
533
534 // The event is listening to open necessary dialog tab
535 editor.on('scaytDialogShown', function(ev) {
536 var dialog = ev.data,
537 scaytInstance = editor.scayt;
538
539 dialog.selectPage(scaytInstance.tabToOpen);
540 });
541 },
542 parseConfig: function(editor) {
543 var plugin = CKEDITOR.plugins.scayt;
544
545 // preprocess config for backward compatibility
546 plugin.replaceOldOptionsNames(editor.config);
547
548 // Checking editor's config after initialization
549 if(typeof editor.config.scayt_autoStartup !== 'boolean') {
550 editor.config.scayt_autoStartup = false;
551 }
552 plugin.state.scayt[editor.name] = editor.config.scayt_autoStartup;
553
554 if(typeof editor.config.grayt_autoStartup !== 'boolean') {
555 editor.config.grayt_autoStartup = false;
556 }
557 if(typeof editor.config.scayt_inlineModeImmediateMarkup !== 'boolean') {
558 editor.config.scayt_inlineModeImmediateMarkup = false;
559 }
560 plugin.state.grayt[editor.name] = editor.config.grayt_autoStartup;
561
562 if(!editor.config.scayt_contextCommands) {
563 editor.config.scayt_contextCommands = 'ignore|ignoreall|add';
564 }
565
566 if(!editor.config.scayt_contextMenuItemsOrder) {
567 editor.config.scayt_contextMenuItemsOrder = 'suggest|moresuggest|control';
568 }
569
570 if(!editor.config.scayt_sLang) {
571 editor.config.scayt_sLang = 'en_US';
572 }
573
574 if(editor.config.scayt_maxSuggestions === undefined || typeof editor.config.scayt_maxSuggestions != 'number' || editor.config.scayt_maxSuggestions < 0) {
575 editor.config.scayt_maxSuggestions = 5;
576 }
577
578 if(editor.config.scayt_minWordLength === undefined || typeof editor.config.scayt_minWordLength != 'number' || editor.config.scayt_minWordLength < 1) {
579 editor.config.scayt_minWordLength = 4;
580 }
581
582 if(editor.config.scayt_customDictionaryIds === undefined || typeof editor.config.scayt_customDictionaryIds !== 'string') {
583 editor.config.scayt_customDictionaryIds = '';
584 }
585
586 if(editor.config.scayt_userDictionaryName === undefined || typeof editor.config.scayt_userDictionaryName !== 'string') {
587 editor.config.scayt_userDictionaryName = null;
588 }
589
590 if(typeof editor.config.scayt_uiTabs === 'string' && editor.config.scayt_uiTabs.split(',').length === 3) {
591 var scayt_uiTabs = [], _tempUITabs = [];
592 editor.config.scayt_uiTabs = editor.config.scayt_uiTabs.split(',');
593
594 CKEDITOR.tools.search(editor.config.scayt_uiTabs, function(value) {
595 if (Number(value) === 1 || Number(value) === 0) {
596 _tempUITabs.push(true);
597 scayt_uiTabs.push(Number(value));
598 } else {
599 _tempUITabs.push(false);
600 }
601 });
602
603 if (CKEDITOR.tools.search(_tempUITabs, false) === null) {
604 editor.config.scayt_uiTabs = scayt_uiTabs;
605 } else {
606 editor.config.scayt_uiTabs = [1,1,1];
607 }
608
609 } else {
610 editor.config.scayt_uiTabs = [1,1,1];
611 }
612
613 if(typeof editor.config.scayt_serviceProtocol != 'string') {
614 editor.config.scayt_serviceProtocol = null;
615 }
616
617 if(typeof editor.config.scayt_serviceHost != 'string') {
618 editor.config.scayt_serviceHost = null;
619 }
620
621 if(typeof editor.config.scayt_servicePort != 'string') {
622 editor.config.scayt_servicePort = null;
623 }
624
625 if(typeof editor.config.scayt_servicePath != 'string') {
626 editor.config.scayt_servicePath = null;
627 }
628
629 if(!editor.config.scayt_moreSuggestions) {
630 editor.config.scayt_moreSuggestions = 'on';
631 }
632
633 if(typeof editor.config.scayt_customerId !== 'string') {
634 editor.config.scayt_customerId = '1:WvF0D4-UtPqN1-43nkD4-NKvUm2-daQqk3-LmNiI-z7Ysb4-mwry24-T8YrS3-Q2tpq2';
635 }
636
637 if(typeof editor.config.scayt_srcUrl !== 'string') {
638 var protocol = document.location.protocol;
639 protocol = protocol.search(/https?:/) != -1 ? protocol : 'http:';
640
641 editor.config.scayt_srcUrl = protocol + '//svc.webspellchecker.net/spellcheck31/lf/scayt3/ckscayt/ckscayt.js';
642 }
643
644 if(typeof CKEDITOR.config.scayt_handleCheckDirty !== 'boolean') {
645 CKEDITOR.config.scayt_handleCheckDirty = true;
646 }
647
648 if(typeof CKEDITOR.config.scayt_handleUndoRedo !== 'boolean') {
649 /* set default as 'true' */
650 CKEDITOR.config.scayt_handleUndoRedo = true;
651 }
652 /* checking 'undo' plugin, if no disable SCAYT handler */
653 CKEDITOR.config.scayt_handleUndoRedo = CKEDITOR.plugins.undo ? CKEDITOR.config.scayt_handleUndoRedo : false;
654
655 if(typeof editor.config.scayt_multiLanguageMode !== 'boolean') {
656 editor.config.scayt_multiLanguageMode = false;
657 }
658
659 if(typeof editor.config.scayt_multiLanguageStyles !== 'object') {
660 editor.config.scayt_multiLanguageStyles = {};
661 }
662
663 if(editor.config.scayt_ignoreAllCapsWords && typeof editor.config.scayt_ignoreAllCapsWords !== 'boolean') {
664 editor.config.scayt_ignoreAllCapsWords = false;
665 }
666
667 if(editor.config.scayt_ignoreDomainNames && typeof editor.config.scayt_ignoreDomainNames !== 'boolean') {
668 editor.config.scayt_ignoreDomainNames = false;
669 }
670
671 if(editor.config.scayt_ignoreWordsWithMixedCases && typeof editor.config.scayt_ignoreWordsWithMixedCases !== 'boolean') {
672 editor.config.scayt_ignoreWordsWithMixedCases = false;
673 }
674
675 if(editor.config.scayt_ignoreWordsWithNumbers && typeof editor.config.scayt_ignoreWordsWithNumbers !== 'boolean') {
676 editor.config.scayt_ignoreWordsWithNumbers = false;
677 }
678
679 if( editor.config.scayt_disableOptionsStorage ) {
680 var userOptions = CKEDITOR.tools.isArray( editor.config.scayt_disableOptionsStorage ) ? editor.config.scayt_disableOptionsStorage : ( typeof editor.config.scayt_disableOptionsStorage === 'string' ) ? [ editor.config.scayt_disableOptionsStorage ] : undefined,
681 availableValue = [ 'all', 'options', 'lang', 'ignore-all-caps-words', 'ignore-domain-names', 'ignore-words-with-mixed-cases', 'ignore-words-with-numbers'],
682 valuesOption = ['lang', 'ignore-all-caps-words', 'ignore-domain-names', 'ignore-words-with-mixed-cases', 'ignore-words-with-numbers'],
683 search = CKEDITOR.tools.search,
684 indexOf = CKEDITOR.tools.indexOf;
685
686 var isValidOption = function( option ) {
687 return !!search( availableValue, option );
688 };
689
690 var makeOptionsToStorage = function( options ) {
691 var retval = [];
692
693 for (var i = 0; i < options.length; i++) {
694 var value = options[i],
695 isGroupOptionInUserOptions = !!search( options, 'options' );
696
697 if( !isValidOption( value ) || isGroupOptionInUserOptions && !!search( valuesOption, function( val ) { if( val === 'lang' ) { return false; } } ) ) {
698 return;
699 }
700
701 if( !!search( valuesOption, value ) ) {
702 valuesOption.splice( indexOf( valuesOption, value ), 1 );
703 }
704
705 if( value === 'all' || isGroupOptionInUserOptions && !!search( options, 'lang' )) {
706 return [];
707 }
708
709 if( value === 'options' ) {
710 valuesOption = [ 'lang' ];
711 }
712 }
713
714 retval = retval.concat( valuesOption );
715
716 return retval;
717 };
718
719 editor.config.scayt_disableOptionsStorage = makeOptionsToStorage( userOptions );
720 }
721 },
722 addRule: function(editor) {
723 var plugin = CKEDITOR.plugins.scayt,
724 dataProcessor = editor.dataProcessor,
725 htmlFilter = dataProcessor && dataProcessor.htmlFilter,
726 pathFilters = editor._.elementsPath && editor._.elementsPath.filters,
727 dataFilter = dataProcessor && dataProcessor.dataFilter,
728 removeFormatFilter = editor.addRemoveFormatFilter,
729 pathFilter = function(element) {
730 var scaytInstance = editor.scayt;
731
732 if( scaytInstance && (element.hasAttribute(plugin.options.data_attribute_name) || element.hasAttribute(plugin.options.problem_grammar_data_attribute)) ) {
733 return false;
734 }
735 },
736 removeFormatFilterTemplate = function(element) {
737 var scaytInstance = editor.scayt,
738 result = true;
739
740 if( scaytInstance && (element.hasAttribute(plugin.options.data_attribute_name) || element.hasAttribute(plugin.options.problem_grammar_data_attribute)) ) {
741 result = false;
742 }
743
744 return result;
745 };
746
747 if(pathFilters) {
748 pathFilters.push(pathFilter);
749 }
750
751 if(dataFilter) {
752 var dataFilterRules = {
753 elements: {
754 span: function(element) {
755
756 var scaytState = element.hasClass(plugin.options.misspelled_word_class) && element.attributes[plugin.options.data_attribute_name],
757 graytState = element.hasClass(plugin.options.problem_grammar_class) && element.attributes[plugin.options.problem_grammar_data_attribute];
758
759 if(plugin && (scaytState || graytState)) {
760 delete element.name;
761 }
762
763 return element;
764 }
765 }
766 };
767
768 dataFilter.addRules(dataFilterRules);
769 }
770
771 if (htmlFilter) {
772 var htmlFilterRules = {
773 elements: {
774 span: function(element) {
775
776 var scaytState = element.hasClass(plugin.options.misspelled_word_class) && element.attributes[plugin.options.data_attribute_name],
777 graytState = element.hasClass(plugin.options.problem_grammar_class) && element.attributes[plugin.options.problem_grammar_data_attribute];
778
779 if(plugin && (scaytState || graytState)) {
780 delete element.name;
781 }
782
783 return element;
784 }
785 }
786 };
787
788 htmlFilter.addRules(htmlFilterRules);
789 }
790
791 if(removeFormatFilter) {
792 removeFormatFilter.call(editor, removeFormatFilterTemplate);
793 }
794 },
795 scaytMenuDefinition: function(editor) {
796 var self = this,
797 plugin = CKEDITOR.plugins.scayt,
798 scayt_instance = editor.scayt;
799
800 return {
801 scayt: {
802 scayt_ignore: {
803 label: scayt_instance.getLocal('btn_ignore'),
804 group : 'scayt_control',
805 order : 1,
806 exec: function(editor) {
807 var scaytInstance = editor.scayt;
808 scaytInstance.ignoreWord();
809 }
810 },
811 scayt_ignoreall: {
812 label : scayt_instance.getLocal('btn_ignoreAll'),
813 group : 'scayt_control',
814 order : 2,
815 exec: function(editor) {
816 var scaytInstance = editor.scayt;
817 scaytInstance.ignoreAllWords();
818 }
819 },
820 scayt_add: {
821 label : scayt_instance.getLocal('btn_addWord'),
822 group : 'scayt_control',
823 order : 3,
824 exec : function(editor) {
825 var scaytInstance = editor.scayt;
826
827 // @TODO: We need to add set/restore bookmark logic to 'addWordToUserDictionary' method inside dictionarymanager.
828 // Timeout is used as tmp fix for IE9, when after hitting 'Add word' menu item, document container was blurred.
829 setTimeout(function() {
830 scaytInstance.addWordToUserDictionary();
831 }, 10);
832 }
833 },
834 scayt_option: {
835 label : scayt_instance.getLocal('btn_options'),
836 group : 'scayt_control',
837 order : 4,
838 exec: function(editor) {
839 var scaytInstance = editor.scayt;
840
841 scaytInstance.tabToOpen = 'options';
842 editor.lockSelection();
843 editor.openDialog(self.dialogName);
844 },
845 verification: function(editor) {
846 return (editor.config.scayt_uiTabs[0] == 1) ? true : false;
847 }
848 },
849 scayt_language: {
850 label : scayt_instance.getLocal('btn_langs'),
851 group : 'scayt_control',
852 order : 5,
853 exec: function(editor) {
854 var scaytInstance = editor.scayt;
855
856 scaytInstance.tabToOpen = 'langs';
857 editor.lockSelection();
858 editor.openDialog(self.dialogName);
859 },
860 verification: function(editor) {
861 return (editor.config.scayt_uiTabs[1] == 1) ? true : false;
862 }
863 },
864 scayt_dictionary: {
865 label : scayt_instance.getLocal('btn_dictionaries'),
866 group : 'scayt_control',
867 order : 6,
868 exec: function(editor) {
869 var scaytInstance = editor.scayt;
870
871 scaytInstance.tabToOpen = 'dictionaries';
872 editor.lockSelection();
873 editor.openDialog(self.dialogName);
874 },
875 verification: function(editor) {
876 return (editor.config.scayt_uiTabs[2] == 1) ? true : false;
877 }
878 },
879 scayt_about: {
880 label : scayt_instance.getLocal('btn_about'),
881 group : 'scayt_control',
882 order : 7,
883 exec: function(editor) {
884 var scaytInstance = editor.scayt;
885
886 scaytInstance.tabToOpen = 'about';
887 editor.lockSelection();
888 editor.openDialog(self.dialogName);
889 }
890 }
891 },
892 grayt: {
893 grayt_problemdescription: {
894 label : 'Grammar problem description',
895 group : 'grayt_description', // look at addMenuItems method for further info
896 order : 1,
897 state : CKEDITOR.TRISTATE_DISABLED,
898 exec: function(editor) {}
899 },
900 grayt_ignore: {
901 label : scayt_instance.getLocal('btn_ignore'),
902 group : 'grayt_control',
903 order : 2,
904 exec: function(editor) {
905 var scaytInstance = editor.scayt;
906
907 scaytInstance.ignorePhrase();
908 }
909 }
910 }
911 };
912 },
913 buildSuggestionMenuItems: function(editor, suggestions, isScaytNode) {
914 var self = this,
915 itemList = {},
916 subItemList = {},
917 replaceKeyName = isScaytNode ? 'word' : 'phrase',
918 updateEventName = isScaytNode ? 'startGrammarCheck' : 'startSpellCheck',
919 plugin = CKEDITOR.plugins.scayt,
920 scayt_instance = editor.scayt;
921
922 if(suggestions.length > 0 && suggestions[0] !== 'no_any_suggestions') {
923
924 if(isScaytNode) {
925 // build SCAYT suggestions
926 for(var i = 0; i < suggestions.length; i++) {
927
928 var commandName = 'scayt_suggest_' + CKEDITOR.plugins.scayt.suggestions[i].replace(' ', '_');
929
930 editor.addCommand(commandName, self.createCommand(CKEDITOR.plugins.scayt.suggestions[i], replaceKeyName, updateEventName));
931
932 if(i < editor.config.scayt_maxSuggestions) {
933
934 // mainSuggestions
935 editor.addMenuItem(commandName, {
936 label: suggestions[i],
937 command: commandName,
938 group: 'scayt_suggest',
939 order: i + 1
940 });
941
942 itemList[commandName] = CKEDITOR.TRISTATE_OFF;
943
944 } else {
945
946 // moreSuggestions
947 editor.addMenuItem(commandName, {
948 label: suggestions[i],
949 command: commandName,
950 group: 'scayt_moresuggest',
951 order: i + 1
952 });
953
954 subItemList[commandName] = CKEDITOR.TRISTATE_OFF;
955
956 if(editor.config.scayt_moreSuggestions === 'on') {
957
958 editor.addMenuItem('scayt_moresuggest', {
959 label : scayt_instance.getLocal('btn_moreSuggestions'),
960 group : 'scayt_moresuggest',
961 order : 10,
962 getItems : function() {
963 return subItemList;
964 }
965 });
966
967 itemList['scayt_moresuggest'] = CKEDITOR.TRISTATE_OFF;
968 }
969 }
970 }
971 } else {
972 // build GRAYT suggestions
973 for(var i = 0; i < suggestions.length; i++) {
974 var commandName = 'grayt_suggest_' + CKEDITOR.plugins.scayt.suggestions[i].replace(' ', '_');
975
976 editor.addCommand(commandName, self.createCommand(CKEDITOR.plugins.scayt.suggestions[i], replaceKeyName, updateEventName));
977
978 // mainSuggestions
979 editor.addMenuItem(commandName, {
980 label: suggestions[i],
981 command: commandName,
982 group: 'grayt_suggest',
983 order: i + 1
984 });
985
986 itemList[commandName] = CKEDITOR.TRISTATE_OFF;
987 }
988 }
989 } else {
990 var noSuggestionsCommand = 'no_scayt_suggest';
991 itemList[noSuggestionsCommand] = CKEDITOR.TRISTATE_DISABLED;
992
993 editor.addCommand(noSuggestionsCommand, {
994 exec: function() {
995
996 }
997 });
998
999 editor.addMenuItem(noSuggestionsCommand, {
1000 label : scayt_instance.getLocal('btn_noSuggestions') || noSuggestionsCommand,
1001 command: noSuggestionsCommand,
1002 group : 'scayt_suggest',
1003 order : 0
1004 });
1005 }
1006
1007 return itemList;
1008 },
1009 menuGenerator: function(editor, selectionNode) {
1010 var self = this,
1011 scaytInstance = editor.scayt,
1012 menuItems = this.scaytMenuDefinition(editor),
1013 itemList = {},
1014 allowedOption = editor.config.scayt_contextCommands.split('|'),
1015 lang = selectionNode.getAttribute(scaytInstance.getLangAttribute()) || scaytInstance.getLang(),
1016 word, grammarPhrase, isScaytNode, isGrammarNode, problemDescriptionText;
1017
1018
1019 isScaytNode = scaytInstance.isScaytNode(selectionNode);
1020 isGrammarNode = scaytInstance.isGraytNode(selectionNode);
1021
1022 if(isScaytNode) {
1023 // we clicked scayt misspelling
1024 // get suggestions
1025 menuItems = menuItems.scayt;
1026
1027 word = selectionNode.getAttribute(scaytInstance.getScaytNodeAttributeName());
1028
1029 scaytInstance.fire('getSuggestionsList', {
1030 lang: lang,
1031 word: word
1032 });
1033
1034 itemList = this.buildSuggestionMenuItems(editor, CKEDITOR.plugins.scayt.suggestions, isScaytNode);
1035 } else if(isGrammarNode) {
1036 // we clicked grammar problem
1037 // get suggestions
1038 menuItems = menuItems.grayt;
1039 grammarPhrase = selectionNode.getAttribute(scaytInstance.getGraytNodeAttributeName());
1040
1041 // setup grammar problem description
1042 problemDescriptionText = scaytInstance.getProblemDescriptionText(grammarPhrase, lang);
1043 if(menuItems.grayt_problemdescription && problemDescriptionText) {
1044 menuItems.grayt_problemdescription.label = problemDescriptionText;
1045 }
1046
1047 scaytInstance.fire('getGrammarSuggestionsList', {
1048 lang: lang,
1049 phrase: grammarPhrase
1050 });
1051
1052 itemList = this.buildSuggestionMenuItems(editor, CKEDITOR.plugins.scayt.suggestions, isScaytNode);
1053 }
1054
1055 if(isScaytNode && editor.config.scayt_contextCommands == 'off') {
1056 return itemList;
1057 }
1058
1059 for(var key in menuItems) {
1060 if(isScaytNode && CKEDITOR.tools.indexOf(allowedOption, key.replace('scayt_', '')) == -1 && editor.config.scayt_contextCommands != 'all') {
1061 continue;
1062 }
1063
1064 if(typeof menuItems[key].state != 'undefined') {
1065 itemList[key] = menuItems[key].state;
1066 } else {
1067 itemList[key] = CKEDITOR.TRISTATE_OFF;
1068 }
1069
1070 // delete item from context menu if its state isn't verified as allowed
1071 if(typeof menuItems[key].verification === 'function' && !menuItems[key].verification(editor)) {
1072 // itemList[key] = (menuItems[key].verification(editor)) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED;
1073 delete itemList[key];
1074 }
1075
1076 editor.addCommand(key, {
1077 exec: menuItems[key].exec
1078 });
1079
1080 editor.addMenuItem(key, {
1081 label : editor.lang.scayt[menuItems[key].label] || menuItems[key].label,
1082 command: key,
1083 group : menuItems[key].group,
1084 order : menuItems[key].order
1085 });
1086 }
1087
1088 return itemList;
1089 },
1090 createCommand: function(suggestion, replaceKeyName, updateEventName) {
1091 return {
1092 exec: function(editor) {
1093 var scaytInstance = editor.scayt,
1094 eventObject = {};
1095
1096 eventObject[replaceKeyName] = suggestion;
1097 scaytInstance.replaceSelectionNode(eventObject);
1098
1099 // we need to remove grammar markup from selection node if we just performed replace action for misspelling
1100 if(updateEventName === 'startGrammarCheck') {
1101 scaytInstance.removeMarkupInSelectionNode({grammarOnly: true});
1102 }
1103 // for grayt problem replacement we need to fire 'startSpellCheck'
1104 // for scayt misspelling replacement we need to fire 'startGrammarCheck'
1105 scaytInstance.fire(updateEventName);
1106 }
1107 };
1108 }
1109 });
1110
1111 CKEDITOR.plugins.scayt = {
1112 state: {
1113 scayt: {},
1114 grayt: {}
1115 },
1116 suggestions: [],
1117 loadingHelper: {
1118 loadOrder: []
1119 },
1120 isLoading: false,
1121 options: {
1122 disablingCommandExec: {
1123 source: true,
1124 newpage: true,
1125 templates: true
1126 },
1127 data_attribute_name: 'data-scayt-word',
1128 misspelled_word_class: 'scayt-misspell-word',
1129 problem_grammar_data_attribute: 'data-grayt-phrase',
1130 problem_grammar_class: 'gramm-problem'
1131 },
1132 backCompatibilityMap: {
1133 'scayt_service_protocol': 'scayt_serviceProtocol',
1134 'scayt_service_host' : 'scayt_serviceHost',
1135 'scayt_service_port' : 'scayt_servicePort',
1136 'scayt_service_path' : 'scayt_servicePath',
1137 'scayt_customerid' : 'scayt_customerId'
1138 },
1139 replaceOldOptionsNames: function(config) {
1140 for(var key in config) {
1141 if(key in this.backCompatibilityMap) {
1142 config[this.backCompatibilityMap[key]] = config[key];
1143 delete config[key];
1144 }
1145 }
1146 },
1147 createScayt : function(editor) {
1148 var self = this,
1149 plugin = CKEDITOR.plugins.scayt;
1150
1151 this.loadScaytLibrary(editor, function(_editor) {
1152 var textContainer = _editor.window && _editor.window.getFrame() || _editor.editable();
1153
1154 // Do not create SCAYT if there is no text container for usage
1155 if(!textContainer) {
1156 plugin.state.scayt[_editor.name] = false;
1157 return;
1158 }
1159
1160 var scaytInstanceOptions = {
1161 lang : _editor.config.scayt_sLang,
1162 container : textContainer.$,
1163 customDictionary : _editor.config.scayt_customDictionaryIds,
1164 userDictionaryName : _editor.config.scayt_userDictionaryName,
1165 localization : _editor.langCode,
1166 customer_id : _editor.config.scayt_customerId,
1167 debug : _editor.config.scayt_debug,
1168 data_attribute_name : self.options.data_attribute_name,
1169 misspelled_word_class: self.options.misspelled_word_class,
1170 problem_grammar_data_attribute: self.options.problem_grammar_data_attribute,
1171 problem_grammar_class: self.options.problem_grammar_class,
1172 'options-to-restore': _editor.config.scayt_disableOptionsStorage,
1173 focused : _editor.editable().hasFocus, // #30260 we need to set focused=true if CKEditor is focused before SCAYT initialization
1174 ignoreElementsRegex : _editor.config.scayt_elementsToIgnore,
1175 minWordLength : _editor.config.scayt_minWordLength,
1176 multiLanguageMode : _editor.config.scayt_multiLanguageMode,
1177 multiLanguageStyles : _editor.config.scayt_multiLanguageStyles,
1178 graytAutoStartup : plugin.state.grayt[_editor.name]
1179 };
1180
1181 if(_editor.config.scayt_serviceProtocol) {
1182 scaytInstanceOptions['service_protocol'] = _editor.config.scayt_serviceProtocol;
1183 }
1184
1185 if(_editor.config.scayt_serviceHost) {
1186 scaytInstanceOptions['service_host'] = _editor.config.scayt_serviceHost;
1187 }
1188
1189 if(_editor.config.scayt_servicePort) {
1190 scaytInstanceOptions['service_port'] = _editor.config.scayt_servicePort;
1191 }
1192
1193 if(_editor.config.scayt_servicePath) {
1194 scaytInstanceOptions['service_path'] = _editor.config.scayt_servicePath;
1195 }
1196
1197 //predefined options
1198 if(typeof _editor.config.scayt_ignoreAllCapsWords === 'boolean') {
1199 scaytInstanceOptions['ignore-all-caps-words'] = _editor.config.scayt_ignoreAllCapsWords;
1200 }
1201
1202 if(typeof _editor.config.scayt_ignoreDomainNames === 'boolean') {
1203 scaytInstanceOptions['ignore-domain-names'] = _editor.config.scayt_ignoreDomainNames;
1204 }
1205
1206 if(typeof _editor.config.scayt_ignoreWordsWithMixedCases === 'boolean') {
1207 scaytInstanceOptions['ignore-words-with-mixed-cases'] = _editor.config.scayt_ignoreWordsWithMixedCases;
1208 }
1209
1210 if(typeof _editor.config.scayt_ignoreWordsWithNumbers === 'boolean') {
1211 scaytInstanceOptions['ignore-words-with-numbers'] = _editor.config.scayt_ignoreWordsWithNumbers;
1212 }
1213
1214 var scaytInstance = new SCAYT.CKSCAYT(scaytInstanceOptions, function() {
1215 // success callback
1216 }, function() {
1217 // error callback
1218 }),
1219 wordsPrefix = 'word_';
1220
1221 scaytInstance.subscribe('suggestionListSend', function(data) {
1222 // TODO: 1. Maybe store suggestions for specific editor
1223 // TODO: 2. Fix issue with suggestion duplicates on on server
1224 //CKEDITOR.plugins.scayt.suggestions = data.suggestionList;
1225 var _wordsCollection = {},
1226 _suggestionList =[];
1227
1228 for (var i = 0; i < data.suggestionList.length; i++) {
1229 if (!_wordsCollection[wordsPrefix + data.suggestionList[i]]) {
1230 _wordsCollection[wordsPrefix + data.suggestionList[i]] = data.suggestionList[i];
1231 _suggestionList.push(data.suggestionList[i]);
1232 }
1233 }
1234
1235 CKEDITOR.plugins.scayt.suggestions = _suggestionList;
1236 });
1237
1238 // if selection has changed programmatically by SCAYT we need to react appropriately
1239 scaytInstance.subscribe('selectionIsChanged', function(data) {
1240 var selection = _editor.getSelection();
1241
1242 if(selection.isLocked) {
1243 _editor.lockSelection();
1244 }
1245 });
1246
1247 scaytInstance.subscribe('graytStateChanged', function(data) {
1248 plugin.state.grayt[_editor.name] = data.state;
1249 });
1250
1251 _editor.scayt = scaytInstance;
1252
1253 _editor.fire('scaytButtonState', _editor.readOnly ? CKEDITOR.TRISTATE_DISABLED : CKEDITOR.TRISTATE_ON);
1254 });
1255 },
1256 destroy: function(editor) {
1257 if(editor.scayt) {
1258 editor.scayt.destroy();
1259 }
1260
1261 delete editor.scayt;
1262 editor.fire('scaytButtonState', CKEDITOR.TRISTATE_OFF);
1263 },
1264 loadScaytLibrary: function(editor, callback) {
1265 var self = this,
1266 date,
1267 timestamp,
1268 scaytUrl;
1269
1270 // no need to process load requests from same editor as it can cause bugs with
1271 // loading ckscayt app due to subsequent calls of some events
1272 // need to be before 'if' statement, because of timing issue in CKEDITOR.scriptLoader
1273 // when callback executing is delayed for a few milliseconds, and scayt can be created twise
1274 // on one instance
1275 if(this.loadingHelper[editor.name]) return;
1276
1277 if(typeof window.SCAYT === 'undefined' || typeof window.SCAYT.CKSCAYT !== 'function') {
1278
1279 // add onLoad callbacks for editors while SCAYT is loading
1280 this.loadingHelper[editor.name] = callback;
1281 this.loadingHelper.loadOrder.push(editor.name);
1282
1283 //creating unique timestamp for SCAYT URL
1284 date = new Date();
1285 timestamp = date.getTime();
1286 scaytUrl = editor.config.scayt_srcUrl;
1287
1288 //if there already implemented timstamp for scayr_srcURL use it, if not use our timestamp
1289 scaytUrl = scaytUrl + (scaytUrl.indexOf('?') >= 0 ? '' : '?' + timestamp);
1290
1291 if (!this.loadingHelper.ckscaytLoading) {
1292 CKEDITOR.scriptLoader.load(scaytUrl, function(success) {
1293 var editorName;
1294
1295 if ( success ) {
1296 CKEDITOR.fireOnce('scaytReady');
1297
1298 for(var i = 0; i < self.loadingHelper.loadOrder.length; i++) {
1299 editorName = self.loadingHelper.loadOrder[i];
1300
1301 if(typeof self.loadingHelper[editorName] === 'function') {
1302 self.loadingHelper[editorName](CKEDITOR.instances[editorName]);
1303 }
1304
1305 delete self.loadingHelper[editorName];
1306 }
1307 self.loadingHelper.loadOrder = [];
1308 }
1309 });
1310 this.loadingHelper.ckscaytLoading = true;
1311 }
1312
1313
1314 } else if(window.SCAYT && typeof window.SCAYT.CKSCAYT === 'function') {
1315 CKEDITOR.fireOnce('scaytReady');
1316
1317 if(!editor.scayt) {
1318 if(typeof callback === 'function') {
1319 callback(editor);
1320 }
1321 }
1322 }
1323 }
1324 };
1325
1326 CKEDITOR.on('dialogDefinition', function(dialogDefinitionEvent) {
1327 var dialogName = dialogDefinitionEvent.data.name,
1328 dialogDefinition = dialogDefinitionEvent.data.definition,
1329 dialog = dialogDefinition.dialog;
1330
1331 if (dialogName === 'scaytDialog') {
1332 dialog.on('cancel', function(cancelEvent) {
1333 return false;
1334 }, this, null, -1);
1335 }
1336
1337 if ( dialogName === 'checkspell' ) {
1338 dialog.on( 'cancel', function( cancelEvent ) {
1339 var editor = cancelEvent.sender && cancelEvent.sender.getParentEditor(),
1340 plugin = CKEDITOR.plugins.scayt,
1341 scaytInstance = editor.scayt;
1342
1343 if ( scaytInstance && plugin.state.scayt[ editor.name ] && scaytInstance.setMarkupPaused ) {
1344 scaytInstance.setMarkupPaused( false );
1345 }
1346
1347 editor.unlockSelection();
1348 }, this, null, -2 ); // we need to call cancel callback before WSC plugin
1349 }
1350
1351 if (dialogName === 'link') {
1352 dialog.on('ok', function(okEvent) {
1353 var editor = okEvent.sender && okEvent.sender.getParentEditor();
1354
1355 if(editor) {
1356 setTimeout(function() {
1357 editor.fire('reloadMarkupScayt', {
1358 removeOptions: {
1359 removeInside: true,
1360 forceBookmark: true
1361 },
1362 timeout: 0
1363 });
1364 }, 0);
1365 }
1366 });
1367 }
1368 });
1369
1370 CKEDITOR.on('scaytReady', function() {
1371
1372 // Override editor.checkDirty method avoid CK checkDirty functionality to fix SCAYT issues with incorrect checkDirty behavior.
1373 if(CKEDITOR.config.scayt_handleCheckDirty === true) {
1374 var editorCheckDirty = CKEDITOR.editor.prototype;
1375
1376 editorCheckDirty.checkDirty = CKEDITOR.tools.override(editorCheckDirty.checkDirty, function(org) {
1377
1378 return function() {
1379 var retval = null,
1380 pluginStatus = CKEDITOR.plugins.scayt && CKEDITOR.plugins.scayt.state.scayt[this.name] && this.scayt,
1381 scaytInstance = this.scayt;
1382
1383 if(!pluginStatus) {
1384 retval = org.call(this);
1385 } else {
1386 retval = (this.status == 'ready');
1387
1388 if (retval) {
1389 var currentData = scaytInstance.removeMarkupFromString(this.getSnapshot()),
1390 prevData = scaytInstance.removeMarkupFromString(this._.previousValue);
1391
1392 retval = (retval && (prevData !== currentData))
1393 }
1394 }
1395
1396 return retval;
1397 };
1398 });
1399
1400 editorCheckDirty.resetDirty = CKEDITOR.tools.override(editorCheckDirty.resetDirty, function(org) {
1401 return function() {
1402 var pluginStatus = CKEDITOR.plugins.scayt && CKEDITOR.plugins.scayt.state.scayt[this.name] && this.scayt,
1403 scaytInstance = this.scayt;//CKEDITOR.plugins.scayt.getScayt(this);
1404
1405 if(!pluginStatus) {
1406 org.call(this);
1407 } else {
1408 this._.previousValue = scaytInstance.removeMarkupFromString(this.getSnapshot());
1409 }
1410 };
1411 });
1412 }
1413
1414 if (CKEDITOR.config.scayt_handleUndoRedo === true) {
1415 var undoImagePrototype = CKEDITOR.plugins.undo.Image.prototype;
1416
1417 // add backword compatibility for CKEDITOR 4.2. method equals was repleced on other method
1418 var equalsContentMethodName = (typeof undoImagePrototype.equalsContent == "function") ? 'equalsContent' : 'equals';
1419
1420 undoImagePrototype[equalsContentMethodName] = CKEDITOR.tools.override(undoImagePrototype[equalsContentMethodName], function(org) {
1421 return function(otherImage) {
1422 var pluginState = CKEDITOR.plugins.scayt && CKEDITOR.plugins.scayt.state.scayt[otherImage.editor.name] && otherImage.editor.scayt,
1423 scaytInstance = otherImage.editor.scayt,
1424 thisContents = this.contents,
1425 otherContents = otherImage.contents,
1426 retval = null;
1427
1428 // Making the comparison based on content without SCAYT word markers.
1429 if(pluginState) {
1430 this.contents = scaytInstance.removeMarkupFromString(thisContents) || '';
1431 otherImage.contents = scaytInstance.removeMarkupFromString(otherContents) || '';
1432 }
1433
1434 var retval = org.apply(this, arguments);
1435
1436 this.contents = thisContents;
1437 otherImage.contents = otherContents;
1438
1439 return retval;
1440 };
1441 });
1442 }
1443 });
1444
1445 /**
1446 * Automatically enables SCAYT on editor startup. When set to `true`, this option turns on SCAYT automatically
1447 * after loading the editor.
1448 *
1449 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1450 *
1451 * config.scayt_autoStartup = true;
1452 *
1453 * @cfg {Boolean} [scayt_autoStartup=false]
1454 * @member CKEDITOR.config
1455 */
1456
1457 /**
1458 * Enables Grammar As You Type (GRAYT) on SCAYT startup. When set to `true`, this option turns on GRAYT automatically
1459 * after SCAYT started.
1460 *
1461 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1462 *
1463 * config.grayt_autoStartup = true;
1464 *
1465 * @since 4.5.6
1466 * @cfg {Boolean} [grayt_autoStartup=false]
1467 * @member CKEDITOR.config
1468 */
1469
1470 /**
1471 * Enables SCAYT initialization when inline CKEditor is not focused. When set to `true`, SCAYT markup is
1472 * displayed in both inline editor states, focused and unfocused, so the SCAYT instance is not destroyed.
1473 *
1474 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1475 *
1476 * config.scayt_inlineModeImmediateMarkup = true;
1477 *
1478 * @since 4.5.6
1479 * @cfg {Boolean} [scayt_inlineModeImmediateMarkup=false]
1480 * @member CKEDITOR.config
1481 */
1482
1483 /**
1484 * Defines the number of SCAYT suggestions to show in the main context menu.
1485 * Possible values are:
1486 *
1487 * * `0` (zero) &ndash; No suggestions are shown in the main context menu. All
1488 * entries will be listed in the "More Suggestions" sub-menu.
1489 * * Positive number &ndash; The maximum number of suggestions to show in the context
1490 * menu. Other entries will be shown in the "More Suggestions" sub-menu.
1491 * * Negative number &ndash; Five suggestions are shown in the main context menu. All other
1492 * entries will be listed in the "More Suggestions" sub-menu.
1493 *
1494 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1495 *
1496 * Examples:
1497 *
1498 * // Display only three suggestions in the main context menu.
1499 * config.scayt_maxSuggestions = 3;
1500 *
1501 * // Do not show the suggestions directly.
1502 * config.scayt_maxSuggestions = 0;
1503 *
1504 * @cfg {Number} [scayt_maxSuggestions=5]
1505 * @member CKEDITOR.config
1506 */
1507
1508 /**
1509 * Defines the minimum length of words that will be collected from the editor content for spell checking.
1510 * Possible value is any positive number.
1511 *
1512 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1513 *
1514 * Examples:
1515 *
1516 * // Set the minimum length of words that will be collected from editor text.
1517 * config.scayt_minWordLength = 5;
1518 *
1519 * @cfg {Number} [scayt_minWordLength=4]
1520 * @member CKEDITOR.config
1521 */
1522
1523 /**
1524 * Sets the customer ID for SCAYT. Used for hosted users only. Required for migration from free
1525 * to trial or paid versions.
1526 *
1527 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1528 *
1529 * // Load SCAYT using my customer ID.
1530 * config.scayt_customerId = 'your-encrypted-customer-id';
1531 *
1532 * @cfg {String} [scayt_customerId='1:WvF0D4-UtPqN1-43nkD4-NKvUm2-daQqk3-LmNiI-z7Ysb4-mwry24-T8YrS3-Q2tpq2']
1533 * @member CKEDITOR.config
1534 */
1535
1536 /**
1537 * Enables and disables the "More Suggestions" sub-menu in the context menu.
1538 * Possible values are `'on'` and `'off'`.
1539 *
1540 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1541 *
1542 * // Disables the "More Suggestions" sub-menu.
1543 * config.scayt_moreSuggestions = 'off';
1544 *
1545 * @cfg {String} [scayt_moreSuggestions='on']
1546 * @member CKEDITOR.config
1547 */
1548
1549 /**
1550 * Customizes the display of SCAYT context menu commands ("Add Word", "Ignore",
1551 * "Ignore All", "Options", "Languages", "Dictionaries" and "About").
1552 * This must be a string with one or more of the following
1553 * words separated by a pipe character (`'|'`):
1554 *
1555 * * `off` &ndash; Disables all options.
1556 * * `all` &ndash; Enables all options.
1557 * * `ignore` &ndash; Enables the "Ignore" option.
1558 * * `ignoreall` &ndash; Enables the "Ignore All" option.
1559 * * `add` &ndash; Enables the "Add Word" option.
1560 * * `option` &ndash; Enables the "Options" menu item.
1561 * * `language` &ndash; Enables the "Languages" menu item.
1562 * * `dictionary` &ndash; Enables the "Dictionaries" menu item.
1563 * * `about` &ndash; Enables the "About" menu item.
1564 *
1565 * Please note that availability of the "Options", "Languages" and "Dictionaries" items
1566 * also depends on the {@link CKEDITOR.config#scayt_uiTabs} option.
1567 *
1568 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1569 *
1570 * Example:
1571 *
1572 * // Show only "Add Word" and "Ignore All" in the context menu.
1573 * config.scayt_contextCommands = 'add|ignoreall';
1574 *
1575 * @cfg {String} [scayt_contextCommands='ignore|ignoreall|add']
1576 * @member CKEDITOR.config
1577 */
1578
1579 /**
1580 * Sets the default spell checking language for SCAYT. Possible values are:
1581 * `'en_US'`, `'en_GB'`, `'pt_BR'`, `'da_DK'`,
1582 * `'nl_NL'`, `'en_CA'`, `'fi_FI'`, `'fr_FR'`,
1583 * `'fr_CA'`, `'de_DE'`, `'el_GR'`, `'it_IT'`,
1584 * `'nb_NO'`, `'pt_PT'`, `'es_ES'`, `'sv_SE'`.
1585 *
1586 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1587 *
1588 * // Sets SCAYT to German.
1589 * config.scayt_sLang = 'de_DE';
1590 *
1591 * @cfg {String} [scayt_sLang='en_US']
1592 * @member CKEDITOR.config
1593 */
1594
1595 /**
1596 * Customizes the SCAYT dialog and SCAYT toolbar menu to show particular tabs and items.
1597 * This setting must contain a `1` (enabled) or `0`
1598 * (disabled) value for each of the following entries, in this precise order,
1599 * separated by a comma (`','`): `'Options'`, `'Languages'`, and `'Dictionary'`.
1600 *
1601 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1602 *
1603 * // Hides the "Languages" tab.
1604 * config.scayt_uiTabs = '1,0,1';
1605 *
1606 * @cfg {String} [scayt_uiTabs='1,1,1']
1607 * @member CKEDITOR.config
1608 */
1609
1610 /**
1611 * Sets the protocol for the WebSpellChecker service (`ssrv.cgi`) full path.
1612 *
1613 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1614 *
1615 * // Defines the protocol for the WebSpellChecker service (ssrv.cgi) path.
1616 * config.scayt_serviceProtocol = 'https';
1617 *
1618 * @cfg {String} [scayt_serviceProtocol='http']
1619 * @member CKEDITOR.config
1620 */
1621
1622 /**
1623 * Sets the host for the WebSpellChecker service (`ssrv.cgi`) full path.
1624 *
1625 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1626 *
1627 * // Defines the host for the WebSpellChecker service (ssrv.cgi) path.
1628 * config.scayt_serviceHost = 'my-host';
1629 *
1630 * @cfg {String} [scayt_serviceHost='svc.webspellchecker.net']
1631 * @member CKEDITOR.config
1632 */
1633
1634 /**
1635 * Sets the port for the WebSpellChecker service (`ssrv.cgi`) full path.
1636 *
1637 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1638 *
1639 * // Defines the port for the WebSpellChecker service (ssrv.cgi) path.
1640 * config.scayt_servicePort = '2330';
1641 *
1642 * @cfg {String} [scayt_servicePort='80']
1643 * @member CKEDITOR.config
1644 */
1645
1646 /**
1647 * Sets the path to the WebSpellChecker service (`ssrv.cgi`).
1648 *
1649 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1650 *
1651 * // Defines the path to the WebSpellChecker service (ssrv.cgi).
1652 * config.scayt_servicePath = 'my-path/ssrv.cgi';
1653 *
1654 * @cfg {String} [scayt_servicePath='spellcheck31/script/ssrv.cgi']
1655 * @member CKEDITOR.config
1656 */
1657
1658 /**
1659 * Sets the URL to SCAYT core. Required to switch to the licensed version of SCAYT.
1660 *
1661 * Refer to [SCAYT documentation](http://wiki.webspellchecker.net/doku.php?id=migration:hosredfreetolicensedck)
1662 * for more details.
1663 *
1664 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1665 *
1666 * config.scayt_srcUrl = "http://my-host/spellcheck/lf/scayt/scayt.js";
1667 *
1668 * @cfg {String} [scayt_srcUrl='//svc.webspellchecker.net/spellcheck31/lf/scayt3/ckscayt/ckscayt.js']
1669 * @member CKEDITOR.config
1670 */
1671
1672 /**
1673 * Links SCAYT to custom dictionaries. This is a string containing the dictionary IDs
1674 * separated by commas (`','`). Available only for the licensed version.
1675 *
1676 * Refer to [SCAYT documentation](http://wiki.webspellchecker.net/doku.php?id=installationandconfiguration:customdictionaries:licensed)
1677 * for more details.
1678 *
1679 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1680 *
1681 * config.scayt_customDictionaryIds = '3021,3456,3478';
1682 *
1683 * @cfg {String} [scayt_customDictionaryIds='']
1684 * @member CKEDITOR.config
1685 */
1686
1687 /**
1688 * Activates a User Dictionary in SCAYT. The user
1689 * dictionary name must be used. Available only for the licensed version.
1690 *
1691 * Refer to [SCAYT documentation](http://wiki.webspellchecker.net/doku.php?id=installationandconfiguration:userdictionaries)
1692 * for more details.
1693 *
1694 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1695 *
1696 * config.scayt_userDictionaryName = 'MyDictionary';
1697 *
1698 * @cfg {String} [scayt_userDictionaryName='']
1699 * @member CKEDITOR.config
1700 */
1701
1702 /**
1703 * Defines the order of SCAYT context menu items by groups.
1704 * This must be a string with one or more of the following
1705 * words separated by a pipe character (`'|'`):
1706 *
1707 * * `suggest` &ndash; The main suggestion word list.
1708 * * `moresuggest` &ndash; The "More suggestions" word list.
1709 * * `control` &ndash; SCAYT commands, such as "Ignore" and "Add Word".
1710 *
1711 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1712 *
1713 * Example:
1714 *
1715 * config.scayt_contextMenuItemsOrder = 'moresuggest|control|suggest';
1716 *
1717 * @cfg {String} [scayt_contextMenuItemsOrder='suggest|moresuggest|control']
1718 * @member CKEDITOR.config
1719 */
1720
1721 /**
1722 * If set to `true`, it overrides the {@link CKEDITOR.editor#checkDirty checkDirty} functionality of CKEditor
1723 * to fix SCAYT issues with incorrect `checkDirty` behavior. If set to `false`,
1724 * it provides better performance on big preloaded text.
1725 *
1726 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1727 *
1728 * config.scayt_handleCheckDirty = 'false';
1729 *
1730 * @cfg {String} [scayt_handleCheckDirty='true']
1731 * @member CKEDITOR.config
1732 */
1733
1734 /**
1735 * Configures undo/redo behavior of SCAYT in CKEditor.
1736 * If set to `true`, it overrides the undo/redo functionality of CKEditor
1737 * to fix SCAYT issues with incorrect undo/redo behavior. If set to `false`,
1738 * it provides better performance on text undo/redo.
1739 *
1740 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1741 *
1742 * config.scayt_handleUndoRedo = 'false';
1743 *
1744 * @cfg {String} [scayt_handleUndoRedo='true']
1745 * @member CKEDITOR.config
1746 */
1747
1748 /**
1749 * Enables the "Ignore All-Caps Words" option by default.
1750 * You may need to {@link CKEDITOR.config#scayt_disableOptionsStorage disable option storing} for this setting to be
1751 * effective because option storage has a higher priority.
1752 *
1753 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1754 *
1755 * config.scayt_ignoreAllCapsWords = true;
1756 *
1757 * @since 4.5.6
1758 * @cfg {Boolean} [scayt_ignoreAllCapsWords=false]
1759 * @member CKEDITOR.config
1760 */
1761
1762 /**
1763 * Enables the "Ignore Domain Names" option by default.
1764 * You may need to {@link CKEDITOR.config#scayt_disableOptionsStorage disable option storing} for this setting to be
1765 * effective because option storage has a higher priority.
1766 *
1767 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1768 *
1769 * config.scayt_ignoreDomainNames = true;
1770 *
1771 * @since 4.5.6
1772 * @cfg {Boolean} [scayt_ignoreDomainNames=false]
1773 * @member CKEDITOR.config
1774 */
1775
1776 /**
1777 * Enables the "Ignore Words with Mixed Case" option by default.
1778 * You may need to {@link CKEDITOR.config#scayt_disableOptionsStorage disable option storing} for this setting to be
1779 * effective because option storage has a higher priority.
1780 *
1781 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1782 *
1783 * config.scayt_ignoreWordsWithMixedCases = true;
1784 *
1785 * @since 4.5.6
1786 * @cfg {Boolean} [scayt_ignoreWordsWithMixedCases=false]
1787 * @member CKEDITOR.config
1788 */
1789
1790 /**
1791 * Enables the "Ignore Words with Numbers" option by default.
1792 * You may need to {@link CKEDITOR.config#scayt_disableOptionsStorage disable option storing} for this setting to be
1793 * effective because option storage has a higher priority.
1794 *
1795 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1796 *
1797 * config.scayt_ignoreWordsWithNumbers = true;
1798 *
1799 * @since 4.5.6
1800 * @cfg {Boolean} [scayt_ignoreWordsWithNumbers=false]
1801 * @member CKEDITOR.config
1802 */
1803
1804 /**
1805 * Disables storing of SCAYT options between sessions. Option storing will be turned off after a page refresh.
1806 * The following settings can be used:
1807 *
1808 * * `'options'` &ndash; Disables storing of all SCAYT Ignore options.
1809 * * `'ignore-all-caps-words'` &ndash; Disables storing of the "Ignore All-Caps Words" option.
1810 * * `'ignore-domain-names'` &ndash; Disables storing of the "Ignore Domain Names" option.
1811 * * `'ignore-words-with-mixed-cases'` &ndash; Disables storing of the "Ignore Words with Mixed Case" option.
1812 * * `'ignore-words-with-numbers'` &ndash; Disables storing of the "Ignore Words with Numbers" option.
1813 * * `'lang'` &ndash; Disables storing of the SCAYT spell check language.
1814 * * `'all'` &ndash; Disables storing of all SCAYT options.
1815 *
1816 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1817 *
1818 * Example:
1819 *
1820 * // Disabling one option.
1821 * config.scayt_disableOptionsStorage = 'all';
1822 *
1823 * // Disabling several options.
1824 * config.scayt_disableOptionsStorage = ['lang', 'ignore-domain-names', 'ignore-words-with-numbers'];
1825 *
1826 *
1827 * @cfg {String|Array} [scayt_disableOptionsStorage = '']
1828 * @member CKEDITOR.config
1829 */
1830
1831 /**
1832 * Specifies the names of tags that will be skipped while spell checking. This is a string containing tag names
1833 * separated by commas (`','`). Please note that the `'style'` tag would be added to specified tags list.
1834 *
1835 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1836 *
1837 * config.scayt_elementsToIgnore = 'del,pre';
1838 *
1839 * @cfg {String} [scayt_elementsToIgnore='style']
1840 * @member CKEDITOR.config
1841 */
1842
1843 /**
1844 * Enables multi-language support in SCAYT. If set to `true`, turns on SCAYT multi-language support after loading the editor.
1845 *
1846 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1847 *
1848 * config.scayt_multiLanguageMode = true;
1849 *
1850 * @cfg {Boolean} [scayt_multiLanguageMode=false]
1851 * @member CKEDITOR.config
1852 */
1853
1854 /**
1855 * Defines additional styles for misspellings for specified languages. Styles will be applied only if
1856 * the {@link CKEDITOR.config#scayt_multiLanguageMode} option is set to `true` and the [Language](http://ckeditor.com/addon/language)
1857 * plugin is included and loaded in the editor. By default, all misspellings will still be underlined with the red waveline.
1858 *
1859 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1860 *
1861 * Example:
1862 *
1863 * // Display misspellings in French language with green color and underlined with red waveline.
1864 * config.scayt_multiLanguageStyles = {
1865 * 'fr': 'color: green'
1866 * };
1867 *
1868 * // Display misspellings in Italian language with green color and underlined with red waveline
1869 * // and German misspellings with red color only.
1870 * config.scayt_multiLanguageStyles = {
1871 * 'it': 'color: green',
1872 * 'de': 'background-image: none; color: red'
1873 * };
1874 *
1875 * @cfg {Object} [scayt_multiLanguageStyles = {}]
1876 * @member CKEDITOR.config
1877 */