JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
bb0df051e17dcb5ff1b0358aebf6495148fd6342
[ckeditor.git] / _source / plugins / scayt / plugin.js
1 /*\r
2 Copyright (c) 2003-2009, 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         var onEngineLoad = function()\r
17         {\r
18                 var editor = this;\r
19 \r
20                 var createInstance = function() // Create new instance every time Document is created.\r
21                 {\r
22                         // Initialise Scayt instance.\r
23                         var oParams = {};\r
24                         oParams.srcNodeRef = editor.document.getWindow().$.frameElement;                // Get the iframe.\r
25                         // syntax : AppName.AppVersion@AppRevision\r
26                         oParams.assocApp  = "CKEDITOR." + CKEDITOR.version + "@" + CKEDITOR.revision;\r
27 \r
28                         oParams.customerid = editor.config.scayt_customerid  || "1:11111111111111111111111111111111111111";\r
29                         oParams.customDictionaryName = editor.config.scayt_customDictionaryName;\r
30                         oParams.userDictionaryName = editor.config.scayt_userDictionaryName;\r
31                         oParams.defLang = editor.scayt_defLang;\r
32 \r
33                         if ( CKEDITOR._scaytParams )\r
34                         {\r
35                                 for ( var k in CKEDITOR._scaytParams )\r
36                                 {\r
37                                         oParams[ k ] = CKEDITOR._scaytParams[ k ];\r
38                                 }\r
39                         }\r
40 \r
41                         var scayt_control = new window.scayt( oParams );\r
42 \r
43                         // Copy config.\r
44                         var     lastInstance = plugin.instances[ editor.name ];\r
45                         if ( lastInstance )\r
46                         {\r
47                                 scayt_control.sLang = lastInstance.sLang;\r
48                                 scayt_control.option( lastInstance.option() );\r
49                                 scayt_control.paused = lastInstance.paused;\r
50                         }\r
51 \r
52                         plugin.instances[ editor.name ] = scayt_control;\r
53 \r
54                         try {\r
55                                 scayt_control.setDisabled( scayt_control.paused === false );                            // I really don't know why it causes JS error in IE\r
56                         } catch (e) {}\r
57                         editor.fire( 'showScaytState' );\r
58                 };\r
59 \r
60                 editor.on( 'contentDom', createInstance );\r
61                 editor.on( 'contentDomUnload', function()\r
62                         {\r
63                                 // Remove scripts.\r
64                                 var scripts = CKEDITOR.document.getElementsByTag( 'script' ),\r
65                                         scaytIdRegex =  /^dojoIoScript(\d+)$/i,\r
66                                         scaytSrcRegex =  /^https?:\/\/svc\.spellchecker\.net\/spellcheck\/script\/ssrv\.cgi/i;\r
67 \r
68                                 for ( var i=0; i < scripts.count(); i++ )\r
69                                 {\r
70                                         var script = scripts.getItem( i ),\r
71                                                 id = script.getId(),\r
72                                                 src = script.getAttribute( 'src' );\r
73 \r
74                                         if ( id && src && id.match( scaytIdRegex ) && src.match( scaytSrcRegex ))\r
75                                                 script.remove();\r
76                                 }\r
77                         });\r
78 \r
79                 editor.on( 'beforeCommandExec', function( ev )          // Disable SCAYT before Source command execution.\r
80                         {\r
81                                 if ( (ev.data.name == 'source' ||  ev.data.name == 'newpage') && editor.mode == 'wysiwyg' )\r
82                                 {\r
83                                         var scayt_instanse = plugin.getScayt( editor );\r
84                                         if ( scayt_instanse )\r
85                                         {\r
86                                                 scayt_instanse.paused = !scayt_instanse.disabled;\r
87                                                 scayt_instanse.destroy();\r
88                                                 delete plugin.instances[ editor.name ];\r
89                                         }\r
90                                 }\r
91                         });\r
92 \r
93                 // Listen to data manipulation to reflect scayt markup.\r
94                 editor.on( 'afterSetData', function()\r
95                         {\r
96                                 if ( plugin.isScaytEnabled( editor ) )\r
97                                         plugin.getScayt( editor ).refresh();\r
98                         });\r
99 \r
100                 // Reload spell-checking for current word after insertion completed.\r
101                 editor.on( 'insertElement', function()\r
102                         {\r
103                                 var scayt_instance = plugin.getScayt( editor );\r
104                                 if ( plugin.isScaytEnabled( editor ) )\r
105                                 {\r
106                                         // Unlock the selection before reload, SCAYT will take\r
107                                         // care selection update.\r
108                                         if ( CKEDITOR.env.ie )\r
109                                                 editor.getSelection().unlock( true );\r
110 \r
111                                         // Swallow any SCAYT engine errors.\r
112                                         try{\r
113                                                 scayt_instance.refresh();\r
114                                         }catch( er )\r
115                                         {}\r
116                                 }\r
117                         }, this, null, 50 );\r
118 \r
119                 editor.on( 'scaytDialog', function( ev )        // Communication with dialog.\r
120                         {\r
121                                 ev.data.djConfig = window.djConfig;\r
122                                 ev.data.scayt_control = plugin.getScayt( editor );\r
123                                 ev.data.tab = openPage;\r
124                                 ev.data.scayt = window.scayt;\r
125                         });\r
126 \r
127                 var dataProcessor = editor.dataProcessor,\r
128                         htmlFilter = dataProcessor && dataProcessor.htmlFilter;\r
129                 if ( htmlFilter )\r
130                 {\r
131                         htmlFilter.addRules(\r
132                                 {\r
133                                         elements :\r
134                                         {\r
135                                                 span : function( element )\r
136                                                 {\r
137                                                         if ( element.attributes.scayt_word && element.attributes.scaytid )\r
138                                                         {\r
139                                                                 delete element.name;    // Write children, but don't write this node.\r
140                                                                 return element;\r
141                                                         }\r
142                                                 }\r
143                                         }\r
144                                 }\r
145                         );\r
146                 }\r
147 \r
148                 if ( editor.document )\r
149                         createInstance();\r
150         };\r
151 \r
152         CKEDITOR.plugins.scayt =\r
153         {\r
154                 engineLoaded : false,\r
155                 instances : {},\r
156                 getScayt : function( editor )\r
157                 {\r
158                         return this.instances[ editor.name ];\r
159                 },\r
160                 isScaytReady : function( editor )\r
161                 {\r
162                         return this.engineLoaded === true &&\r
163                                 'undefined' !== typeof window.scayt && this.getScayt( editor );\r
164                 },\r
165                 isScaytEnabled : function( editor )\r
166                 {\r
167                         var scayt_instanse = this.getScayt( editor );\r
168                         return ( scayt_instanse ) ? scayt_instanse.disabled === false : false;\r
169                 },\r
170                 loadEngine : function( editor )\r
171                 {\r
172                         if ( this.engineLoaded === true )\r
173                                 return onEngineLoad.apply( editor );    // Add new instance.\r
174                         else if ( this.engineLoaded == -1 )                     // We are waiting.\r
175                                 return CKEDITOR.on( 'scaytReady', function(){ onEngineLoad.apply( editor );} ); // Use function(){} to avoid rejection as duplicate.\r
176 \r
177                         CKEDITOR.on( 'scaytReady', onEngineLoad, editor );\r
178                         CKEDITOR.on( 'scaytReady', function()\r
179                                 {\r
180                                         this.engineLoaded = true;\r
181                                 },\r
182                                 this,\r
183                                 null,\r
184                                 0 );    // First to run.\r
185 \r
186                         this.engineLoaded = -1; // Loading in progress.\r
187 \r
188                         // compose scayt url\r
189                         var protocol = document.location.protocol;\r
190                         // Default to 'http' for unknown.\r
191                         protocol = protocol.search( /https?:/) != -1? protocol : 'http:';\r
192                         var baseUrl  = "svc.spellchecker.net/spellcheck/lf/scayt/scayt1.js";\r
193 \r
194                         var scaytUrl  =  editor.config.scayt_srcUrl || ( protocol + "//" + baseUrl );\r
195                         var scaytConfigBaseUrl =  plugin.parseUrl( scaytUrl ).path +  "/";\r
196 \r
197                         CKEDITOR._djScaytConfig =\r
198                         {\r
199                                 baseUrl: scaytConfigBaseUrl,\r
200                                 addOnLoad:\r
201                                 [\r
202                                         function()\r
203                                         {\r
204                                                 CKEDITOR.fireOnce( "scaytReady" );\r
205                                         }\r
206                                 ],\r
207                                 isDebug: false\r
208                         };\r
209                         // Append javascript code.\r
210                         CKEDITOR.document.getHead().append(\r
211                                 CKEDITOR.document.createElement( 'script',\r
212                                         {\r
213                                                 attributes :\r
214                                                         {\r
215                                                                 type : 'text/javascript',\r
216                                                                 src : scaytUrl\r
217                                                         }\r
218                                         })\r
219                         );\r
220 \r
221                         return null;\r
222                 },\r
223                 parseUrl : function ( data )\r
224                 {\r
225                         var match;\r
226                         if ( data.match && ( match = data.match(/(.*)[\/\\](.*?\.\w+)$/) ) )\r
227                                 return { path: match[1], file: match[2] };\r
228                         else\r
229                                 return data;\r
230                 }\r
231         };\r
232 \r
233         var plugin = CKEDITOR.plugins.scayt;\r
234 \r
235         // Context menu constructing.\r
236         var addButtonCommand = function( editor, buttonName, buttonLabel, commandName, command, menugroup, menuOrder )\r
237         {\r
238                 editor.addCommand( commandName, command );\r
239 \r
240                 // If the "menu" plugin is loaded, register the menu item.\r
241                 editor.addMenuItem( commandName,\r
242                         {\r
243                                 label : buttonLabel,\r
244                                 command : commandName,\r
245                                 group : menugroup,\r
246                                 order : menuOrder\r
247                         });\r
248         };\r
249 \r
250         var commandDefinition =\r
251         {\r
252                 preserveState : true,\r
253                 editorFocus : false,\r
254 \r
255                 exec: function( editor )\r
256                 {\r
257                         if ( plugin.isScaytReady( editor ) )\r
258                         {\r
259                                 var isEnabled = plugin.isScaytEnabled( editor );\r
260 \r
261                                 this.setState( isEnabled ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_ON );\r
262 \r
263                                 var scayt_control = plugin.getScayt( editor );\r
264                                 scayt_control.setDisabled( isEnabled );\r
265                         }\r
266                         else if ( !editor.config.scayt_autoStartup && plugin.engineLoaded >= 0 )        // Load first time\r
267                         {\r
268                                 this.setState( CKEDITOR.TRISTATE_DISABLED );\r
269 \r
270                                 editor.on( 'showScaytState', function()\r
271                                         {\r
272                                                 this.removeListener();\r
273                                                 this.setState( plugin.isScaytEnabled( editor ) ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF );\r
274                                         },\r
275                                         this);\r
276 \r
277                                 plugin.loadEngine( editor );\r
278                         }\r
279                 }\r
280         };\r
281 \r
282         // Add scayt plugin.\r
283         CKEDITOR.plugins.add( 'scayt',\r
284         {\r
285                 requires : [ 'menubutton' ],\r
286 \r
287                 beforeInit : function( editor )\r
288                 {\r
289                         // Register own rbc menu group.\r
290                         editor.config.menu_groups = 'scayt_suggest,scayt_moresuggest,scayt_control,' + editor.config.menu_groups;\r
291                 },\r
292 \r
293                 init : function( editor )\r
294                 {\r
295                         var moreSuggestions = {};\r
296                         var mainSuggestions = {};\r
297 \r
298                         // Scayt command.\r
299                         var command = editor.addCommand( commandName, commandDefinition );\r
300 \r
301                         // Add Options dialog.\r
302                         CKEDITOR.dialog.add( commandName, CKEDITOR.getUrl( this.path + 'dialogs/options.js' ) );\r
303 \r
304                         var menuGroup = 'scaytButton';\r
305                         editor.addMenuGroup( menuGroup );\r
306                         editor.addMenuItems(\r
307                                 {\r
308                                         scaytToggle :\r
309                                         {\r
310                                                 label : editor.lang.scayt.enable,\r
311                                                 command : commandName,\r
312                                                 group : menuGroup\r
313                                         },\r
314 \r
315                                         scaytOptions :\r
316                                         {\r
317                                                 label : editor.lang.scayt.options,\r
318                                                 group : menuGroup,\r
319                                                 onClick : function()\r
320                                                 {\r
321                                                         openPage = 'options';\r
322                                                         editor.openDialog( commandName );\r
323                                                 }\r
324                                         },\r
325 \r
326                                         scaytLangs :\r
327                                         {\r
328                                                 label : editor.lang.scayt.langs,\r
329                                                 group : menuGroup,\r
330                                                 onClick : function()\r
331                                                 {\r
332                                                         openPage = 'langs';\r
333                                                         editor.openDialog( commandName );\r
334                                                 }\r
335                                         },\r
336 \r
337                                         scaytAbout :\r
338                                         {\r
339                                                 label : editor.lang.scayt.about,\r
340                                                 group : menuGroup,\r
341                                                 onClick : function()\r
342                                                 {\r
343                                                         openPage = 'about';\r
344                                                         editor.openDialog( commandName );\r
345                                                 }\r
346                                         }\r
347                                 });\r
348 \r
349                                 editor.ui.add( 'Scayt', CKEDITOR.UI_MENUBUTTON,\r
350                                         {\r
351                                                 label : editor.lang.scayt.title,\r
352                                                 title : editor.lang.scayt.title,\r
353                                                 className : 'cke_button_scayt',\r
354                                                 onRender: function()\r
355                                                 {\r
356                                                 command.on( 'state', function()\r
357                                                         {\r
358                                                                 this.setState( command.state );\r
359                                                         },\r
360                                                         this);\r
361                                         },\r
362                                         onMenu : function()\r
363                                         {\r
364                                                 var isEnabled = plugin.isScaytEnabled( editor );\r
365 \r
366                                                 editor.getMenuItem( 'scaytToggle' ).label = editor.lang.scayt[ isEnabled ? 'disable' : 'enable' ];\r
367 \r
368                                                         return {\r
369                                                                 scaytToggle : CKEDITOR.TRISTATE_OFF,\r
370                                                                 scaytOptions : isEnabled ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,\r
371                                                                 scaytLangs : isEnabled ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,\r
372                                                                 scaytAbout : isEnabled ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED\r
373                                                         };\r
374                                                 }\r
375                                         });\r
376 \r
377                         // If the "contextmenu" plugin is loaded, register the listeners.\r
378                         if ( editor.contextMenu && editor.addMenuItems )\r
379                         {\r
380                                 editor.contextMenu.addListener( function( element )\r
381                                         {\r
382                                                 if ( !( plugin.isScaytEnabled( editor ) && element ) )\r
383                                                         return null;\r
384 \r
385                                                 var scayt_control = plugin.getScayt( editor ),\r
386                                                         word = scayt_control.getWord( element.$ );\r
387 \r
388                                                 if ( !word )\r
389                                                         return null;\r
390 \r
391                                                 var sLang = scayt_control.getLang(),\r
392                                                         _r = {},\r
393                                                         items_suggestion = window.scayt.getSuggestion( word, sLang );\r
394                                                 if (!items_suggestion || !items_suggestion.length )\r
395                                                         return null;\r
396                                                 // Remove unused commands and menuitems\r
397                                                 for ( i in moreSuggestions )\r
398                                                 {\r
399                                                         delete editor._.menuItems[ i ];\r
400                                                         delete editor._.commands[ i ];\r
401                                                 }\r
402                                                 for ( i in mainSuggestions )\r
403                                                 {\r
404                                                         delete editor._.menuItems[ i ];\r
405                                                         delete editor._.commands[ i ];\r
406                                                 }\r
407                                                 moreSuggestions = {};           // Reset items.\r
408                                                 mainSuggestions = {};\r
409 \r
410                                                 var moreSuggestionsUnable = false;\r
411 \r
412                                                 for ( var i = 0, l = items_suggestion.length; i < l; i += 1 )\r
413                                                 {\r
414                                                         var commandName = 'scayt_suggestion_' + items_suggestion[i].replace( ' ', '_' );\r
415                                                         var exec = ( function( el, s )\r
416                                                                 {\r
417                                                                         return {\r
418                                                                                 exec: function()\r
419                                                                                 {\r
420                                                                                         scayt_control.replace(el, s);\r
421                                                                                 }\r
422                                                                         };\r
423                                                                 })( element.$, items_suggestion[i] );\r
424 \r
425                                                         if ( i < editor.config.scayt_maxSuggestions )\r
426                                                         {\r
427                                                                 addButtonCommand( editor, 'button_' + commandName, items_suggestion[i],\r
428                                                                         commandName, exec, 'scayt_suggest', i + 1 );\r
429                                                                 _r[ commandName ] = CKEDITOR.TRISTATE_OFF;\r
430                                                                 mainSuggestions[ commandName ] = CKEDITOR.TRISTATE_OFF;\r
431                                                         }\r
432                                                         else\r
433                                                         {\r
434                                                                 addButtonCommand( editor, 'button_' + commandName, items_suggestion[i],\r
435                                                                         commandName, exec, 'scayt_moresuggest', i + 1 );\r
436                                                                 moreSuggestions[ commandName ] = CKEDITOR.TRISTATE_OFF;\r
437                                                                 moreSuggestionsUnable = true;\r
438                                                         }\r
439                                                 }\r
440                                                 if ( moreSuggestionsUnable )\r
441                                                         // Rgister the More suggestions group;\r
442                                                         editor.addMenuItem( 'scayt_moresuggest',\r
443                                                                 {\r
444                                                                         label : editor.lang.scayt.moreSuggestions,\r
445                                                                         group : 'scayt_moresuggest',\r
446                                                                         order : 10,\r
447                                                                         getItems : function()\r
448                                                                         {\r
449                                                                                 return moreSuggestions;\r
450                                                                         }\r
451                                                                 });\r
452 \r
453 \r
454                                                 var ignore_command =\r
455                                                 {\r
456                                                         exec: function()\r
457                                                         {\r
458                                                                 scayt_control.ignore( element.$ );\r
459                                                         }\r
460                                                 };\r
461                                                 var ignore_all_command =\r
462                                                 {\r
463                                                         exec: function()\r
464                                                         {\r
465                                                                 scayt_control.ignoreAll( element.$ );\r
466                                                         }\r
467                                                 };\r
468                                                 var addword_command =\r
469                                                 {\r
470                                                         exec: function()\r
471                                                         {\r
472                                                                 window.scayt.addWordToUserDictionary( element.$ );\r
473                                                         }\r
474                                                 };\r
475 \r
476                                                 addButtonCommand( editor, 'ignore', editor.lang.scayt.ignore,\r
477                                                         'scayt_ignore', ignore_command, 'scayt_control', 1);\r
478                                                 addButtonCommand( editor, 'ignore_all', editor.lang.scayt.ignoreAll,\r
479                                                         'scayt_ignore_all', ignore_all_command, 'scayt_control', 2);\r
480                                                 addButtonCommand( editor, 'add_word', editor.lang.scayt.addWord,\r
481                                                         'scayt_add_word', addword_command, 'scayt_control', 3);\r
482 \r
483                                                 mainSuggestions[ 'scayt_moresuggest' ] = CKEDITOR.TRISTATE_OFF;\r
484                                                 mainSuggestions[ 'scayt_ignore' ] = CKEDITOR.TRISTATE_OFF;\r
485                                                 mainSuggestions[ 'scayt_ignore_all' ] = CKEDITOR.TRISTATE_OFF;\r
486                                                 mainSuggestions[ 'scayt_add_word' ] = CKEDITOR.TRISTATE_OFF;\r
487 \r
488                                                 if ( scayt_control.fireOnContextMenu )\r
489                                                         scayt_control.fireOnContextMenu( editor );\r
490 \r
491                                                 return mainSuggestions;\r
492                                         });\r
493                         }\r
494 \r
495                         // Start plugin\r
496                         if ( editor.config.scayt_autoStartup )\r
497                         {\r
498                                 var showInitialState = function()\r
499                                 {\r
500                                         editor.removeListener( 'showScaytState', showInitialState );\r
501                                         command.setState( plugin.isScaytEnabled( editor ) ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF );\r
502                                 };\r
503                                 editor.on( 'showScaytState', showInitialState );\r
504 \r
505                                 plugin.loadEngine( editor );\r
506                         }\r
507                 }\r
508         });\r
509 })();\r
510 \r
511 CKEDITOR.config.scayt_maxSuggestions =  5;\r
512 CKEDITOR.config.scayt_autoStartup = false;\r