JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
vanilla ckeditor-3.5.3
[ckeditor.git] / _source / plugins / scayt / plugin.js
1 /*\r
2 Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.\r
3 For licensing, see LICENSE.html or http://ckeditor.com/license\r
4 */\r
5 \r
6 /**\r
7  * @fileOverview Spell Check As You Type (SCAYT).\r
8  * Button name : Scayt.\r
9  */\r
10 \r
11 (function()\r
12 {\r
13         var commandName  = 'scaytcheck',\r
14                 openPage = '';\r
15 \r
16         // Checks if a value exists in an array\r
17         function in_array( needle, haystack )\r
18         {\r
19                 var found = 0,\r
20                         key;\r
21                 for ( key in haystack )\r
22                 {\r
23                         if ( haystack[ key ] == needle )\r
24                         {\r
25                                 found = 1;\r
26                                 break;\r
27                         }\r
28                 }\r
29                 return found;\r
30         }\r
31 \r
32         var onEngineLoad = function()\r
33         {\r
34                 var editor = this;\r
35 \r
36                 var createInstance = function() // Create new instance every time Document is created.\r
37                 {\r
38                         var config = editor.config;\r
39                         // Initialise Scayt instance.\r
40                         var oParams = {};\r
41                         // Get the iframe.\r
42                         oParams.srcNodeRef = editor.document.getWindow().$.frameElement;\r
43                         // syntax : AppName.AppVersion@AppRevision\r
44                         oParams.assocApp  = 'CKEDITOR.' + CKEDITOR.version + '@' + CKEDITOR.revision;\r
45                         oParams.customerid = config.scayt_customerid  || '1:WvF0D4-UtPqN1-43nkD4-NKvUm2-daQqk3-LmNiI-z7Ysb4-mwry24-T8YrS3-Q2tpq2';\r
46                         oParams.customDictionaryIds = config.scayt_customDictionaryIds || '';\r
47                         oParams.userDictionaryName = config.scayt_userDictionaryName || '';\r
48                         oParams.sLang = config.scayt_sLang || 'en_US';\r
49 \r
50                         // Introduce SCAYT onLoad callback. (#5632)\r
51                         oParams.onLoad = function()\r
52                                 {\r
53                                         // Draw down word marker to avoid being covered by background-color style.(#5466)\r
54                                         if ( !( CKEDITOR.env.ie && CKEDITOR.env.version < 8 ) )\r
55                                                 this.addStyle( this.selectorCss(), 'padding-bottom: 2px !important;' );\r
56 \r
57                                         // Call scayt_control.focus when SCAYT loaded\r
58                                         // and only if editor has focus and scayt control creates at first time (#5720)\r
59                                         if ( editor.focusManager.hasFocus && !plugin.isControlRestored( editor ) )\r
60                                                 this.focus();\r
61 \r
62                                 };\r
63 \r
64                         oParams.onBeforeChange = function()\r
65                         {\r
66                                 if ( plugin.getScayt( editor ) && !editor.checkDirty() )\r
67                                         setTimeout( function(){ editor.resetDirty(); }, 0 );\r
68                         };\r
69 \r
70                         var scayt_custom_params = window.scayt_custom_params;\r
71                         if ( typeof scayt_custom_params == 'object' )\r
72                         {\r
73                                 for ( var k in scayt_custom_params )\r
74                                         oParams[ k ] = scayt_custom_params[ k ];\r
75                         }\r
76                         // needs for restoring a specific scayt control settings\r
77                         if ( plugin.getControlId( editor ) )\r
78                                 oParams.id = plugin.getControlId( editor );\r
79 \r
80                         var scayt_control = new window.scayt( oParams );\r
81 \r
82                         scayt_control.afterMarkupRemove.push( function( node )\r
83                         {\r
84                                 ( new CKEDITOR.dom.element( node, scayt_control.document ) ).mergeSiblings();\r
85                         } );\r
86 \r
87                         // Copy config.\r
88                         var lastInstance = plugin.instances[ editor.name ];\r
89                         if ( lastInstance )\r
90                         {\r
91                                 scayt_control.sLang = lastInstance.sLang;\r
92                                 scayt_control.option( lastInstance.option() );\r
93                                 scayt_control.paused = lastInstance.paused;\r
94                         }\r
95 \r
96                         plugin.instances[ editor.name ] = scayt_control;\r
97 \r
98                         try {\r
99                                 scayt_control.setDisabled( plugin.isPaused( editor ) === false );\r
100                         } catch (e) {}\r
101 \r
102                         editor.fire( 'showScaytState' );\r
103                 };\r
104 \r
105                 editor.on( 'contentDom', createInstance );\r
106                 editor.on( 'contentDomUnload', function()\r
107                         {\r
108                                 // Remove scripts.\r
109                                 var scripts = CKEDITOR.document.getElementsByTag( 'script' ),\r
110                                         scaytIdRegex =  /^dojoIoScript(\d+)$/i,\r
111                                         scaytSrcRegex =  /^https?:\/\/svc\.spellchecker\.net\/spellcheck\/script\/ssrv\.cgi/i;\r
112 \r
113                                 for ( var i=0; i < scripts.count(); i++ )\r
114                                 {\r
115                                         var script = scripts.getItem( i ),\r
116                                                 id = script.getId(),\r
117                                                 src = script.getAttribute( 'src' );\r
118 \r
119                                         if ( id && src && id.match( scaytIdRegex ) && src.match( scaytSrcRegex ))\r
120                                                 script.remove();\r
121                                 }\r
122                         });\r
123 \r
124                 editor.on( 'beforeCommandExec', function( ev )          // Disable SCAYT before Source command execution.\r
125                         {\r
126                                 if ( ( ev.data.name == 'source' || ev.data.name == 'newpage' ) && editor.mode == 'wysiwyg' )\r
127                                 {\r
128                                         var scayt_instance = plugin.getScayt( editor );\r
129                                         if ( scayt_instance )\r
130                                         {\r
131                                                 plugin.setPaused( editor, !scayt_instance.disabled );\r
132                                                 // store a control id for restore a specific scayt control settings\r
133                                                 plugin.setControlId( editor, scayt_instance.id );\r
134                                                 scayt_instance.destroy( true );\r
135                                                 delete plugin.instances[ editor.name ];\r
136                                         }\r
137                                 }\r
138                                 // Catch on source mode switch off (#5720)\r
139                                 else if ( ev.data.name == 'source'  && editor.mode == 'source' )\r
140                                         plugin.markControlRestore( editor );\r
141                         });\r
142 \r
143                 editor.on( 'afterCommandExec', function( ev )\r
144                         {\r
145                                 if ( !plugin.isScaytEnabled( editor ) )\r
146                                         return;\r
147 \r
148                                 if ( editor.mode == 'wysiwyg' && ( ev.data.name == 'undo' || ev.data.name == 'redo' ) )\r
149                                         window.setTimeout( function() { plugin.getScayt( editor ).refresh(); }, 10 );\r
150                         });\r
151 \r
152                 editor.on( 'destroy', function( ev )\r
153                         {\r
154                                 var editor = ev.editor,\r
155                                         scayt_instance = plugin.getScayt( editor );\r
156 \r
157                                 // SCAYT instance might already get destroyed by mode switch (#5744).\r
158                                 if ( !scayt_instance )\r
159                                         return;\r
160 \r
161                                 delete plugin.instances[ editor.name ];\r
162                                 // store a control id for restore a specific scayt control settings\r
163                                 plugin.setControlId( editor, scayt_instance.id );\r
164                                 scayt_instance.destroy( true );\r
165                         });\r
166 \r
167                 // Listen to data manipulation to reflect scayt markup.\r
168                 editor.on( 'afterSetData', function()\r
169                         {\r
170                                 if ( plugin.isScaytEnabled( editor ) ) {\r
171                                         window.setTimeout( function()\r
172                                                 {\r
173                                                         var instance = plugin.getScayt( editor );\r
174                                                         instance && instance.refresh();\r
175                                                 }, 10 );\r
176                                 }\r
177                         });\r
178 \r
179                 // Reload spell-checking for current word after insertion completed.\r
180                 editor.on( 'insertElement', function()\r
181                         {\r
182                                 var scayt_instance = plugin.getScayt( editor );\r
183                                 if ( plugin.isScaytEnabled( editor ) )\r
184                                 {\r
185                                         // Unlock the selection before reload, SCAYT will take\r
186                                         // care selection update.\r
187                                         if ( CKEDITOR.env.ie )\r
188                                                 editor.getSelection().unlock( true );\r
189 \r
190                                         // Return focus to the editor and refresh SCAYT markup (#5573).\r
191                                         window.setTimeout( function()\r
192                                         {\r
193                                                 scayt_instance.focus();\r
194                                                 scayt_instance.refresh();\r
195                                         }, 10 );\r
196                                 }\r
197                         }, this, null, 50 );\r
198 \r
199                 editor.on( 'insertHtml', function()\r
200                         {\r
201                                 var scayt_instance = plugin.getScayt( editor );\r
202                                 if ( plugin.isScaytEnabled( editor ) )\r
203                                 {\r
204                                         // Unlock the selection before reload, SCAYT will take\r
205                                         // care selection update.\r
206                                         if ( CKEDITOR.env.ie )\r
207                                                 editor.getSelection().unlock( true );\r
208 \r
209                                         // Return focus to the editor (#5573)\r
210                                         // Refresh SCAYT markup\r
211                                         window.setTimeout( function()\r
212                                         {\r
213                                                 scayt_instance.focus();\r
214                                                 scayt_instance.refresh();\r
215                                         }, 10 );\r
216                                 }\r
217                         }, this, null, 50 );\r
218 \r
219                 editor.on( 'scaytDialog', function( ev )        // Communication with dialog.\r
220                         {\r
221                                 ev.data.djConfig = window.djConfig;\r
222                                 ev.data.scayt_control = plugin.getScayt( editor );\r
223                                 ev.data.tab = openPage;\r
224                                 ev.data.scayt = window.scayt;\r
225                         });\r
226 \r
227                 var dataProcessor = editor.dataProcessor,\r
228                         htmlFilter = dataProcessor && dataProcessor.htmlFilter;\r
229 \r
230                 if ( htmlFilter )\r
231                 {\r
232                         htmlFilter.addRules(\r
233                                 {\r
234                                         elements :\r
235                                         {\r
236                                                 span : function( element )\r
237                                                 {\r
238                                                         if ( element.attributes[ 'data-scayt_word' ]\r
239                                                                         && element.attributes[ 'data-scaytid' ] )\r
240                                                         {\r
241                                                                 delete element.name;    // Write children, but don't write this node.\r
242                                                                 return element;\r
243                                                         }\r
244                                                 }\r
245                                         }\r
246                                 }\r
247                         );\r
248                 }\r
249 \r
250                 // Override Image.equals method avoid CK snapshot module to add SCAYT markup to snapshots. (#5546)\r
251                 var undoImagePrototype = CKEDITOR.plugins.undo.Image.prototype;\r
252                 undoImagePrototype.equals = CKEDITOR.tools.override( undoImagePrototype.equals, function( org )\r
253                 {\r
254                         return function( otherImage )\r
255                         {\r
256                                 var thisContents = this.contents,\r
257                                         otherContents = otherImage.contents;\r
258                                 var scayt_instance = plugin.getScayt( this.editor );\r
259                                 // Making the comparison based on content without SCAYT word markers.\r
260                                 if ( scayt_instance && plugin.isScaytReady( this.editor ) )\r
261                                 {\r
262                                         // scayt::reset might return value undefined. (#5742)\r
263                                         this.contents = scayt_instance.reset( thisContents ) || '';\r
264                                         otherImage.contents = scayt_instance.reset( otherContents ) || '';\r
265                                 }\r
266 \r
267                                 var retval = org.apply( this, arguments );\r
268 \r
269                                 this.contents = thisContents;\r
270                                 otherImage.contents = otherContents;\r
271                                 return retval;\r
272                         };\r
273                 });\r
274 \r
275                 if ( editor.document )\r
276                         createInstance();\r
277         };\r
278 \r
279 CKEDITOR.plugins.scayt =\r
280         {\r
281                 engineLoaded : false,\r
282                 instances : {},\r
283                 // Data storage for SCAYT control, based on editor instances\r
284                 controlInfo : {},\r
285                 setControlInfo : function( editor, o )\r
286                 {\r
287                         if ( editor && editor.name && typeof ( this.controlInfo[ editor.name ] ) != 'object' )\r
288                                 this.controlInfo[ editor.name ] = {};\r
289 \r
290                         for ( var infoOpt in o )\r
291                                 this.controlInfo[ editor.name ][ infoOpt ] = o[ infoOpt ];\r
292                 },\r
293                 isControlRestored : function( editor )\r
294                 {\r
295                         if ( editor &&\r
296                                         editor.name &&\r
297                                         this.controlInfo[ editor.name ] )\r
298                         {\r
299                                 return this.controlInfo[ editor.name ].restored ;\r
300                         }\r
301                         return false;\r
302                 },\r
303                 markControlRestore : function( editor )\r
304                 {\r
305                         this.setControlInfo( editor, { restored:true } );\r
306                 },\r
307                 setControlId: function( editor, id )\r
308                 {\r
309                         this.setControlInfo( editor, { id:id } );\r
310                 },\r
311                 getControlId: function( editor )\r
312                 {\r
313                         if ( editor &&\r
314                                         editor.name &&\r
315                                         this.controlInfo[ editor.name ] &&\r
316                                         this.controlInfo[ editor.name ].id )\r
317                         {\r
318                                 return this.controlInfo[ editor.name ].id;\r
319                         }\r
320                         return null;\r
321                 },\r
322                 setPaused: function( editor , bool )\r
323                 {\r
324                         this.setControlInfo( editor, { paused:bool } );\r
325                 },\r
326                 isPaused: function( editor )\r
327                 {\r
328                         if ( editor &&\r
329                                         editor.name &&\r
330                                         this.controlInfo[editor.name] )\r
331                         {\r
332                                 return this.controlInfo[editor.name].paused;\r
333                         }\r
334                         return undefined;\r
335                 },\r
336                 getScayt : function( editor )\r
337                 {\r
338                         return this.instances[ editor.name ];\r
339                 },\r
340                 isScaytReady : function( editor )\r
341                 {\r
342                         return this.engineLoaded === true &&\r
343                                 'undefined' !== typeof window.scayt && this.getScayt( editor );\r
344                 },\r
345                 isScaytEnabled : function( editor )\r
346                 {\r
347                         var scayt_instance = this.getScayt( editor );\r
348                         return ( scayt_instance ) ? scayt_instance.disabled === false : false;\r
349                 },\r
350                 getUiTabs : function( editor )\r
351                 {\r
352                         var uiTabs = [];\r
353 \r
354                         // read UI tabs value from config\r
355                         var configUiTabs = editor.config.scayt_uiTabs || "1,1,1";\r
356 \r
357                         // convert string to array\r
358                         configUiTabs = configUiTabs.split( ',' );\r
359 \r
360                         // "About us" should be always shown for standard config\r
361                         configUiTabs[3] = "1";\r
362 \r
363                         for ( var i = 0; i < 4; i++ ) {\r
364                                 uiTabs[i] = (typeof window.scayt != "undefined" && typeof window.scayt.uiTags != "undefined")\r
365                                                                 ? (parseInt(configUiTabs[i],10) && window.scayt.uiTags[i])\r
366                                                                 : parseInt(configUiTabs[i],10);\r
367                         }\r
368                         return uiTabs;\r
369                 },\r
370                 loadEngine : function( editor )\r
371                 {\r
372                         // SCAYT doesn't work with Firefox2, Opera and AIR.\r
373                         if ( CKEDITOR.env.gecko && CKEDITOR.env.version < 10900 || CKEDITOR.env.opera || CKEDITOR.env.air )\r
374                                 return editor.fire( 'showScaytState' );\r
375 \r
376                         if ( this.engineLoaded === true )\r
377                                 return onEngineLoad.apply( editor );    // Add new instance.\r
378                         else if ( this.engineLoaded == -1 )                     // We are waiting.\r
379                                 return CKEDITOR.on( 'scaytReady', function(){ onEngineLoad.apply( editor ); } );        // Use function(){} to avoid rejection as duplicate.\r
380 \r
381                         CKEDITOR.on( 'scaytReady', onEngineLoad, editor );\r
382                         CKEDITOR.on( 'scaytReady', function()\r
383                                 {\r
384                                         this.engineLoaded = true;\r
385                                 },\r
386                                 this,\r
387                                 null,\r
388                                 0\r
389                         );      // First to run.\r
390 \r
391                         this.engineLoaded = -1; // Loading in progress.\r
392 \r
393                         // compose scayt url\r
394                         var protocol = document.location.protocol;\r
395                         // Default to 'http' for unknown.\r
396                         protocol = protocol.search( /https?:/) != -1? protocol : 'http:';\r
397                         var baseUrl  = 'svc.spellchecker.net/scayt26/loader__base.js';\r
398 \r
399                         var scaytUrl  =  editor.config.scayt_srcUrl || ( protocol + '//' + baseUrl );\r
400                         var scaytConfigBaseUrl =  plugin.parseUrl( scaytUrl ).path +  '/';\r
401 \r
402                         if( window.scayt == undefined )\r
403                         {\r
404                                 CKEDITOR._djScaytConfig =\r
405                                 {\r
406                                         baseUrl: scaytConfigBaseUrl,\r
407                                         addOnLoad:\r
408                                         [\r
409                                                 function()\r
410                                                 {\r
411                                                         CKEDITOR.fireOnce( 'scaytReady' );\r
412                                                 }\r
413                                         ],\r
414                                         isDebug: false\r
415                                 };\r
416                                 // Append javascript code.\r
417                                 CKEDITOR.document.getHead().append(\r
418                                         CKEDITOR.document.createElement( 'script',\r
419                                                 {\r
420                                                         attributes :\r
421                                                                 {\r
422                                                                         type : 'text/javascript',\r
423                                                                         async : 'true',\r
424                                                                         src : scaytUrl\r
425                                                                 }\r
426                                                 })\r
427                                 );\r
428                         }\r
429                         else\r
430                                 CKEDITOR.fireOnce( 'scaytReady' );\r
431 \r
432                         return null;\r
433                 },\r
434                 parseUrl : function ( data )\r
435                 {\r
436                         var match;\r
437                         if ( data.match && ( match = data.match(/(.*)[\/\\](.*?\.\w+)$/) ) )\r
438                                 return { path: match[1], file: match[2] };\r
439                         else\r
440                                 return data;\r
441                 }\r
442         };\r
443 \r
444         var plugin = CKEDITOR.plugins.scayt;\r
445 \r
446         // Context menu constructing.\r
447         var addButtonCommand = function( editor, buttonName, buttonLabel, commandName, command, menugroup, menuOrder )\r
448         {\r
449                 editor.addCommand( commandName, command );\r
450 \r
451                 // If the "menu" plugin is loaded, register the menu item.\r
452                 editor.addMenuItem( commandName,\r
453                         {\r
454                                 label : buttonLabel,\r
455                                 command : commandName,\r
456                                 group : menugroup,\r
457                                 order : menuOrder\r
458                         });\r
459         };\r
460 \r
461         var commandDefinition =\r
462         {\r
463                 preserveState : true,\r
464                 editorFocus : false,\r
465                 canUndo : false,\r
466 \r
467                 exec: function( editor )\r
468                 {\r
469                         if ( plugin.isScaytReady( editor ) )\r
470                         {\r
471                                 var isEnabled = plugin.isScaytEnabled( editor );\r
472 \r
473                                 this.setState( isEnabled ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_ON );\r
474 \r
475                                 var scayt_control = plugin.getScayt( editor );\r
476                                 // the place where the status of editor focus should be restored\r
477                                 // after there will be ability to store its state before SCAYT button click\r
478                                 // if (storedFocusState is focused )\r
479                                 //   scayt_control.focus();\r
480                                 //\r
481                                 // now focus is set certainly\r
482                                 scayt_control.focus();\r
483                                 scayt_control.setDisabled( isEnabled );\r
484                         }\r
485                         else if ( !editor.config.scayt_autoStartup && plugin.engineLoaded >= 0 )        // Load first time\r
486                         {\r
487                                 this.setState( CKEDITOR.TRISTATE_DISABLED );\r
488                                 plugin.loadEngine( editor );\r
489                         }\r
490                 }\r
491         };\r
492 \r
493         // Add scayt plugin.\r
494         CKEDITOR.plugins.add( 'scayt',\r
495         {\r
496                 requires : [ 'menubutton' ],\r
497 \r
498                 beforeInit : function( editor )\r
499                 {\r
500                         var items_order = editor.config.scayt_contextMenuItemsOrder\r
501                                         || 'suggest|moresuggest|control',\r
502                                 items_order_str = "";\r
503 \r
504                         items_order = items_order.split( '|' );\r
505 \r
506                         if ( items_order && items_order.length )\r
507                         {\r
508                                 for ( var pos = 0 ; pos < items_order.length ; pos++ )\r
509                                         items_order_str += 'scayt_' + items_order[ pos ] + ( items_order.length != parseInt( pos, 10 ) + 1 ? ',' : '' );\r
510                         }\r
511 \r
512                         // Put it on top of all context menu items (#5717)\r
513                         editor.config.menu_groups =  items_order_str + ',' + editor.config.menu_groups;\r
514                 },\r
515 \r
516                 init : function( editor )\r
517                 {\r
518                         // Delete span[data-scaytid] when text pasting in editor (#6921)\r
519                         var dataFilter = editor.dataProcessor && editor.dataProcessor.dataFilter;\r
520                         var dataFilterRules =\r
521                         {\r
522                                         elements :\r
523                                         {\r
524                                                         span : function( element )\r
525                                                         {\r
526                                                                         var attrs = element.attributes;\r
527                                                                         if ( attrs && attrs[ 'data-scaytid' ] )\r
528                                                                                         delete element.name;\r
529                                                         }\r
530                                         }\r
531                         };\r
532                         dataFilter && dataFilter.addRules( dataFilterRules );\r
533 \r
534                         var moreSuggestions = {},\r
535                                 mainSuggestions = {};\r
536 \r
537                         // Scayt command.\r
538                         var command = editor.addCommand( commandName, commandDefinition );\r
539 \r
540                         // Add Options dialog.\r
541                         CKEDITOR.dialog.add( commandName, CKEDITOR.getUrl( this.path + 'dialogs/options.js' ) );\r
542 \r
543                         var uiTabs = plugin.getUiTabs( editor );\r
544 \r
545                         var menuGroup = 'scaytButton';\r
546                         editor.addMenuGroup( menuGroup );\r
547                         // combine menu items to render\r
548                         var uiMenuItems = {};\r
549 \r
550                         var lang = editor.lang.scayt;\r
551 \r
552                         // always added\r
553                         uiMenuItems.scaytToggle =\r
554                                 {\r
555                                         label : lang.enable,\r
556                                         command : commandName,\r
557                                         group : menuGroup\r
558                                 };\r
559 \r
560                         if ( uiTabs[0] == 1 )\r
561                                 uiMenuItems.scaytOptions =\r
562                                 {\r
563                                         label : lang.options,\r
564                                         group : menuGroup,\r
565                                         onClick : function()\r
566                                         {\r
567                                                 openPage = 'options';\r
568                                                 editor.openDialog( commandName );\r
569                                         }\r
570                                 };\r
571 \r
572                         if ( uiTabs[1] == 1 )\r
573                                 uiMenuItems.scaytLangs =\r
574                                 {\r
575                                         label : lang.langs,\r
576                                         group : menuGroup,\r
577                                         onClick : function()\r
578                                         {\r
579                                                 openPage = 'langs';\r
580                                                 editor.openDialog( commandName );\r
581                                         }\r
582                                 };\r
583                         if ( uiTabs[2] == 1 )\r
584                                 uiMenuItems.scaytDict =\r
585                                 {\r
586                                         label : lang.dictionariesTab,\r
587                                         group : menuGroup,\r
588                                         onClick : function()\r
589                                         {\r
590                                                 openPage = 'dictionaries';\r
591                                                 editor.openDialog( commandName );\r
592                                         }\r
593                                 };\r
594                         // always added\r
595                         uiMenuItems.scaytAbout =\r
596                                 {\r
597                                         label : editor.lang.scayt.about,\r
598                                         group : menuGroup,\r
599                                         onClick : function()\r
600                                         {\r
601                                                 openPage = 'about';\r
602                                                 editor.openDialog( commandName );\r
603                                         }\r
604                                 };\r
605 \r
606                         editor.addMenuItems( uiMenuItems );\r
607 \r
608                                 editor.ui.add( 'Scayt', CKEDITOR.UI_MENUBUTTON,\r
609                                         {\r
610                                                 label : lang.title,\r
611                                                 title : CKEDITOR.env.opera ? lang.opera_title : lang.title,\r
612                                                 className : 'cke_button_scayt',\r
613                                                 modes : { wysiwyg : 1 },\r
614                                                 onRender: function()\r
615                                                 {\r
616                                                         command.on( 'state', function()\r
617                                                         {\r
618                                                                 this.setState( command.state );\r
619                                                         },\r
620                                                         this);\r
621                                                 },\r
622                                                 onMenu : function()\r
623                                                 {\r
624                                                         var isEnabled = plugin.isScaytEnabled( editor );\r
625 \r
626                                                         editor.getMenuItem( 'scaytToggle' ).label = lang[ isEnabled ? 'disable' : 'enable' ];\r
627 \r
628                                                         var uiTabs = plugin.getUiTabs( editor );\r
629 \r
630                                                         return {\r
631                                                                 scaytToggle  : CKEDITOR.TRISTATE_OFF,\r
632                                                                 scaytOptions : isEnabled && uiTabs[0] ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,\r
633                                                                 scaytLangs   : isEnabled && uiTabs[1] ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,\r
634                                                                 scaytDict    : isEnabled && uiTabs[2] ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,\r
635                                                                 scaytAbout   : isEnabled && uiTabs[3] ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED\r
636                                                         };\r
637                                                 }\r
638                                         });\r
639 \r
640                         // If the "contextmenu" plugin is loaded, register the listeners.\r
641                         if ( editor.contextMenu && editor.addMenuItems )\r
642                         {\r
643                                 editor.contextMenu.addListener( function( element, selection )\r
644                                         {\r
645                                                 if ( !plugin.isScaytEnabled( editor )\r
646                                                                 || selection.getRanges()[ 0 ].checkReadOnly() )\r
647                                                         return null;\r
648 \r
649                                                 var scayt_control = plugin.getScayt( editor ),\r
650                                                         node = scayt_control.getScaytNode();\r
651 \r
652                                                 if ( !node )\r
653                                                         return null;\r
654 \r
655                                                         var word = scayt_control.getWord( node );\r
656 \r
657                                                 if ( !word )\r
658                                                         return null;\r
659 \r
660                                                 var sLang = scayt_control.getLang(),\r
661                                                         _r = {},\r
662                                                         items_suggestion = window.scayt.getSuggestion( word, sLang );\r
663                                                 if ( !items_suggestion || !items_suggestion.length )\r
664                                                         return null;\r
665                                                 // Remove unused commands and menuitems\r
666                                                 for ( i in moreSuggestions )\r
667                                                 {\r
668                                                         delete editor._.menuItems[ i ];\r
669                                                         delete editor._.commands[ i ];\r
670                                                 }\r
671                                                 for ( i in mainSuggestions )\r
672                                                 {\r
673                                                         delete editor._.menuItems[ i ];\r
674                                                         delete editor._.commands[ i ];\r
675                                                 }\r
676                                                 moreSuggestions = {};           // Reset items.\r
677                                                 mainSuggestions = {};\r
678 \r
679                                                 var moreSuggestionsUnable = editor.config.scayt_moreSuggestions || 'on';\r
680                                                 var moreSuggestionsUnableAdded = false;\r
681 \r
682                                                 var maxSuggestions = editor.config.scayt_maxSuggestions;\r
683                                                 ( typeof maxSuggestions != 'number' ) && ( maxSuggestions = 5 );\r
684                                                 !maxSuggestions && ( maxSuggestions = items_suggestion.length );\r
685 \r
686                                                 var contextCommands = editor.config.scayt_contextCommands || 'all';\r
687                                                 contextCommands = contextCommands.split( '|' );\r
688 \r
689                                                 for ( var i = 0, l = items_suggestion.length; i < l; i += 1 )\r
690                                                 {\r
691                                                         var commandName = 'scayt_suggestion_' + items_suggestion[i].replace( ' ', '_' );\r
692                                                         var exec = ( function( el, s )\r
693                                                                 {\r
694                                                                         return {\r
695                                                                                 exec: function()\r
696                                                                                 {\r
697                                                                                         scayt_control.replace( el, s );\r
698                                                                                 }\r
699                                                                         };\r
700                                                                 })( node, items_suggestion[i] );\r
701 \r
702                                                         if ( i < maxSuggestions )\r
703                                                         {\r
704                                                                 addButtonCommand( editor, 'button_' + commandName, items_suggestion[i],\r
705                                                                         commandName, exec, 'scayt_suggest', i + 1 );\r
706                                                                 _r[ commandName ] = CKEDITOR.TRISTATE_OFF;\r
707                                                                 mainSuggestions[ commandName ] = CKEDITOR.TRISTATE_OFF;\r
708                                                         }\r
709                                                         else if ( moreSuggestionsUnable == 'on' )\r
710                                                         {\r
711                                                                 addButtonCommand( editor, 'button_' + commandName, items_suggestion[i],\r
712                                                                         commandName, exec, 'scayt_moresuggest', i + 1 );\r
713                                                                 moreSuggestions[ commandName ] = CKEDITOR.TRISTATE_OFF;\r
714                                                                 moreSuggestionsUnableAdded = true;\r
715                                                         }\r
716                                                 }\r
717 \r
718                                                 if ( moreSuggestionsUnableAdded )\r
719                                                 {\r
720                                                         // Register the More suggestions group;\r
721                                                         editor.addMenuItem( 'scayt_moresuggest',\r
722                                                         {\r
723                                                                 label : lang.moreSuggestions,\r
724                                                                 group : 'scayt_moresuggest',\r
725                                                                 order : 10,\r
726                                                                 getItems : function()\r
727                                                                 {\r
728                                                                         return moreSuggestions;\r
729                                                                 }\r
730                                                         });\r
731                                                         mainSuggestions[ 'scayt_moresuggest' ] = CKEDITOR.TRISTATE_OFF;\r
732                                                 }\r
733 \r
734                                                 if ( in_array( 'all', contextCommands )  || in_array( 'ignore', contextCommands)  )\r
735                                                 {\r
736                                                         var ignore_command = {\r
737                                                                 exec: function(){\r
738                                                                         scayt_control.ignore( node );\r
739                                                                 }\r
740                                                         };\r
741                                                         addButtonCommand( editor, 'ignore', lang.ignore, 'scayt_ignore', ignore_command, 'scayt_control', 1 );\r
742                                                         mainSuggestions[ 'scayt_ignore' ] = CKEDITOR.TRISTATE_OFF;\r
743                                                 }\r
744 \r
745                                                 if ( in_array( 'all', contextCommands )  || in_array( 'ignoreall', contextCommands ) )\r
746                                                 {\r
747                                                         var ignore_all_command = {\r
748                                                                 exec: function(){\r
749                                                                         scayt_control.ignoreAll( node );\r
750                                                                 }\r
751                                                         };\r
752                                                         addButtonCommand(editor, 'ignore_all', lang.ignoreAll, 'scayt_ignore_all', ignore_all_command, 'scayt_control', 2);\r
753                                                         mainSuggestions['scayt_ignore_all'] = CKEDITOR.TRISTATE_OFF;\r
754                                                 }\r
755 \r
756                                                 if ( in_array( 'all', contextCommands )  || in_array( 'add', contextCommands ) )\r
757                                                 {\r
758                                                         var addword_command = {\r
759                                                                 exec: function(){\r
760                                                                         window.scayt.addWordToUserDictionary( node );\r
761                                                                 }\r
762                                                         };\r
763                                                         addButtonCommand(editor, 'add_word', lang.addWord, 'scayt_add_word', addword_command, 'scayt_control', 3);\r
764                                                         mainSuggestions['scayt_add_word'] = CKEDITOR.TRISTATE_OFF;\r
765                                                 }\r
766 \r
767                                                 if ( scayt_control.fireOnContextMenu )\r
768                                                         scayt_control.fireOnContextMenu( editor );\r
769 \r
770                                                 return mainSuggestions;\r
771                                         });\r
772                         }\r
773 \r
774                         var showInitialState = function()\r
775                                 {\r
776                                         editor.removeListener( 'showScaytState', showInitialState );\r
777 \r
778                                         if ( !CKEDITOR.env.opera && !CKEDITOR.env.air )\r
779                                                 command.setState( plugin.isScaytEnabled( editor ) ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF );\r
780                                         else\r
781                                                 command.setState( CKEDITOR.TRISTATE_DISABLED );\r
782                                 };\r
783 \r
784                         editor.on( 'showScaytState', showInitialState );\r
785 \r
786                         if ( CKEDITOR.env.opera || CKEDITOR.env.air )\r
787                         {\r
788                                 editor.on( 'instanceReady', function()\r
789                                 {\r
790                                         showInitialState();\r
791                                 });\r
792                         }\r
793 \r
794                         // Start plugin\r
795                         if ( editor.config.scayt_autoStartup )\r
796                         {\r
797                                 editor.on( 'instanceReady', function()\r
798                                 {\r
799                                         plugin.loadEngine( editor );\r
800                                 });\r
801                         }\r
802                 },\r
803 \r
804                 afterInit : function( editor )\r
805                 {\r
806                         // Prevent word marker line from displaying in elements path and been removed when cleaning format. (#3570) (#4125)\r
807                         var elementsPathFilters,\r
808                                         scaytFilter = function( element )\r
809                                         {\r
810                                                 if ( element.hasAttribute( 'data-scaytid' ) )\r
811                                                         return false;\r
812                                         };\r
813 \r
814                         if ( editor._.elementsPath && ( elementsPathFilters = editor._.elementsPath.filters ) )\r
815                                 elementsPathFilters.push( scaytFilter );\r
816 \r
817                         editor.addRemoveFormatFilter && editor.addRemoveFormatFilter( scaytFilter );\r
818 \r
819                 }\r
820         });\r
821 })();\r
822 \r
823 /**\r
824  * If enabled (true), turns on SCAYT automatically after loading the editor.\r
825  * @name CKEDITOR.config.scayt_autoStartup\r
826  * @type Boolean\r
827  * @default false\r
828  * @example\r
829  * config.scayt_autoStartup = true;\r
830  */\r
831 \r
832 /**\r
833  * Defines the number of SCAYT suggestions to show in the main context menu.\r
834  * The possible values are:\r
835  * <ul>\r
836  *      <li>0 (zero): All suggestions are displayed in the main context menu.</li>\r
837  *      <li>Positive number: The maximum number of suggestions to shown in context\r
838  *              menu. Other entries will be shown in "More Suggestions" sub-menu.</li>\r
839  *      <li>Negative number: No suggestions are shown in the main context menu. All\r
840  *              entries will be listed in the "Suggestions" sub-menu.</li>\r
841  * </ul>\r
842  * @name CKEDITOR.config.scayt_maxSuggestions\r
843  * @type Number\r
844  * @default 5\r
845  * @example\r
846  * // Display only three suggestions in the main context menu.\r
847  * config.scayt_maxSuggestions = 3;\r
848  * @example\r
849  * // Do not show the suggestions directly.\r
850  * config.scayt_maxSuggestions = -1;\r
851  */\r
852 \r
853 /**\r
854  * Sets the customer ID for SCAYT. Required for migration from free version\r
855  * with banner to paid version.\r
856  * @name CKEDITOR.config.scayt_customerid\r
857  * @type String\r
858  * @default ''\r
859  * @example\r
860  * // Load SCAYT using my customer ID.\r
861  * config.scayt_customerid  = 'your-encrypted-customer-id';\r
862  */\r
863 \r
864 /**\r
865  * Enables/disables the "More Suggestions" sub-menu in the context menu.\r
866  * The possible values are "on" or "off".\r
867  * @name CKEDITOR.config.scayt_moreSuggestions\r
868  * @type String\r
869  * @default 'on'\r
870  * @example\r
871  * // Disables the "More Suggestions" sub-menu.\r
872  * config.scayt_moreSuggestions = 'off';\r
873  */\r
874 \r
875 /**\r
876  * Customizes the display of SCAYT context menu commands ("Add Word", "Ignore"\r
877  * and "Ignore All"). It must be a string with one or more of the following\r
878  * words separated by a pipe ("|"):\r
879  * <ul>\r
880  *      <li>"off": disables all options.</li>\r
881  *      <li>"all": enables all options.</li>\r
882  *      <li>"ignore": enables the "Ignore" option.</li>\r
883  *      <li>"ignoreall": enables the "Ignore All" option.</li>\r
884  *      <li>"add": enables the "Add Word" option.</li>\r
885  * </ul>\r
886  * @name CKEDITOR.config.scayt_contextCommands\r
887  * @type String\r
888  * @default 'all'\r
889  * @example\r
890  * // Show only "Add Word" and "Ignore All" in the context menu.\r
891  * config.scayt_contextCommands = 'add|ignoreall';\r
892  */\r
893 \r
894 /**\r
895  * Sets the default spellchecking language for SCAYT.\r
896  * @name CKEDITOR.config.scayt_sLang\r
897  * @type String\r
898  * @default 'en_US'\r
899  * @example\r
900  * // Sets SCAYT to German.\r
901  * config.scayt_sLang = 'de_DE';\r
902  */\r
903 \r
904 /**\r
905  * Sets the visibility of the SCAYT tabs in the settings dialog and toolbar\r
906  * button. The value must contain a "1" (enabled) or "0" (disabled) number for\r
907  * each of the following entries, in this precise order, separated by a\r
908  * comma (","): "Options", "Languages" and "Dictionary".\r
909  * @name CKEDITOR.config.scayt_uiTabs\r
910  * @type String\r
911  * @default '1,1,1'\r
912  * @example\r
913  * // Hide the "Languages" tab.\r
914  * config.scayt_uiTabs = '1,0,1';\r
915  */\r
916 \r
917 \r
918 /**\r
919  * Set the URL to SCAYT core. Required to switch to licensed version of SCAYT application.\r
920  * Further details at http://wiki.spellchecker.net/doku.php?id=3rd:wysiwyg:fckeditor:wscckf3l .\r
921  * @name CKEDITOR.config.scayt_srcUrl\r
922  * @type String\r
923  * @default ''\r
924  * @example\r
925  * config.scayt_srcUrl = "http://my-host/spellcheck/lf/scayt/scayt.js";\r
926  */\r
927 \r
928 /**\r
929  * Links SCAYT to custom dictionaries. It's a string containing dictionary ids\r
930  * separared by commas (","). Available only for licensed version.\r
931  * Further details at http://wiki.spellchecker.net/doku.php?id=custom_dictionary_support .\r
932  * @name CKEDITOR.config.scayt_customDictionaryIds\r
933  * @type String\r
934  * @default ''\r
935  * @example\r
936  * config.scayt_customDictionaryIds = '3021,3456,3478"';\r
937  */\r
938 \r
939 /**\r
940  * Makes it possible to activate a custom dictionary on SCAYT. The user\r
941  * dictionary name must be used. Available only for licensed version.\r
942  * @name CKEDITOR.config.scayt_userDictionaryName\r
943  * @type String\r
944  * @default ''\r
945  * @example\r
946  * config.scayt_userDictionaryName = 'MyDictionary';\r
947  */\r
948 \r
949 /**\r
950  * Define order of placing of SCAYT context menu items by groups.\r
951  * It must be a string with one or more of the following\r
952  * words separated by a pipe ("|"):\r
953  * <ul>\r
954  *     <li>'suggest'     - main suggestion word list,</li>\r
955  *     <li>'moresuggest' - more suggestions word list,</li>\r
956  *     <li>'control'     - SCAYT commands, such as 'Ignore' and 'Add Word'</li>\r
957  * </ul>\r
958  *\r
959  * @name CKEDITOR.config.scayt_contextMenuItemsOrder\r
960  * @type String\r
961  * @default 'suggest|moresuggest|control'\r
962  * @example\r
963  * config.scayt_contextMenuItemsOrder = 'moresuggest|control|suggest';\r
964  */\r