2 CKEDITOR
.plugins
.add('scayt', {
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%
10 dialogName: 'scaytDialog',
11 init: function(editor
) {
13 plugin
= CKEDITOR
.plugins
.scayt
;
15 this.bindEvents(editor
);
16 this.parseConfig(editor
);
20 CKEDITOR
.dialog
.add(this.dialogName
, CKEDITOR
.getUrl(this.path
+ 'dialogs/options.js'));
23 this.addMenuItems(editor
);
24 var config
= editor
.config
,
25 lang
= editor
.lang
.scayt
,
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',
35 var buttonState
= editor
.ui
.instances
.Scayt
.getState();
37 // check if scayt is created
39 // check if scayt is enabled
40 if(plugin
.state
.scayt
[editor
.name
]) {
41 buttonState
= CKEDITOR
.TRISTATE_ON
;
43 buttonState
= CKEDITOR
.TRISTATE_OFF
;
47 editor
.fire('scaytButtonState', buttonState
);
49 onRender: function() {
52 editor
.on('scaytButtonState', function(ev
) {
53 if(typeof ev
.data
!== undefined) {
54 that
.setState(ev
.data
);
59 var scaytInstance
= editor
.scayt
;
61 editor
.getMenuItem('scaytToggle').label
= editor
.lang
.scayt
[(scaytInstance
? plugin
.state
.scayt
[editor
.name
] : false) ? 'btn_disable' : 'btn_enable'];
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
73 if(!editor
.config
.scayt_uiTabs
[0]) {
74 delete menuDefinition
.scaytOptions
;
77 if(!editor
.config
.scayt_uiTabs
[1]) {
78 delete menuDefinition
.scaytLangs
;
81 if(!editor
.config
.scayt_uiTabs
[2]) {
82 delete menuDefinition
.scaytDict
;
85 return menuDefinition
;
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
;
96 selectionNode
= scaytInstance
.getSelectionNode();
98 // SCAYT shouldn't build context menu if instance isnot created or word is without misspelling or grammar problem
100 var items
= self
.menuGenerator(editor
, selectionNode
);
102 scaytInstance
.showBanner('.' + editor
.contextMenu
._
.definition
.panel
.className
.split(' ').join(' .'));
110 editor
.contextMenu
._
.onHide
= CKEDITOR
.tools
.override(editor
.contextMenu
._
.onHide
, function(org
) {
112 var scaytInstance
= editor
.scayt
;
115 scaytInstance
.hideBanner();
118 return org
.apply(this);
123 addMenuItems: function(editor
) {
125 plugin
= CKEDITOR
.plugins
.scayt
,
126 graytGroups
= ['grayt_description', 'grayt_suggest', 'grayt_control'],
127 menuGroup
= 'scaytButton';
129 editor
.addMenuGroup(menuGroup
);
131 var items_order
= editor
.config
.scayt_contextMenuItemsOrder
.split('|');
133 for(var pos
= 0 ; pos
< items_order
.length
; pos
++) {
134 items_order
[pos
] = 'scayt_' + items_order
[pos
];
136 items_order
= graytGroups
.concat(items_order
);
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);
144 editor
.addCommand( 'scaytToggle', {
145 exec: function(editor
) {
146 var scaytInstance
= editor
.scayt
;
148 plugin
.state
.scayt
[editor
.name
] = !plugin
.state
.scayt
[editor
.name
];
150 if(plugin
.state
.scayt
[editor
.name
] === true) {
152 plugin
.createScayt(editor
);
156 plugin
.destroy(editor
);
162 editor
.addCommand( 'scaytAbout', {
163 exec: function(editor
) {
164 var scaytInstance
= editor
.scayt
;
166 scaytInstance
.tabToOpen
= 'about';
167 editor
.lockSelection();
168 editor
.openDialog(self
.dialogName
);
172 editor
.addCommand( 'scaytOptions', {
173 exec: function(editor
) {
174 var scaytInstance
= editor
.scayt
;
176 scaytInstance
.tabToOpen
= 'options';
177 editor
.lockSelection();
178 editor
.openDialog(self
.dialogName
);
182 editor
.addCommand( 'scaytLangs', {
183 exec: function(editor
) {
184 var scaytInstance
= editor
.scayt
;
186 scaytInstance
.tabToOpen
= 'langs';
187 editor
.lockSelection();
188 editor
.openDialog(self
.dialogName
);
192 editor
.addCommand( 'scaytDict', {
193 exec: function(editor
) {
194 var scaytInstance
= editor
.scayt
;
196 scaytInstance
.tabToOpen
= 'dictionaries';
197 editor
.lockSelection();
198 editor
.openDialog(self
.dialogName
);
204 label : editor
.lang
.scayt
.btn_enable
,
206 command: 'scaytToggle'
209 label : editor
.lang
.scayt
.btn_about
,
211 command: 'scaytAbout'
214 label : editor
.lang
.scayt
.btn_options
,
216 command: 'scaytOptions'
219 label : editor
.lang
.scayt
.btn_langs
,
221 command: 'scaytLangs'
224 label : editor
.lang
.scayt
.btn_dictionaries
,
230 if(editor
.plugins
.wsc
) {
232 label : editor
.lang
.wsc
.toolbar
,
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();
240 text
= text
.replace(/\s/g, '');
243 if(scaytInstance
&& plugin
.state
.scayt
[editor
.name
] && scaytInstance
.setMarkupPaused
) {
244 scaytInstance
.setMarkupPaused(true);
247 editor
.lockSelection();
248 editor
.execCommand('checkspell');
250 alert('Nothing to check!');
256 editor
.addMenuItems(uiMenuItems
);
258 bindEvents: function(editor
) {
260 plugin
= CKEDITOR
.plugins
.scayt
,
261 inline_mode
= (editor
.elementMode
== CKEDITOR
.ELEMENT_MODE_INLINE
);
263 var scaytDestroy = function() {
264 plugin
.destroy(editor
);
268 * Dirty fix for placeholder drag&drop
269 * Should be fixed with next release
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);
278 evt.data.dropRange = dropRange;
280 }, this, null, 0); // We should be sure that we modify dropRange before CKEDITOR.plugins.clipboard calls
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
);
290 var addMarkupStateHandlers = function() {
291 var editable
= editor
.editable();
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
298 var pluginStatus
= CKEDITOR
.plugins
.scayt
&& CKEDITOR
.plugins
.scayt
.state
.scayt
[editor
.name
] && editor
.scayt
,
299 selectedElement
, ranges
, textLength
, range
;
301 if((inline_mode
? true : pluginStatus
) && editor
._
.savedSelection
) {
302 selectedElement
= editor
._
.savedSelection
.getSelectedElement();
303 ranges
= !selectedElement
&& editor
._
.savedSelection
.getRanges();
305 for(var i
= 0; i
< ranges
.length
; 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);
316 }, this, null, -10 ); // priority "-10" is set to call SCAYT CKEDITOR.editor#unlockSelection before CKEDITOR.editor#unlockSelection call
319 var contentDomHandler = function() {
322 if (!editor
.config
.scayt_inlineModeImmediateMarkup
) {
324 * Give an opportunity to CKEditor to perform all needed updates
325 * and only after that call 'scaytDestroy' method (#72725)
327 editor
.on('blur', function () { setTimeout( scaytDestroy
, 0 ); } );
328 editor
.on('focus', contentDomReady
);
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
) {
344 addMarkupStateHandlers();
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.
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
);
356 widget
.wrapper
= target
.getAscendant( function( el
) {
357 return el
.hasAttribute( 'data-cke-widget-wrapper' )
360 }, this, null, -10); // '-10': we need to be shure that widget#wrapper updated before any other calls
363 editor
.on('contentDom', contentDomHandler
);
365 editor
.on('beforeCommandExec', function(ev
) {
366 var scaytInstance
= editor
.scayt
,
367 selectedLangElement
= null,
368 forceBookmark
= false,
369 removeMarkupInsideSelection
= true;
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') {
374 plugin
.destroy(editor
);
375 editor
.fire('scaytButtonState', CKEDITOR
.TRISTATE_DISABLED
);
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') {
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;
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;
398 editor
.fire('reloadMarkupScayt', {
400 removeInside: removeMarkupInsideSelection
,
401 forceBookmark: forceBookmark
,
402 selectionNode: selectedLangElement
410 editor
.on('beforeSetMode', function(ev
) {
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
;
419 plugin
.destroy(editor
);
420 editor
.fire('scaytButtonState', CKEDITOR
.TRISTATE_DISABLED
);
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');
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();
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;
441 if (!scaytLangList
|| !(scaytLangList
.ltr
&& scaytLangList
.rtl
)) return;
443 scaytInstance
.fire('startSpellCheck, startGrammarCheck');
448 // handle readonly changes
449 editor
.on('readOnly', function(ev
) {
453 scaytInstance
= editor
.scayt
;
455 if(ev
.editor
.readOnly
=== true) {
457 scaytInstance
.fire('removeMarkupInDocument', {});
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
);
470 // we need to destroy SCAYT before CK editor will be completely destroyed
471 editor
.on('beforeDestroy', scaytDestroy
);
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() {
478 // in inline mode SetData does not fire contentDom event
479 if(editor
.elementMode
== CKEDITOR
.ELEMENT_MODE_INLINE
|| editor
.plugins
.divarea
) {
485 * Main entry point to react on changes in document
487 editor
.on('reloadMarkupScayt', function(ev
) {
488 var removeOptions
= ev
.data
&& ev
.data
.removeOptions
,
489 timeout
= ev
.data
&& ev
.data
.timeout
;
492 * Perform removeMarkupInSelectionNode and 'startSpellCheck' fire
493 * asynchroniosly and keep CKEDITOR flow as expected
495 setTimeout(function() {
496 var scaytInstance
= editor
.scayt
,
497 scaytLangList
= scaytInstance
&& scaytInstance
.getScaytLangList();
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;
504 if (!scaytLangList
|| !(scaytLangList
.ltr
&& scaytLangList
.rtl
)) return;
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
512 editor
.document
.fire( 'keydown', new CKEDITOR
.dom
.event( { keyCode: 37 } ) );
514 /* trigger remove markup with 'startSpellCheck' */
515 scaytInstance
.removeMarkupInSelectionNode(removeOptions
);
516 scaytInstance
.fire('startSpellCheck, startGrammarCheck');
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}});
526 editor
.on('insertHtml', function() {
527 editor
.fire('reloadMarkupScayt');
530 editor
.on('insertText', function() {
531 editor
.fire('reloadMarkupScayt');
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
;
539 dialog
.selectPage(scaytInstance
.tabToOpen
);
542 parseConfig: function(editor
) {
543 var plugin
= CKEDITOR
.plugins
.scayt
;
545 // preprocess config for backward compatibility
546 plugin
.replaceOldOptionsNames(editor
.config
);
548 // Checking editor's config after initialization
549 if(typeof editor
.config
.scayt_autoStartup
!== 'boolean') {
550 editor
.config
.scayt_autoStartup
= false;
552 plugin
.state
.scayt
[editor
.name
] = editor
.config
.scayt_autoStartup
;
554 if(typeof editor
.config
.grayt_autoStartup
!== 'boolean') {
555 editor
.config
.grayt_autoStartup
= false;
557 if(typeof editor
.config
.scayt_inlineModeImmediateMarkup
!== 'boolean') {
558 editor
.config
.scayt_inlineModeImmediateMarkup
= false;
560 plugin
.state
.grayt
[editor
.name
] = editor
.config
.grayt_autoStartup
;
562 if(!editor
.config
.scayt_contextCommands
) {
563 editor
.config
.scayt_contextCommands
= 'ignore|ignoreall|add';
566 if(!editor
.config
.scayt_contextMenuItemsOrder
) {
567 editor
.config
.scayt_contextMenuItemsOrder
= 'suggest|moresuggest|control';
570 if(!editor
.config
.scayt_sLang
) {
571 editor
.config
.scayt_sLang
= 'en_US';
574 if(editor
.config
.scayt_maxSuggestions
=== undefined || typeof editor
.config
.scayt_maxSuggestions
!= 'number' || editor
.config
.scayt_maxSuggestions
< 0) {
575 editor
.config
.scayt_maxSuggestions
= 5;
578 if(editor
.config
.scayt_minWordLength
=== undefined || typeof editor
.config
.scayt_minWordLength
!= 'number' || editor
.config
.scayt_minWordLength
< 1) {
579 editor
.config
.scayt_minWordLength
= 4;
582 if(editor
.config
.scayt_customDictionaryIds
=== undefined || typeof editor
.config
.scayt_customDictionaryIds
!== 'string') {
583 editor
.config
.scayt_customDictionaryIds
= '';
586 if(editor
.config
.scayt_userDictionaryName
=== undefined || typeof editor
.config
.scayt_userDictionaryName
!== 'string') {
587 editor
.config
.scayt_userDictionaryName
= null;
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(',');
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
));
599 _tempUITabs
.push(false);
603 if (CKEDITOR
.tools
.search(_tempUITabs
, false) === null) {
604 editor
.config
.scayt_uiTabs
= scayt_uiTabs
;
606 editor
.config
.scayt_uiTabs
= [1,1,1];
610 editor
.config
.scayt_uiTabs
= [1,1,1];
613 if(typeof editor
.config
.scayt_serviceProtocol
!= 'string') {
614 editor
.config
.scayt_serviceProtocol
= null;
617 if(typeof editor
.config
.scayt_serviceHost
!= 'string') {
618 editor
.config
.scayt_serviceHost
= null;
621 if(typeof editor
.config
.scayt_servicePort
!= 'string') {
622 editor
.config
.scayt_servicePort
= null;
625 if(typeof editor
.config
.scayt_servicePath
!= 'string') {
626 editor
.config
.scayt_servicePath
= null;
629 if(!editor
.config
.scayt_moreSuggestions
) {
630 editor
.config
.scayt_moreSuggestions
= 'on';
633 if(typeof editor
.config
.scayt_customerId
!== 'string') {
634 editor
.config
.scayt_customerId
= '1:WvF0D4-UtPqN1-43nkD4-NKvUm2-daQqk3-LmNiI-z7Ysb4-mwry24-T8YrS3-Q2tpq2';
637 if(typeof editor
.config
.scayt_srcUrl
!== 'string') {
638 var protocol
= document
.location
.protocol
;
639 protocol
= protocol
.search(/https?:/) != -1 ? protocol : 'http:';
641 editor
.config
.scayt_srcUrl
= protocol
+ '//svc.webspellchecker.net/spellcheck31/lf/scayt3/ckscayt/ckscayt.js';
644 if(typeof CKEDITOR
.config
.scayt_handleCheckDirty
!== 'boolean') {
645 CKEDITOR
.config
.scayt_handleCheckDirty
= true;
648 if(typeof CKEDITOR
.config
.scayt_handleUndoRedo
!== 'boolean') {
649 /* set default as 'true' */
650 CKEDITOR
.config
.scayt_handleUndoRedo
= true;
652 /* checking 'undo' plugin, if no disable SCAYT handler */
653 CKEDITOR
.config
.scayt_handleUndoRedo
= CKEDITOR
.plugins
.undo
? CKEDITOR
.config
.scayt_handleUndoRedo : false;
655 if(typeof editor
.config
.scayt_multiLanguageMode
!== 'boolean') {
656 editor
.config
.scayt_multiLanguageMode
= false;
659 if(typeof editor
.config
.scayt_multiLanguageStyles
!== 'object') {
660 editor
.config
.scayt_multiLanguageStyles
= {};
663 if(editor
.config
.scayt_ignoreAllCapsWords
&& typeof editor
.config
.scayt_ignoreAllCapsWords
!== 'boolean') {
664 editor
.config
.scayt_ignoreAllCapsWords
= false;
667 if(editor
.config
.scayt_ignoreDomainNames
&& typeof editor
.config
.scayt_ignoreDomainNames
!== 'boolean') {
668 editor
.config
.scayt_ignoreDomainNames
= false;
671 if(editor
.config
.scayt_ignoreWordsWithMixedCases
&& typeof editor
.config
.scayt_ignoreWordsWithMixedCases
!== 'boolean') {
672 editor
.config
.scayt_ignoreWordsWithMixedCases
= false;
675 if(editor
.config
.scayt_ignoreWordsWithNumbers
&& typeof editor
.config
.scayt_ignoreWordsWithNumbers
!== 'boolean') {
676 editor
.config
.scayt_ignoreWordsWithNumbers
= false;
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
;
686 var isValidOption = function( option
) {
687 return !!search( availableValue
, option
);
690 var makeOptionsToStorage = function( options
) {
693 for (var i
= 0; i
< options
.length
; i
++) {
694 var value
= options
[i
],
695 isGroupOptionInUserOptions
= !!search( options
, 'options' );
697 if( !isValidOption( value
) || isGroupOptionInUserOptions
&& !!search( valuesOption
, function( val
) { if( val
=== 'lang' ) { return false; } } ) ) {
701 if( !!search( valuesOption
, value
) ) {
702 valuesOption
.splice( indexOf( valuesOption
, value
), 1 );
705 if( value
=== 'all' || isGroupOptionInUserOptions
&& !!search( options
, 'lang' )) {
709 if( value
=== 'options' ) {
710 valuesOption
= [ 'lang' ];
714 retval
= retval
.concat( valuesOption
);
719 editor
.config
.scayt_disableOptionsStorage
= makeOptionsToStorage( userOptions
);
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
;
732 if( scaytInstance
&& (element
.hasAttribute(plugin
.options
.data_attribute_name
) || element
.hasAttribute(plugin
.options
.problem_grammar_data_attribute
)) ) {
736 removeFormatFilterTemplate = function(element
) {
737 var scaytInstance
= editor
.scayt
,
740 if( scaytInstance
&& (element
.hasAttribute(plugin
.options
.data_attribute_name
) || element
.hasAttribute(plugin
.options
.problem_grammar_data_attribute
)) ) {
748 pathFilters
.push(pathFilter
);
752 var dataFilterRules
= {
754 span: function(element
) {
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
];
759 if(plugin
&& (scaytState
|| graytState
)) {
768 dataFilter
.addRules(dataFilterRules
);
772 var htmlFilterRules
= {
774 span: function(element
) {
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
];
779 if(plugin
&& (scaytState
|| graytState
)) {
788 htmlFilter
.addRules(htmlFilterRules
);
791 if(removeFormatFilter
) {
792 removeFormatFilter
.call(editor
, removeFormatFilterTemplate
);
795 scaytMenuDefinition: function(editor
) {
797 plugin
= CKEDITOR
.plugins
.scayt
,
798 scayt_instance
= editor
.scayt
;
803 label: scayt_instance
.getLocal('btn_ignore'),
804 group : 'scayt_control',
806 exec: function(editor
) {
807 var scaytInstance
= editor
.scayt
;
808 scaytInstance
.ignoreWord();
812 label : scayt_instance
.getLocal('btn_ignoreAll'),
813 group : 'scayt_control',
815 exec: function(editor
) {
816 var scaytInstance
= editor
.scayt
;
817 scaytInstance
.ignoreAllWords();
821 label : scayt_instance
.getLocal('btn_addWord'),
822 group : 'scayt_control',
824 exec : function(editor
) {
825 var scaytInstance
= editor
.scayt
;
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();
835 label : scayt_instance
.getLocal('btn_options'),
836 group : 'scayt_control',
838 exec: function(editor
) {
839 var scaytInstance
= editor
.scayt
;
841 scaytInstance
.tabToOpen
= 'options';
842 editor
.lockSelection();
843 editor
.openDialog(self
.dialogName
);
845 verification: function(editor
) {
846 return (editor
.config
.scayt_uiTabs
[0] == 1) ? true : false;
850 label : scayt_instance
.getLocal('btn_langs'),
851 group : 'scayt_control',
853 exec: function(editor
) {
854 var scaytInstance
= editor
.scayt
;
856 scaytInstance
.tabToOpen
= 'langs';
857 editor
.lockSelection();
858 editor
.openDialog(self
.dialogName
);
860 verification: function(editor
) {
861 return (editor
.config
.scayt_uiTabs
[1] == 1) ? true : false;
865 label : scayt_instance
.getLocal('btn_dictionaries'),
866 group : 'scayt_control',
868 exec: function(editor
) {
869 var scaytInstance
= editor
.scayt
;
871 scaytInstance
.tabToOpen
= 'dictionaries';
872 editor
.lockSelection();
873 editor
.openDialog(self
.dialogName
);
875 verification: function(editor
) {
876 return (editor
.config
.scayt_uiTabs
[2] == 1) ? true : false;
880 label : scayt_instance
.getLocal('btn_about'),
881 group : 'scayt_control',
883 exec: function(editor
) {
884 var scaytInstance
= editor
.scayt
;
886 scaytInstance
.tabToOpen
= 'about';
887 editor
.lockSelection();
888 editor
.openDialog(self
.dialogName
);
893 grayt_problemdescription: {
894 label : 'Grammar problem description',
895 group : 'grayt_description', // look at addMenuItems method for further info
897 state : CKEDITOR
.TRISTATE_DISABLED
,
898 exec: function(editor
) {}
901 label : scayt_instance
.getLocal('btn_ignore'),
902 group : 'grayt_control',
904 exec: function(editor
) {
905 var scaytInstance
= editor
.scayt
;
907 scaytInstance
.ignorePhrase();
913 buildSuggestionMenuItems: function(editor
, suggestions
, isScaytNode
) {
917 replaceKeyName
= isScaytNode
? 'word' : 'phrase',
918 updateEventName
= isScaytNode
? 'startGrammarCheck' : 'startSpellCheck',
919 plugin
= CKEDITOR
.plugins
.scayt
,
920 scayt_instance
= editor
.scayt
;
922 if(suggestions
.length
> 0 && suggestions
[0] !== 'no_any_suggestions') {
925 // build SCAYT suggestions
926 for(var i
= 0; i
< suggestions
.length
; i
++) {
928 var commandName
= 'scayt_suggest_' + CKEDITOR
.plugins
.scayt
.suggestions
[i
].replace(' ', '_');
930 editor
.addCommand(commandName
, self
.createCommand(CKEDITOR
.plugins
.scayt
.suggestions
[i
], replaceKeyName
, updateEventName
));
932 if(i
< editor
.config
.scayt_maxSuggestions
) {
935 editor
.addMenuItem(commandName
, {
936 label: suggestions
[i
],
937 command: commandName
,
938 group: 'scayt_suggest',
942 itemList
[commandName
] = CKEDITOR
.TRISTATE_OFF
;
947 editor
.addMenuItem(commandName
, {
948 label: suggestions
[i
],
949 command: commandName
,
950 group: 'scayt_moresuggest',
954 subItemList
[commandName
] = CKEDITOR
.TRISTATE_OFF
;
956 if(editor
.config
.scayt_moreSuggestions
=== 'on') {
958 editor
.addMenuItem('scayt_moresuggest', {
959 label : scayt_instance
.getLocal('btn_moreSuggestions'),
960 group : 'scayt_moresuggest',
962 getItems : function() {
967 itemList
['scayt_moresuggest'] = CKEDITOR
.TRISTATE_OFF
;
972 // build GRAYT suggestions
973 for(var i
= 0; i
< suggestions
.length
; i
++) {
974 var commandName
= 'grayt_suggest_' + CKEDITOR
.plugins
.scayt
.suggestions
[i
].replace(' ', '_');
976 editor
.addCommand(commandName
, self
.createCommand(CKEDITOR
.plugins
.scayt
.suggestions
[i
], replaceKeyName
, updateEventName
));
979 editor
.addMenuItem(commandName
, {
980 label: suggestions
[i
],
981 command: commandName
,
982 group: 'grayt_suggest',
986 itemList
[commandName
] = CKEDITOR
.TRISTATE_OFF
;
990 var noSuggestionsCommand
= 'no_scayt_suggest';
991 itemList
[noSuggestionsCommand
] = CKEDITOR
.TRISTATE_DISABLED
;
993 editor
.addCommand(noSuggestionsCommand
, {
999 editor
.addMenuItem(noSuggestionsCommand
, {
1000 label : scayt_instance
.getLocal('btn_noSuggestions') || noSuggestionsCommand
,
1001 command: noSuggestionsCommand
,
1002 group : 'scayt_suggest',
1009 menuGenerator: function(editor
, selectionNode
) {
1011 scaytInstance
= editor
.scayt
,
1012 menuItems
= this.scaytMenuDefinition(editor
),
1014 allowedOption
= editor
.config
.scayt_contextCommands
.split('|'),
1015 lang
= selectionNode
.getAttribute(scaytInstance
.getLangAttribute()) || scaytInstance
.getLang(),
1016 word
, grammarPhrase
, isScaytNode
, isGrammarNode
, problemDescriptionText
;
1019 isScaytNode
= scaytInstance
.isScaytNode(selectionNode
);
1020 isGrammarNode
= scaytInstance
.isGraytNode(selectionNode
);
1023 // we clicked scayt misspelling
1025 menuItems
= menuItems
.scayt
;
1027 word
= selectionNode
.getAttribute(scaytInstance
.getScaytNodeAttributeName());
1029 scaytInstance
.fire('getSuggestionsList', {
1034 itemList
= this.buildSuggestionMenuItems(editor
, CKEDITOR
.plugins
.scayt
.suggestions
, isScaytNode
);
1035 } else if(isGrammarNode
) {
1036 // we clicked grammar problem
1038 menuItems
= menuItems
.grayt
;
1039 grammarPhrase
= selectionNode
.getAttribute(scaytInstance
.getGraytNodeAttributeName());
1041 // setup grammar problem description
1042 problemDescriptionText
= scaytInstance
.getProblemDescriptionText(grammarPhrase
, lang
);
1043 if(menuItems
.grayt_problemdescription
&& problemDescriptionText
) {
1044 menuItems
.grayt_problemdescription
.label
= problemDescriptionText
;
1047 scaytInstance
.fire('getGrammarSuggestionsList', {
1049 phrase: grammarPhrase
1052 itemList
= this.buildSuggestionMenuItems(editor
, CKEDITOR
.plugins
.scayt
.suggestions
, isScaytNode
);
1055 if(isScaytNode
&& editor
.config
.scayt_contextCommands
== 'off') {
1059 for(var key
in menuItems
) {
1060 if(isScaytNode
&& CKEDITOR
.tools
.indexOf(allowedOption
, key
.replace('scayt_', '')) == -1 && editor
.config
.scayt_contextCommands
!= 'all') {
1064 if(typeof menuItems
[key
].state
!= 'undefined') {
1065 itemList
[key
] = menuItems
[key
].state
;
1067 itemList
[key
] = CKEDITOR
.TRISTATE_OFF
;
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
];
1076 editor
.addCommand(key
, {
1077 exec: menuItems
[key
].exec
1080 editor
.addMenuItem(key
, {
1081 label : editor
.lang
.scayt
[menuItems
[key
].label
] || menuItems
[key
].label
,
1083 group : menuItems
[key
].group
,
1084 order : menuItems
[key
].order
1090 createCommand: function(suggestion
, replaceKeyName
, updateEventName
) {
1092 exec: function(editor
) {
1093 var scaytInstance
= editor
.scayt
,
1096 eventObject
[replaceKeyName
] = suggestion
;
1097 scaytInstance
.replaceSelectionNode(eventObject
);
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});
1103 // for grayt problem replacement we need to fire 'startSpellCheck'
1104 // for scayt misspelling replacement we need to fire 'startGrammarCheck'
1105 scaytInstance
.fire(updateEventName
);
1111 CKEDITOR
.plugins
.scayt
= {
1122 disablingCommandExec: {
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'
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'
1139 replaceOldOptionsNames: function(config
) {
1140 for(var key
in config
) {
1141 if(key
in this.backCompatibilityMap
) {
1142 config
[this.backCompatibilityMap
[key
]] = config
[key
];
1147 createScayt : function(editor
) {
1149 plugin
= CKEDITOR
.plugins
.scayt
;
1151 this.loadScaytLibrary(editor
, function(_editor
) {
1152 var textContainer
= _editor
.window
&& _editor
.window
.getFrame() || _editor
.editable();
1154 // Do not create SCAYT if there is no text container for usage
1155 if(!textContainer
) {
1156 plugin
.state
.scayt
[_editor
.name
] = false;
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
]
1181 if(_editor
.config
.scayt_serviceProtocol
) {
1182 scaytInstanceOptions
['service_protocol'] = _editor
.config
.scayt_serviceProtocol
;
1185 if(_editor
.config
.scayt_serviceHost
) {
1186 scaytInstanceOptions
['service_host'] = _editor
.config
.scayt_serviceHost
;
1189 if(_editor
.config
.scayt_servicePort
) {
1190 scaytInstanceOptions
['service_port'] = _editor
.config
.scayt_servicePort
;
1193 if(_editor
.config
.scayt_servicePath
) {
1194 scaytInstanceOptions
['service_path'] = _editor
.config
.scayt_servicePath
;
1197 //predefined options
1198 if(typeof _editor
.config
.scayt_ignoreAllCapsWords
=== 'boolean') {
1199 scaytInstanceOptions
['ignore-all-caps-words'] = _editor
.config
.scayt_ignoreAllCapsWords
;
1202 if(typeof _editor
.config
.scayt_ignoreDomainNames
=== 'boolean') {
1203 scaytInstanceOptions
['ignore-domain-names'] = _editor
.config
.scayt_ignoreDomainNames
;
1206 if(typeof _editor
.config
.scayt_ignoreWordsWithMixedCases
=== 'boolean') {
1207 scaytInstanceOptions
['ignore-words-with-mixed-cases'] = _editor
.config
.scayt_ignoreWordsWithMixedCases
;
1210 if(typeof _editor
.config
.scayt_ignoreWordsWithNumbers
=== 'boolean') {
1211 scaytInstanceOptions
['ignore-words-with-numbers'] = _editor
.config
.scayt_ignoreWordsWithNumbers
;
1214 var scaytInstance
= new SCAYT
.CKSCAYT(scaytInstanceOptions
, function() {
1219 wordsPrefix
= 'word_';
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
=[];
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
]);
1235 CKEDITOR
.plugins
.scayt
.suggestions
= _suggestionList
;
1238 // if selection has changed programmatically by SCAYT we need to react appropriately
1239 scaytInstance
.subscribe('selectionIsChanged', function(data
) {
1240 var selection
= _editor
.getSelection();
1242 if(selection
.isLocked
) {
1243 _editor
.lockSelection();
1247 scaytInstance
.subscribe('graytStateChanged', function(data
) {
1248 plugin
.state
.grayt
[_editor
.name
] = data
.state
;
1251 _editor
.scayt
= scaytInstance
;
1253 _editor
.fire('scaytButtonState', _editor
.readOnly
? CKEDITOR
.TRISTATE_DISABLED : CKEDITOR
.TRISTATE_ON
);
1256 destroy: function(editor
) {
1258 editor
.scayt
.destroy();
1261 delete editor
.scayt
;
1262 editor
.fire('scaytButtonState', CKEDITOR
.TRISTATE_OFF
);
1264 loadScaytLibrary: function(editor
, callback
) {
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
1275 if(this.loadingHelper
[editor
.name
]) return;
1277 if(typeof window
.SCAYT
=== 'undefined' || typeof window
.SCAYT
.CKSCAYT
!== 'function') {
1279 // add onLoad callbacks for editors while SCAYT is loading
1280 this.loadingHelper
[editor
.name
] = callback
;
1281 this.loadingHelper
.loadOrder
.push(editor
.name
);
1283 //creating unique timestamp for SCAYT URL
1285 timestamp
= date
.getTime();
1286 scaytUrl
= editor
.config
.scayt_srcUrl
;
1288 //if there already implemented timstamp for scayr_srcURL use it, if not use our timestamp
1289 scaytUrl
= scaytUrl
+ (scaytUrl
.indexOf('?') >= 0 ? '' : '?' + timestamp
);
1291 if (!this.loadingHelper
.ckscaytLoading
) {
1292 CKEDITOR
.scriptLoader
.load(scaytUrl
, function(success
) {
1296 CKEDITOR
.fireOnce('scaytReady');
1298 for(var i
= 0; i
< self
.loadingHelper
.loadOrder
.length
; i
++) {
1299 editorName
= self
.loadingHelper
.loadOrder
[i
];
1301 if(typeof self
.loadingHelper
[editorName
] === 'function') {
1302 self
.loadingHelper
[editorName
](CKEDITOR
.instances
[editorName
]);
1305 delete self
.loadingHelper
[editorName
];
1307 self
.loadingHelper
.loadOrder
= [];
1310 this.loadingHelper
.ckscaytLoading
= true;
1314 } else if(window
.SCAYT
&& typeof window
.SCAYT
.CKSCAYT
=== 'function') {
1315 CKEDITOR
.fireOnce('scaytReady');
1318 if(typeof callback
=== 'function') {
1326 CKEDITOR
.on('dialogDefinition', function(dialogDefinitionEvent
) {
1327 var dialogName
= dialogDefinitionEvent
.data
.name
,
1328 dialogDefinition
= dialogDefinitionEvent
.data
.definition
,
1329 dialog
= dialogDefinition
.dialog
;
1331 if (dialogName
=== 'scaytDialog') {
1332 dialog
.on('cancel', function(cancelEvent
) {
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
;
1343 if ( scaytInstance
&& plugin
.state
.scayt
[ editor
.name
] && scaytInstance
.setMarkupPaused
) {
1344 scaytInstance
.setMarkupPaused( false );
1347 editor
.unlockSelection();
1348 }, this, null, -2 ); // we need to call cancel callback before WSC plugin
1351 if (dialogName
=== 'link') {
1352 dialog
.on('ok', function(okEvent
) {
1353 var editor
= okEvent
.sender
&& okEvent
.sender
.getParentEditor();
1356 setTimeout(function() {
1357 editor
.fire('reloadMarkupScayt', {
1370 CKEDITOR
.on('scaytReady', function() {
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;
1376 editorCheckDirty
.checkDirty
= CKEDITOR
.tools
.override(editorCheckDirty
.checkDirty
, function(org
) {
1380 pluginStatus
= CKEDITOR
.plugins
.scayt
&& CKEDITOR
.plugins
.scayt
.state
.scayt
[this.name
] && this.scayt
,
1381 scaytInstance
= this.scayt
;
1384 retval
= org
.call(this);
1386 retval
= (this.status
== 'ready');
1389 var currentData
= scaytInstance
.removeMarkupFromString(this.getSnapshot()),
1390 prevData
= scaytInstance
.removeMarkupFromString(this._
.previousValue
);
1392 retval
= (retval
&& (prevData
!== currentData
))
1400 editorCheckDirty
.resetDirty
= CKEDITOR
.tools
.override(editorCheckDirty
.resetDirty
, function(org
) {
1402 var pluginStatus
= CKEDITOR
.plugins
.scayt
&& CKEDITOR
.plugins
.scayt
.state
.scayt
[this.name
] && this.scayt
,
1403 scaytInstance
= this.scayt
;//CKEDITOR.plugins.scayt.getScayt(this);
1408 this._
.previousValue
= scaytInstance
.removeMarkupFromString(this.getSnapshot());
1414 if (CKEDITOR
.config
.scayt_handleUndoRedo
=== true) {
1415 var undoImagePrototype
= CKEDITOR
.plugins
.undo
.Image
.prototype;
1417 // add backword compatibility for CKEDITOR 4.2. method equals was repleced on other method
1418 var equalsContentMethodName
= (typeof undoImagePrototype
.equalsContent
== "function") ? 'equalsContent' : 'equals';
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
,
1428 // Making the comparison based on content without SCAYT word markers.
1430 this.contents
= scaytInstance
.removeMarkupFromString(thisContents
) || '';
1431 otherImage
.contents
= scaytInstance
.removeMarkupFromString(otherContents
) || '';
1434 var retval
= org
.apply(this, arguments
);
1436 this.contents
= thisContents
;
1437 otherImage
.contents
= otherContents
;
1446 * Automatically enables SCAYT on editor startup. When set to `true`, this option turns on SCAYT automatically
1447 * after loading the editor.
1449 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1451 * config.scayt_autoStartup = true;
1453 * @cfg {Boolean} [scayt_autoStartup=false]
1454 * @member CKEDITOR.config
1458 * Enables Grammar As You Type (GRAYT) on SCAYT startup. When set to `true`, this option turns on GRAYT automatically
1459 * after SCAYT started.
1461 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1463 * config.grayt_autoStartup = true;
1466 * @cfg {Boolean} [grayt_autoStartup=false]
1467 * @member CKEDITOR.config
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.
1474 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1476 * config.scayt_inlineModeImmediateMarkup = true;
1479 * @cfg {Boolean} [scayt_inlineModeImmediateMarkup=false]
1480 * @member CKEDITOR.config
1484 * Defines the number of SCAYT suggestions to show in the main context menu.
1485 * Possible values are:
1487 * * `0` (zero) – No suggestions are shown in the main context menu. All
1488 * entries will be listed in the "More Suggestions" sub-menu.
1489 * * Positive number – 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 – Five suggestions are shown in the main context menu. All other
1492 * entries will be listed in the "More Suggestions" sub-menu.
1494 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1498 * // Display only three suggestions in the main context menu.
1499 * config.scayt_maxSuggestions = 3;
1501 * // Do not show the suggestions directly.
1502 * config.scayt_maxSuggestions = 0;
1504 * @cfg {Number} [scayt_maxSuggestions=5]
1505 * @member CKEDITOR.config
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.
1512 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1516 * // Set the minimum length of words that will be collected from editor text.
1517 * config.scayt_minWordLength = 5;
1519 * @cfg {Number} [scayt_minWordLength=4]
1520 * @member CKEDITOR.config
1524 * Sets the customer ID for SCAYT. Used for hosted users only. Required for migration from free
1525 * to trial or paid versions.
1527 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1529 * // Load SCAYT using my customer ID.
1530 * config.scayt_customerId = 'your-encrypted-customer-id';
1532 * @cfg {String} [scayt_customerId='1:WvF0D4-UtPqN1-43nkD4-NKvUm2-daQqk3-LmNiI-z7Ysb4-mwry24-T8YrS3-Q2tpq2']
1533 * @member CKEDITOR.config
1537 * Enables and disables the "More Suggestions" sub-menu in the context menu.
1538 * Possible values are `'on'` and `'off'`.
1540 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1542 * // Disables the "More Suggestions" sub-menu.
1543 * config.scayt_moreSuggestions = 'off';
1545 * @cfg {String} [scayt_moreSuggestions='on']
1546 * @member CKEDITOR.config
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 (`'|'`):
1555 * * `off` – Disables all options.
1556 * * `all` – Enables all options.
1557 * * `ignore` – Enables the "Ignore" option.
1558 * * `ignoreall` – Enables the "Ignore All" option.
1559 * * `add` – Enables the "Add Word" option.
1560 * * `option` – Enables the "Options" menu item.
1561 * * `language` – Enables the "Languages" menu item.
1562 * * `dictionary` – Enables the "Dictionaries" menu item.
1563 * * `about` – Enables the "About" menu item.
1565 * Please note that availability of the "Options", "Languages" and "Dictionaries" items
1566 * also depends on the {@link CKEDITOR.config#scayt_uiTabs} option.
1568 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1572 * // Show only "Add Word" and "Ignore All" in the context menu.
1573 * config.scayt_contextCommands = 'add|ignoreall';
1575 * @cfg {String} [scayt_contextCommands='ignore|ignoreall|add']
1576 * @member CKEDITOR.config
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'`.
1586 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1588 * // Sets SCAYT to German.
1589 * config.scayt_sLang = 'de_DE';
1591 * @cfg {String} [scayt_sLang='en_US']
1592 * @member CKEDITOR.config
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'`.
1601 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1603 * // Hides the "Languages" tab.
1604 * config.scayt_uiTabs = '1,0,1';
1606 * @cfg {String} [scayt_uiTabs='1,1,1']
1607 * @member CKEDITOR.config
1611 * Sets the protocol for the WebSpellChecker service (`ssrv.cgi`) full path.
1613 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1615 * // Defines the protocol for the WebSpellChecker service (ssrv.cgi) path.
1616 * config.scayt_serviceProtocol = 'https';
1618 * @cfg {String} [scayt_serviceProtocol='http']
1619 * @member CKEDITOR.config
1623 * Sets the host for the WebSpellChecker service (`ssrv.cgi`) full path.
1625 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1627 * // Defines the host for the WebSpellChecker service (ssrv.cgi) path.
1628 * config.scayt_serviceHost = 'my-host';
1630 * @cfg {String} [scayt_serviceHost='svc.webspellchecker.net']
1631 * @member CKEDITOR.config
1635 * Sets the port for the WebSpellChecker service (`ssrv.cgi`) full path.
1637 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1639 * // Defines the port for the WebSpellChecker service (ssrv.cgi) path.
1640 * config.scayt_servicePort = '2330';
1642 * @cfg {String} [scayt_servicePort='80']
1643 * @member CKEDITOR.config
1647 * Sets the path to the WebSpellChecker service (`ssrv.cgi`).
1649 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1651 * // Defines the path to the WebSpellChecker service (ssrv.cgi).
1652 * config.scayt_servicePath = 'my-path/ssrv.cgi';
1654 * @cfg {String} [scayt_servicePath='spellcheck31/script/ssrv.cgi']
1655 * @member CKEDITOR.config
1659 * Sets the URL to SCAYT core. Required to switch to the licensed version of SCAYT.
1661 * Refer to [SCAYT documentation](http://wiki.webspellchecker.net/doku.php?id=migration:hosredfreetolicensedck)
1664 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1666 * config.scayt_srcUrl = "http://my-host/spellcheck/lf/scayt/scayt.js";
1668 * @cfg {String} [scayt_srcUrl='//svc.webspellchecker.net/spellcheck31/lf/scayt3/ckscayt/ckscayt.js']
1669 * @member CKEDITOR.config
1673 * Links SCAYT to custom dictionaries. This is a string containing the dictionary IDs
1674 * separated by commas (`','`). Available only for the licensed version.
1676 * Refer to [SCAYT documentation](http://wiki.webspellchecker.net/doku.php?id=installationandconfiguration:customdictionaries:licensed)
1679 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1681 * config.scayt_customDictionaryIds = '3021,3456,3478';
1683 * @cfg {String} [scayt_customDictionaryIds='']
1684 * @member CKEDITOR.config
1688 * Activates a User Dictionary in SCAYT. The user
1689 * dictionary name must be used. Available only for the licensed version.
1691 * Refer to [SCAYT documentation](http://wiki.webspellchecker.net/doku.php?id=installationandconfiguration:userdictionaries)
1694 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1696 * config.scayt_userDictionaryName = 'MyDictionary';
1698 * @cfg {String} [scayt_userDictionaryName='']
1699 * @member CKEDITOR.config
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 (`'|'`):
1707 * * `suggest` – The main suggestion word list.
1708 * * `moresuggest` – The "More suggestions" word list.
1709 * * `control` – SCAYT commands, such as "Ignore" and "Add Word".
1711 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1715 * config.scayt_contextMenuItemsOrder = 'moresuggest|control|suggest';
1717 * @cfg {String} [scayt_contextMenuItemsOrder='suggest|moresuggest|control']
1718 * @member CKEDITOR.config
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.
1726 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1728 * config.scayt_handleCheckDirty = 'false';
1730 * @cfg {String} [scayt_handleCheckDirty='true']
1731 * @member CKEDITOR.config
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.
1740 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1742 * config.scayt_handleUndoRedo = 'false';
1744 * @cfg {String} [scayt_handleUndoRedo='true']
1745 * @member CKEDITOR.config
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.
1753 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1755 * config.scayt_ignoreAllCapsWords = true;
1758 * @cfg {Boolean} [scayt_ignoreAllCapsWords=false]
1759 * @member CKEDITOR.config
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.
1767 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1769 * config.scayt_ignoreDomainNames = true;
1772 * @cfg {Boolean} [scayt_ignoreDomainNames=false]
1773 * @member CKEDITOR.config
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.
1781 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1783 * config.scayt_ignoreWordsWithMixedCases = true;
1786 * @cfg {Boolean} [scayt_ignoreWordsWithMixedCases=false]
1787 * @member CKEDITOR.config
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.
1795 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1797 * config.scayt_ignoreWordsWithNumbers = true;
1800 * @cfg {Boolean} [scayt_ignoreWordsWithNumbers=false]
1801 * @member CKEDITOR.config
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:
1808 * * `'options'` – Disables storing of all SCAYT Ignore options.
1809 * * `'ignore-all-caps-words'` – Disables storing of the "Ignore All-Caps Words" option.
1810 * * `'ignore-domain-names'` – Disables storing of the "Ignore Domain Names" option.
1811 * * `'ignore-words-with-mixed-cases'` – Disables storing of the "Ignore Words with Mixed Case" option.
1812 * * `'ignore-words-with-numbers'` – Disables storing of the "Ignore Words with Numbers" option.
1813 * * `'lang'` – Disables storing of the SCAYT spell check language.
1814 * * `'all'` – Disables storing of all SCAYT options.
1816 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1820 * // Disabling one option.
1821 * config.scayt_disableOptionsStorage = 'all';
1823 * // Disabling several options.
1824 * config.scayt_disableOptionsStorage = ['lang', 'ignore-domain-names', 'ignore-words-with-numbers'];
1827 * @cfg {String|Array} [scayt_disableOptionsStorage = '']
1828 * @member CKEDITOR.config
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.
1835 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1837 * config.scayt_elementsToIgnore = 'del,pre';
1839 * @cfg {String} [scayt_elementsToIgnore='style']
1840 * @member CKEDITOR.config
1844 * Enables multi-language support in SCAYT. If set to `true`, turns on SCAYT multi-language support after loading the editor.
1846 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1848 * config.scayt_multiLanguageMode = true;
1850 * @cfg {Boolean} [scayt_multiLanguageMode=false]
1851 * @member CKEDITOR.config
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.
1859 * Read more in the [documentation](#!/guide/dev_spellcheck) and see the [SDK sample](http://sdk.ckeditor.com/samples/spellchecker.html).
1863 * // Display misspellings in French language with green color and underlined with red waveline.
1864 * config.scayt_multiLanguageStyles = {
1865 * 'fr': 'color: green'
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'
1875 * @cfg {Object} [scayt_multiLanguageStyles = {}]
1876 * @member CKEDITOR.config