JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
84756ba157152fdf2f2b774cdca074fbe03e5381
[ckeditor.git] / _source / plugins / scayt / plugin.js
1 /*\r
2 Copyright (c) 2003-2013, 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                         if( plugin.instances[ editor.name ] )\r
39                                 plugin.instances[ editor.name ].destroy();\r
40 \r
41                         var config = editor.config;\r
42                         // Initialise Scayt instance.\r
43                         var oParams = {};\r
44                         // Get the iframe.\r
45                         oParams.srcNodeRef = editor.document.getWindow().$.frameElement;\r
46                         // syntax : AppName.AppVersion@AppRevision\r
47                         oParams.assocApp  = 'CKEDITOR.' + CKEDITOR.version + '@' + CKEDITOR.revision;\r
48                         oParams.customerid = config.scayt_customerid  || '1:WvF0D4-UtPqN1-43nkD4-NKvUm2-daQqk3-LmNiI-z7Ysb4-mwry24-T8YrS3-Q2tpq2';\r
49                         oParams.customDictionaryIds = config.scayt_customDictionaryIds || '';\r
50                         oParams.userDictionaryName = config.scayt_userDictionaryName || '';\r
51                         oParams.sLang = config.scayt_sLang || 'en_US';\r
52 \r
53                         // Introduce SCAYT onLoad callback. (#5632)\r
54                         oParams.onLoad = function()\r
55                                 {\r
56                                         // Draw down word marker to avoid being covered by background-color style.(#5466)\r
57                                         if ( !( CKEDITOR.env.ie && CKEDITOR.env.version < 8 ) )\r
58                                                 this.addStyle( this.selectorCss(), 'padding-bottom: 2px !important;' );\r
59 \r
60                                         // Call scayt_control.focus when SCAYT loaded\r
61                                         // and only if editor has focus and scayt control creates at first time (#5720)\r
62                                         if ( editor.focusManager.hasFocus && !plugin.isControlRestored( editor ) )\r
63                                                 this.focus();\r
64 \r
65                                 };\r
66 \r
67                         oParams.onBeforeChange = function()\r
68                         {\r
69                                 if ( plugin.getScayt( editor ) && !editor.checkDirty() )\r
70                                         setTimeout( function(){ editor.resetDirty(); }, 0 );\r
71                         };\r
72 \r
73                         var scayt_custom_params = window.scayt_custom_params;\r
74                         if ( typeof scayt_custom_params == 'object' )\r
75                         {\r
76                                 for ( var k in scayt_custom_params )\r
77                                         oParams[ k ] = scayt_custom_params[ k ];\r
78                         }\r
79                         // needs for restoring a specific scayt control settings\r
80                         if ( plugin.getControlId( editor ) )\r
81                                 oParams.id = plugin.getControlId( editor );\r
82 \r
83                         var scayt_control = new window.scayt( oParams );\r
84                         scayt_control.afterMarkupRemove.push( function( node )\r
85                         {\r
86                                 ( new CKEDITOR.dom.element( node, scayt_control.document ) ).mergeSiblings();\r
87                         } );\r
88 \r
89                         // Copy config.\r
90                         var lastInstance = plugin.instances[ editor.name ];\r
91                         if ( lastInstance )\r
92                         {\r
93                                 scayt_control.sLang = lastInstance.sLang;\r
94                                 scayt_control.option( lastInstance.option() );\r
95                                 scayt_control.paused = lastInstance.paused;\r
96                         }\r
97 \r
98                         plugin.instances[ editor.name ] = scayt_control;\r
99 \r
100                         try {\r
101                                 scayt_control.setDisabled( plugin.isPaused( editor ) === false );\r
102                         } catch (e) {}\r
103 \r
104                         editor.fire( 'showScaytState' );\r
105                 };\r
106 \r
107                 editor.on( 'contentDom', function(ev)\r
108                         {\r
109                                 createInstance();\r
110                         });\r
111                 editor.on( 'contentDomUnload', function()\r
112                         {\r
113                                 // Remove scripts.\r
114                                 var scripts = CKEDITOR.document.getElementsByTag( 'script' ),\r
115                                         scaytIdRegex =  /^dojoIoScript(\d+)$/i,\r
116                                         scaytSrcRegex =  /^https?:\/\/svc\.webspellchecker\.net\/spellcheck\/script\/ssrv\.cgi/i;\r
117 \r
118                                 for ( var i=0; i < scripts.count(); i++ )\r
119                                 {\r
120                                         var script = scripts.getItem( i ),\r
121                                                 id = script.getId(),\r
122                                                 src = script.getAttribute( 'src' );\r
123 \r
124                                         if ( id && src && id.match( scaytIdRegex ) && src.match( scaytSrcRegex ))\r
125                                                 script.remove();\r
126                                 }\r
127                         });\r
128 \r
129                 editor.on( 'beforeCommandExec', function( ev )          // Disable SCAYT before Source command execution.\r
130                         {\r
131                                 if ( ev.data.name == 'source'  && editor.mode == 'source' )\r
132                                         plugin.markControlRestore( editor );\r
133                         });\r
134 \r
135                 editor.on( 'afterCommandExec', function( ev )\r
136                         {\r
137                                 if ( !plugin.isScaytEnabled( editor ) )\r
138                                         return;\r
139 \r
140                                 if ( editor.mode == 'wysiwyg' && ( ev.data.name == 'undo' || ev.data.name == 'redo' ) )\r
141                                         window.setTimeout( function() { plugin.getScayt( editor ).refresh(); }, 10 );\r
142                         });\r
143                 editor.on( 'destroy', function( ev )\r
144                         {\r
145                                 var editor = ev.editor,\r
146                                         scayt_instance = plugin.getScayt( editor );\r
147 \r
148                                 // SCAYT instance might already get destroyed by mode switch (#5744).\r
149                                 if ( !scayt_instance )\r
150                                         return;\r
151 \r
152                                 delete plugin.instances[ editor.name ];\r
153                                 // store a control id for restore a specific scayt control settings\r
154                                 plugin.setControlId( editor, scayt_instance.id );\r
155                                 scayt_instance.destroy( true );\r
156                         });\r
157                 //#9439 after SetData method fires contentDom event and SCAYT create additional instanse\r
158                 // This way we should destroy SCAYT on setData event when contenteditable Iframe was re-created\r
159                 editor.on( 'setData', function( ev )\r
160                         {\r
161                                 var scayt_instance = plugin.getScayt( editor );\r
162                                         if ( scayt_instance )\r
163                                         {\r
164                                                 plugin.setPaused( editor, !scayt_instance.disabled );\r
165                                                 // store a control id for restore a specific scayt control settings\r
166                                                 plugin.setControlId( editor, scayt_instance.id );\r
167                                                 scayt_instance.destroy( true );\r
168                                                 delete plugin.instances[ editor.name ];\r
169                                         }\r
170                         });\r
171 \r
172                 // Reload spell-checking for current word after insertion completed.\r
173                 editor.on( 'insertElement', function()\r
174                         {\r
175                                 var scayt_instance = plugin.getScayt( editor );\r
176                                 if ( plugin.isScaytEnabled( editor ) )\r
177                                 {\r
178                                         // Unlock the selection before reload, SCAYT will take\r
179                                         // care selection update.\r
180                                         if ( CKEDITOR.env.ie )\r
181                                                 editor.getSelection().unlock( true );\r
182 \r
183                                         // Return focus to the editor and refresh SCAYT markup (#5573).\r
184                                         window.setTimeout( function()\r
185                                         {\r
186                                                 scayt_instance.focus();\r
187                                                 scayt_instance.refresh();\r
188                                         }, 10 );\r
189                                 }\r
190                         }, this, null, 50 );\r
191 \r
192                 editor.on( 'insertHtml', function()\r
193                         {\r
194                                 var scayt_instance = plugin.getScayt( editor );\r
195                                 if ( plugin.isScaytEnabled( editor ) )\r
196                                 {\r
197                                         // Unlock the selection before reload, SCAYT will take\r
198                                         // care selection update.\r
199                                         if ( CKEDITOR.env.ie )\r
200                                                 editor.getSelection().unlock( true );\r
201 \r
202                                         // Return focus to the editor (#5573)\r
203                                         // Refresh SCAYT markup\r
204                                         window.setTimeout( function()\r
205                                         {\r
206                                                 scayt_instance.focus();\r
207                                                 scayt_instance.refresh();\r
208                                         }, 10 );\r
209                                 }\r
210                         }, this, null, 50 );\r
211 \r
212                 editor.on( 'scaytDialog', function( ev )        // Communication with dialog.\r
213                         {\r
214                                 ev.data.djConfig = window.djConfig;\r
215                                 ev.data.scayt_control = plugin.getScayt( editor );\r
216                                 ev.data.tab = openPage;\r
217                                 ev.data.scayt = window.scayt;\r
218                         });\r
219 \r
220                 var dataProcessor = editor.dataProcessor,\r
221                         htmlFilter = dataProcessor && dataProcessor.htmlFilter;\r
222 \r
223                 if ( htmlFilter )\r
224                 {\r
225                         htmlFilter.addRules(\r
226                                 {\r
227                                         elements :\r
228                                         {\r
229                                                 span : function( element )\r
230                                                 {\r
231                                                         if ( element.attributes[ 'data-scayt_word' ]\r
232                                                                         && element.attributes[ 'data-scaytid' ] )\r
233                                                         {\r
234                                                                 delete element.name;    // Write children, but don't write this node.\r
235                                                                 return element;\r
236                                                         }\r
237                                                 }\r
238                                         }\r
239                                 }\r
240                         );\r
241                 }\r
242 \r
243                 // Override Image.equals method avoid CK snapshot module to add SCAYT markup to snapshots. (#5546)\r
244                 var undoImagePrototype = CKEDITOR.plugins.undo.Image.prototype;\r
245                 undoImagePrototype.equals = CKEDITOR.tools.override( undoImagePrototype.equals, function( org )\r
246                 {\r
247                         return function( otherImage )\r
248                         {\r
249                                 var thisContents = this.contents,\r
250                                         otherContents = otherImage.contents;\r
251                                 var scayt_instance = plugin.getScayt( this.editor );\r
252                                 // Making the comparison based on content without SCAYT word markers.\r
253                                 if ( scayt_instance && plugin.isScaytReady( this.editor ) )\r
254                                 {\r
255                                         // scayt::reset might return value undefined. (#5742)\r
256                                         this.contents = scayt_instance.reset( thisContents ) || '';\r
257                                         otherImage.contents = scayt_instance.reset( otherContents ) || '';\r
258                                 }\r
259 \r
260                                 var retval = org.apply( this, arguments );\r
261 \r
262                                 this.contents = thisContents;\r
263                                 otherImage.contents = otherContents;\r
264                                 return retval;\r
265                         };\r
266                 });\r
267 \r
268                 if ( editor.document )\r
269                         createInstance();\r
270         };\r
271 \r
272 CKEDITOR.plugins.scayt =\r
273         {\r
274                 engineLoaded : false,\r
275                 instances : {},\r
276                 // Data storage for SCAYT control, based on editor instances\r
277                 controlInfo : {},\r
278                 setControlInfo : function( editor, o )\r
279                 {\r
280                         if ( editor && editor.name && typeof ( this.controlInfo[ editor.name ] ) != 'object' )\r
281                                 this.controlInfo[ editor.name ] = {};\r
282 \r
283                         for ( var infoOpt in o )\r
284                                 this.controlInfo[ editor.name ][ infoOpt ] = o[ infoOpt ];\r
285                 },\r
286                 isControlRestored : function( editor )\r
287                 {\r
288                         if ( editor &&\r
289                                         editor.name &&\r
290                                         this.controlInfo[ editor.name ] )\r
291                         {\r
292                                 return this.controlInfo[ editor.name ].restored ;\r
293                         }\r
294                         return false;\r
295                 },\r
296                 markControlRestore : function( editor )\r
297                 {\r
298                         this.setControlInfo( editor, { restored:true } );\r
299                 },\r
300                 setControlId: function( editor, id )\r
301                 {\r
302                         this.setControlInfo( editor, { id:id } );\r
303                 },\r
304                 getControlId: function( editor )\r
305                 {\r
306                         if ( editor &&\r
307                                         editor.name &&\r
308                                         this.controlInfo[ editor.name ] &&\r
309                                         this.controlInfo[ editor.name ].id )\r
310                         {\r
311                                 return this.controlInfo[ editor.name ].id;\r
312                         }\r
313                         return null;\r
314                 },\r
315                 setPaused: function( editor , bool )\r
316                 {\r
317                         this.setControlInfo( editor, { paused:bool } );\r
318                 },\r
319                 isPaused: function( editor )\r
320                 {\r
321                         if ( editor &&\r
322                                         editor.name &&\r
323                                         this.controlInfo[editor.name] )\r
324                         {\r
325                                 return this.controlInfo[editor.name].paused;\r
326                         }\r
327                         return undefined;\r
328                 },\r
329                 getScayt : function( editor )\r
330                 {\r
331                         return this.instances[ editor.name ];\r
332                 },\r
333                 isScaytReady : function( editor )\r
334                 {\r
335                         return this.engineLoaded === true &&\r
336                                 'undefined' !== typeof window.scayt && this.getScayt( editor );\r
337                 },\r
338                 isScaytEnabled : function( editor )\r
339                 {\r
340                         var scayt_instance = this.getScayt( editor );\r
341                         return ( scayt_instance ) ? scayt_instance.disabled === false : false;\r
342                 },\r
343                 getUiTabs : function( editor )\r
344                 {\r
345                         var uiTabs = [];\r
346 \r
347                         // read UI tabs value from config\r
348                         var configUiTabs = editor.config.scayt_uiTabs || "1,1,1";\r
349 \r
350                         // convert string to array\r
351                         configUiTabs = configUiTabs.split( ',' );\r
352 \r
353                         // "About us" should be always shown for standard config\r
354                         configUiTabs[3] = "1";\r
355 \r
356                         for ( var i = 0; i < 4; i++ ) {\r
357                                 uiTabs[i] = (typeof window.scayt != "undefined" && typeof window.scayt.uiTags != "undefined")\r
358                                                                 ? (parseInt(configUiTabs[i],10) && window.scayt.uiTags[i])\r
359                                                                 : parseInt(configUiTabs[i],10);\r
360                         }\r
361                         return uiTabs;\r
362                 },\r
363                 loadEngine : function( editor )\r
364                 {\r
365                         // SCAYT doesn't work with Firefox2, Opera and AIR.\r
366                         if ( CKEDITOR.env.gecko && CKEDITOR.env.version < 10900 || CKEDITOR.env.opera || CKEDITOR.env.air )\r
367                                 return editor.fire( 'showScaytState' );\r
368 \r
369                         if ( this.engineLoaded === true )\r
370                         {\r
371                                 return onEngineLoad.apply( editor );\r
372                         }\r
373                                 // Add new instance.\r
374                         else if ( this.engineLoaded == -1 )\r
375                         {       // We are waiting.\r
376                                 return CKEDITOR.on( 'scaytReady', function(){ onEngineLoad.apply( editor ); } );        // Use function(){} to avoid rejection as duplicate.\r
377                         }\r
378 \r
379                         CKEDITOR.on( 'scaytReady', onEngineLoad, editor );\r
380                         CKEDITOR.on( 'scaytReady', function()\r
381                                 {\r
382                                         this.engineLoaded = true;\r
383                                 },\r
384                                 this,\r
385                                 null,\r
386                                 0\r
387                         );      // First to run.\r
388 \r
389                         this.engineLoaded = -1; // Loading in progress.\r
390 \r
391                         // compose scayt url\r
392                         var protocol = document.location.protocol;\r
393                         // Default to 'http' for unknown.\r
394                         protocol = protocol.search( /https?:/) != -1? protocol : 'http:';\r
395                         var baseUrl  = 'svc.webspellchecker.net/scayt26/loader__base.js';\r
396 \r
397                         var scaytUrl  =  editor.config.scayt_srcUrl || ( protocol + '//' + baseUrl );\r
398                         var scaytConfigBaseUrl =  plugin.parseUrl( scaytUrl ).path +  '/';\r
399 \r
400                         if( window.scayt == undefined )\r
401                         {\r
402                                 CKEDITOR._djScaytConfig =\r
403                                 {\r
404                                         baseUrl: scaytConfigBaseUrl,\r
405                                         addOnLoad:\r
406                                         [\r
407                                                 function()\r
408                                                 {\r
409                                                         CKEDITOR.fireOnce( 'scaytReady' );\r
410                                                 }\r
411                                         ],\r
412                                         isDebug: false\r
413                                 };\r
414                                 // Append javascript code.\r
415                                 CKEDITOR.document.getHead().append(\r
416                                         CKEDITOR.document.createElement( 'script',\r
417                                                 {\r
418                                                         attributes :\r
419                                                                 {\r
420                                                                         type : 'text/javascript',\r
421                                                                         async : 'true',\r
422                                                                         src : scaytUrl\r
423                                                                 }\r
424                                                 })\r
425                                 );\r
426                         }\r
427                         else\r
428                         {\r
429                                 CKEDITOR.fireOnce( 'scaytReady' );\r
430                         }\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                                                         items_suggestion = window.scayt.getSuggestion( word, sLang );\r
662                                                 // Remove unused commands and menuitems\r
663                                                 for ( var m in moreSuggestions )\r
664                                                 {\r
665                                                         delete editor._.menuItems[ m ];\r
666                                                         delete editor._.commands[ m ];\r
667                                                 }\r
668                                                 for ( m in mainSuggestions )\r
669                                                 {\r
670                                                         delete editor._.menuItems[ m ];\r
671                                                         delete editor._.commands[ m ];\r
672                                                 }\r
673                                                 moreSuggestions = {};           // Reset items.\r
674                                                 mainSuggestions = {};\r
675 \r
676                                                 var moreSuggestionsUnable = editor.config.scayt_moreSuggestions || 'on';\r
677                                                 var moreSuggestionsUnableAdded = false;\r
678 \r
679                                                 var maxSuggestions = editor.config.scayt_maxSuggestions;\r
680                                                 ( typeof maxSuggestions != 'number' ) && ( maxSuggestions = 5 );\r
681                                                 !maxSuggestions && ( maxSuggestions = items_suggestion.length );\r
682 \r
683                                                 var contextCommands = editor.config.scayt_contextCommands || 'all';\r
684                                                 contextCommands = contextCommands.split( '|' );\r
685 \r
686                                                 if ( items_suggestion && items_suggestion.length ) {\r
687                                                         for ( var i = 0, l = items_suggestion.length; i < l; i += 1 )\r
688                                                         {\r
689                                                                 var commandName = 'scayt_suggestion_' + items_suggestion[i].replace( ' ', '_' );\r
690                                                                 var exec = ( function( el, s )\r
691                                                                         {\r
692                                                                                 return {\r
693                                                                                         exec: function()\r
694                                                                                         {\r
695                                                                                                 scayt_control.replace(el, s);\r
696                                                                                         }\r
697                                                                                 };\r
698                                                                         })( node, items_suggestion[i] );\r
699 \r
700                                                                 if ( i < maxSuggestions )\r
701                                                                 {\r
702                                                                         addButtonCommand( editor, 'button_' + commandName, items_suggestion[i],\r
703                                                                                 commandName, exec, 'scayt_suggest', i + 1 );\r
704                                                                         mainSuggestions[ commandName ] = CKEDITOR.TRISTATE_OFF;\r
705                                                                 }\r
706                                                                 else if ( moreSuggestionsUnable == 'on' )\r
707                                                                 {\r
708                                                                         addButtonCommand( editor, 'button_' + commandName, items_suggestion[i],\r
709                                                                                 commandName, exec, 'scayt_moresuggest', i + 1 );\r
710                                                                         moreSuggestions[ commandName ] = CKEDITOR.TRISTATE_OFF;\r
711                                                                         moreSuggestionsUnableAdded = true;\r
712                                                                 }\r
713                                                         }\r
714 \r
715                                                         if ( moreSuggestionsUnableAdded )\r
716                                                         {\r
717                                                                 // Register the More suggestions group;\r
718                                                                 editor.addMenuItem( 'scayt_moresuggest',\r
719                                                                 {\r
720                                                                         label : editor.lang.scayt.moreSuggestions,\r
721                                                                         group : 'scayt_moresuggest',\r
722                                                                         order : 10,\r
723                                                                         getItems : function()\r
724                                                                         {\r
725                                                                                 return moreSuggestions;\r
726                                                                         }\r
727                                                                 });\r
728                                                                 mainSuggestions[ 'scayt_moresuggest' ] = CKEDITOR.TRISTATE_OFF;\r
729                                                         }\r
730                                                 }\r
731                                                 else {\r
732                                                         // "No suggestions" feature\r
733                                                         editor.addMenuItem( 'scayt_nosuggest',\r
734                                                         {\r
735                                                                 label : editor.lang.scayt.noSuggestions ? editor.lang.scayt.noSuggestions : (editor.lang.spellCheck.noSuggestions ? editor.lang.spellCheck.noSuggestions : 'No suggestions'),\r
736                                                                 group : 'scayt_suggest',\r
737                                                                 order : 1\r
738                                                         });\r
739                                                         mainSuggestions[ 'scayt_nosuggest' ] = CKEDITOR.TRISTATE_OFF;\r
740                                                 }\r
741 \r
742                                                 if ( in_array( 'all', contextCommands )  || in_array( 'ignore', contextCommands)  )\r
743                                                 {\r
744                                                         var ignore_command = {\r
745                                                                 exec: function(){\r
746                                                                         scayt_control.ignore( node );\r
747                                                                 }\r
748                                                         };\r
749                                                         addButtonCommand( editor, 'ignore', lang.ignore, 'scayt_ignore', ignore_command, 'scayt_control', 1 );\r
750                                                         mainSuggestions[ 'scayt_ignore' ] = CKEDITOR.TRISTATE_OFF;\r
751                                                 }\r
752 \r
753                                                 if ( in_array( 'all', contextCommands )  || in_array( 'ignoreall', contextCommands ) )\r
754                                                 {\r
755                                                         var ignore_all_command = {\r
756                                                                 exec: function(){\r
757                                                                         scayt_control.ignoreAll( node );\r
758                                                                 }\r
759                                                         };\r
760                                                         addButtonCommand(editor, 'ignore_all', lang.ignoreAll, 'scayt_ignore_all', ignore_all_command, 'scayt_control', 2);\r
761                                                         mainSuggestions['scayt_ignore_all'] = CKEDITOR.TRISTATE_OFF;\r
762                                                 }\r
763 \r
764                                                 if ( in_array( 'all', contextCommands )  || in_array( 'add', contextCommands ) )\r
765                                                 {\r
766                                                         var addword_command = {\r
767                                                                 exec: function(){\r
768                                                                         window.scayt.addWordToUserDictionary( node );\r
769                                                                 }\r
770                                                         };\r
771                                                         addButtonCommand(editor, 'add_word', lang.addWord, 'scayt_add_word', addword_command, 'scayt_control', 3);\r
772                                                         mainSuggestions['scayt_add_word'] = CKEDITOR.TRISTATE_OFF;\r
773                                                 }\r
774 \r
775                                                 if ( scayt_control.fireOnContextMenu )\r
776                                                         scayt_control.fireOnContextMenu( editor );\r
777 \r
778                                                 return mainSuggestions;\r
779                                         });\r
780                         }\r
781 \r
782                         var showInitialState = function()\r
783                                 {\r
784                                         editor.removeListener( 'showScaytState', showInitialState );\r
785 \r
786                                         if ( !CKEDITOR.env.opera && !CKEDITOR.env.air )\r
787                                                 command.setState( plugin.isScaytEnabled( editor ) ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF );\r
788                                         else\r
789                                                 command.setState( CKEDITOR.TRISTATE_DISABLED );\r
790                                 };\r
791 \r
792                         editor.on( 'showScaytState', showInitialState );\r
793 \r
794                         if ( CKEDITOR.env.opera || CKEDITOR.env.air )\r
795                         {\r
796                                 editor.on( 'instanceReady', function()\r
797                                 {\r
798                                         showInitialState();\r
799                                 });\r
800                         }\r
801 \r
802                         // Start plugin\r
803                         if ( editor.config.scayt_autoStartup )\r
804                         {\r
805                                 editor.on( 'instanceReady', function()\r
806                                 {\r
807                                         plugin.loadEngine( editor );\r
808                                 });\r
809                         }\r
810                 },\r
811 \r
812                 afterInit : function( editor )\r
813                 {\r
814                         // Prevent word marker line from displaying in elements path and been removed when cleaning format. (#3570) (#4125)\r
815                         var elementsPathFilters,\r
816                                         scaytFilter = function( element )\r
817                                         {\r
818                                                 if ( element.hasAttribute( 'data-scaytid' ) )\r
819                                                         return false;\r
820                                         };\r
821 \r
822                         if ( editor._.elementsPath && ( elementsPathFilters = editor._.elementsPath.filters ) )\r
823                                 elementsPathFilters.push( scaytFilter );\r
824 \r
825                         editor.addRemoveFormatFilter && editor.addRemoveFormatFilter( scaytFilter );\r
826 \r
827                 }\r
828         });\r
829 })();\r
830 \r
831 /**\r
832  * If enabled (set to <code>true</code>), turns on SCAYT automatically\r
833  * after loading the editor.\r
834  * @name CKEDITOR.config.scayt_autoStartup\r
835  * @type Boolean\r
836  * @default <code>false</code>\r
837  * @example\r
838  * config.scayt_autoStartup = true;\r
839  */\r
840 \r
841 /**\r
842  * Defines the number of SCAYT suggestions to show in the main context menu.\r
843  * Possible values are:\r
844  * <ul>\r
845  *      <li><code>0</code> (zero) &ndash; All suggestions are displayed in the main context menu.</li>\r
846  *      <li>Positive number &ndash; The maximum number of suggestions to show in the context\r
847  *              menu. Other entries will be shown in the "More Suggestions" sub-menu.</li>\r
848  *      <li>Negative number &ndash; No suggestions are shown in the main context menu. All\r
849  *              entries will be listed in the the "Suggestions" sub-menu.</li>\r
850  * </ul>\r
851  * @name CKEDITOR.config.scayt_maxSuggestions\r
852  * @type Number\r
853  * @default <code>5</code>\r
854  * @example\r
855  * // Display only three suggestions in the main context menu.\r
856  * config.scayt_maxSuggestions = 3;\r
857  * @example\r
858  * // Do not show the suggestions directly.\r
859  * config.scayt_maxSuggestions = -1;\r
860  */\r
861 \r
862 /**\r
863  * Sets the customer ID for SCAYT. Required for migration from free,\r
864  * ad-supported version to paid, ad-free version.\r
865  * @name CKEDITOR.config.scayt_customerid\r
866  * @type String\r
867  * @default <code>''</code>\r
868  * @example\r
869  * // Load SCAYT using my customer ID.\r
870  * config.scayt_customerid  = 'your-encrypted-customer-id';\r
871  */\r
872 \r
873 /**\r
874  * Enables/disables the "More Suggestions" sub-menu in the context menu.\r
875  * Possible values are <code>on</code> and <code>off</code>.\r
876  * @name CKEDITOR.config.scayt_moreSuggestions\r
877  * @type String\r
878  * @default <code>'on'</code>\r
879  * @example\r
880  * // Disables the "More Suggestions" sub-menu.\r
881  * config.scayt_moreSuggestions = 'off';\r
882  */\r
883 \r
884 /**\r
885  * Customizes the display of SCAYT context menu commands ("Add Word", "Ignore"\r
886  * and "Ignore All"). This must be a string with one or more of the following\r
887  * words separated by a pipe character ("|"):\r
888  * <ul>\r
889  *      <li><code>off</code> &ndash; disables all options.</li>\r
890  *      <li><code>all</code> &ndash; enables all options.</li>\r
891  *      <li><code>ignore</code> &ndash; enables the "Ignore" option.</li>\r
892  *      <li><code>ignoreall</code> &ndash; enables the "Ignore All" option.</li>\r
893  *      <li><code>add</code> &ndash; enables the "Add Word" option.</li>\r
894  * </ul>\r
895  * @name CKEDITOR.config.scayt_contextCommands\r
896  * @type String\r
897  * @default <code>'all'</code>\r
898  * @example\r
899  * // Show only "Add Word" and "Ignore All" in the context menu.\r
900  * config.scayt_contextCommands = 'add|ignoreall';\r
901  */\r
902 \r
903 /**\r
904  * Sets the default spell checking language for SCAYT. Possible values are:\r
905  * <code>en_US</code>, <code>en_GB</code>, <code>pt_BR</code>, <code>da_DK</code>,\r
906  * <code>nl_NL</code>, <code>en_CA</code>, <code>fi_FI</code>, <code>fr_FR</code>,\r
907  * <code>fr_CA</code>, <code>de_DE</code>, <code>el_GR</code>, <code>it_IT</code>,\r
908  * <code>nb_NO</code>, <code>pt_PT</code>, <code>es_ES</code>, <code>sv_SE</code>.\r
909  * @name CKEDITOR.config.scayt_sLang\r
910  * @type String\r
911  * @default <code>'en_US'</code>\r
912  * @example\r
913  * // Sets SCAYT to German.\r
914  * config.scayt_sLang = 'de_DE';\r
915  */\r
916 \r
917 /**\r
918  * Sets the visibility of particular tabs in the SCAYT dialog window and toolbar\r
919  * button. This setting must contain a <code>1</code> (enabled) or <code>0</code>\r
920  * (disabled) value for each of the following entries, in this precise order,\r
921  * separated by a comma (","): "Options", "Languages", and "Dictionary".\r
922  * @name CKEDITOR.config.scayt_uiTabs\r
923  * @type String\r
924  * @default <code>'1,1,1'</code>\r
925  * @example\r
926  * // Hides the "Languages" tab.\r
927  * config.scayt_uiTabs = '1,0,1';\r
928  */\r
929 \r
930 \r
931 /**\r
932  * Sets the URL to SCAYT core. Required to switch to the licensed version of SCAYT application.\r
933  * Further details available at\r
934  * <a href="http://wiki.webspellchecker.net/doku.php?id=migration:hosredfreetolicensedck">\r
935  * http://wiki.webspellchecker.net/doku.php?id=migration:hosredfreetolicensedck</a>.\r
936  * @name CKEDITOR.config.scayt_srcUrl\r
937  * @type String\r
938  * @default <code>''</code>\r
939  * @example\r
940  * config.scayt_srcUrl = "http://my-host/spellcheck/lf/scayt/scayt.js";\r
941  */\r
942 \r
943 /**\r
944  * Links SCAYT to custom dictionaries. This is a string containing dictionary IDs\r
945  * separared by commas (","). Available only for the licensed version.\r
946  * Further details at\r
947  * <a href="http://wiki.webspellchecker.net/doku.php?id=installationandconfiguration:customdictionaries:licensed">\r
948  * http://wiki.webspellchecker.net/doku.php?id=installationandconfiguration:customdictionaries:licensed</a>.\r
949  * @name CKEDITOR.config.scayt_customDictionaryIds\r
950  * @type String\r
951  * @default <code>''</code>\r
952  * @example\r
953  * config.scayt_customDictionaryIds = '3021,3456,3478"';\r
954  */\r
955 \r
956 /**\r
957  * Makes it possible to activate a custom dictionary in SCAYT. The user\r
958  * dictionary name must be used. Available only for the licensed version.\r
959  * @name CKEDITOR.config.scayt_userDictionaryName\r
960  * @type String\r
961  * @default <code>''</code>\r
962  * @example\r
963  * config.scayt_userDictionaryName = 'MyDictionary';\r
964  */\r
965 \r
966 /**\r
967  * Defines the order SCAYT context menu items by groups.\r
968  * This must be a string with one or more of the following\r
969  * words separated by a pipe character ("|"):\r
970  * <ul>\r
971  *     <li><code>suggest</code> &ndash; main suggestion word list,</li>\r
972  *     <li><code>moresuggest</code> &ndash; more suggestions word list,</li>\r
973  *     <li><code>control</code> &ndash; SCAYT commands, such as "Ignore" and "Add Word".</li>\r
974  * </ul>\r
975  *\r
976  * @name CKEDITOR.config.scayt_contextMenuItemsOrder\r
977  * @type String\r
978  * @default <code>'suggest|moresuggest|control'</code>\r
979  * @example\r
980  * config.scayt_contextMenuItemsOrder = 'moresuggest|control|suggest';\r
981  */\r