JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
cfe35d6976e50c9c6e8bfeab008d8f227bc0d359
[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 \r
254                 exec: function( editor )\r
255                 {\r
256                         if ( plugin.isScaytReady( editor ) )\r
257                         {\r
258                                 var isEnabled = plugin.isScaytEnabled( editor );\r
259 \r
260                                 this.setState( isEnabled ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_ON );\r
261 \r
262                                 var scayt_control = plugin.getScayt( editor );\r
263                                 scayt_control.setDisabled( isEnabled );\r
264                         }\r
265                         else if ( !editor.config.scayt_autoStartup && plugin.engineLoaded >= 0 )        // Load first time\r
266                         {\r
267                                 this.setState( CKEDITOR.TRISTATE_DISABLED );\r
268 \r
269                                 editor.on( 'showScaytState', function()\r
270                                         {\r
271                                                 this.removeListener();\r
272                                                 this.setState( plugin.isScaytEnabled( editor ) ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF );\r
273                                         },\r
274                                         this);\r
275 \r
276                                 plugin.loadEngine( editor );\r
277                         }\r
278                 }\r
279         };\r
280 \r
281         // Add scayt plugin.\r
282         CKEDITOR.plugins.add( 'scayt',\r
283         {\r
284                 requires : [ 'menubutton' ],\r
285 \r
286                 beforeInit : function( editor )\r
287                 {\r
288                         // Register own rbc menu group.\r
289                         editor.config.menu_groups = 'scayt_suggest,scayt_moresuggest,scayt_control,' + editor.config.menu_groups;\r
290                 },\r
291 \r
292                 init : function( editor )\r
293                 {\r
294                         var moreSuggestions = {};\r
295                         var mainSuggestions = {};\r
296 \r
297                         // Scayt command.\r
298                         var command = editor.addCommand( commandName, commandDefinition );\r
299 \r
300                         // Add Options dialog.\r
301                         CKEDITOR.dialog.add( commandName, CKEDITOR.getUrl( this.path + 'dialogs/options.js' ) );\r
302 \r
303                         var menuGroup = 'scaytButton';\r
304                         editor.addMenuGroup( menuGroup );\r
305                         editor.addMenuItems(\r
306                                 {\r
307                                         scaytToggle :\r
308                                         {\r
309                                                 label : editor.lang.scayt.enable,\r
310                                                 command : commandName,\r
311                                                 group : menuGroup\r
312                                         },\r
313 \r
314                                         scaytOptions :\r
315                                         {\r
316                                                 label : editor.lang.scayt.options,\r
317                                                 group : menuGroup,\r
318                                                 onClick : function()\r
319                                                 {\r
320                                                         openPage = 'options';\r
321                                                         editor.openDialog( commandName );\r
322                                                 }\r
323                                         },\r
324 \r
325                                         scaytLangs :\r
326                                         {\r
327                                                 label : editor.lang.scayt.langs,\r
328                                                 group : menuGroup,\r
329                                                 onClick : function()\r
330                                                 {\r
331                                                         openPage = 'langs';\r
332                                                         editor.openDialog( commandName );\r
333                                                 }\r
334                                         },\r
335 \r
336                                         scaytAbout :\r
337                                         {\r
338                                                 label : editor.lang.scayt.about,\r
339                                                 group : menuGroup,\r
340                                                 onClick : function()\r
341                                                 {\r
342                                                         openPage = 'about';\r
343                                                         editor.openDialog( commandName );\r
344                                                 }\r
345                                         }\r
346                                 });\r
347 \r
348                                 editor.ui.add( 'Scayt', CKEDITOR.UI_MENUBUTTON,\r
349                                         {\r
350                                                 label : editor.lang.scayt.title,\r
351                                                 title : editor.lang.scayt.title,\r
352                                                 className : 'cke_button_scayt',\r
353                                                 onRender: function()\r
354                                                 {\r
355                                                 command.on( 'state', function()\r
356                                                         {\r
357                                                                 this.setState( command.state );\r
358                                                         },\r
359                                                         this);\r
360                                         },\r
361                                         onMenu : function()\r
362                                         {\r
363                                                 var isEnabled = plugin.isScaytEnabled( editor );\r
364 \r
365                                                 editor.getMenuItem( 'scaytToggle' ).label = editor.lang.scayt[ isEnabled ? 'disable' : 'enable' ];\r
366 \r
367                                                         return {\r
368                                                                 scaytToggle : CKEDITOR.TRISTATE_OFF,\r
369                                                                 scaytOptions : isEnabled ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,\r
370                                                                 scaytLangs : isEnabled ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,\r
371                                                                 scaytAbout : isEnabled ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED\r
372                                                         };\r
373                                                 }\r
374                                         });\r
375 \r
376                         // If the "contextmenu" plugin is loaded, register the listeners.\r
377                         if ( editor.contextMenu && editor.addMenuItems )\r
378                         {\r
379                                 editor.contextMenu.addListener( function( element )\r
380                                         {\r
381                                                 if ( !( plugin.isScaytEnabled( editor ) && element ) )\r
382                                                         return null;\r
383 \r
384                                                 var scayt_control = plugin.getScayt( editor ),\r
385                                                         word = scayt_control.getWord( element.$ );\r
386 \r
387                                                 if ( !word )\r
388                                                         return null;\r
389 \r
390                                                 var sLang = scayt_control.getLang(),\r
391                                                         _r = {},\r
392                                                         items_suggestion = window.scayt.getSuggestion( word, sLang );\r
393                                                 if (!items_suggestion || !items_suggestion.length )\r
394                                                         return null;\r
395                                                 // Remove unused commands and menuitems\r
396                                                 for ( i in moreSuggestions )\r
397                                                 {\r
398                                                         delete editor._.menuItems[ i ];\r
399                                                         delete editor._.commands[ i ];\r
400                                                 }\r
401                                                 for ( i in mainSuggestions )\r
402                                                 {\r
403                                                         delete editor._.menuItems[ i ];\r
404                                                         delete editor._.commands[ i ];\r
405                                                 }\r
406                                                 moreSuggestions = {};           // Reset items.\r
407                                                 mainSuggestions = {};\r
408 \r
409                                                 var moreSuggestionsUnable = false;\r
410 \r
411                                                 for ( var i = 0, l = items_suggestion.length; i < l; i += 1 )\r
412                                                 {\r
413                                                         var commandName = 'scayt_suggestion_' + items_suggestion[i].replace( ' ', '_' );\r
414                                                         var exec = ( function( el, s )\r
415                                                                 {\r
416                                                                         return {\r
417                                                                                 exec: function()\r
418                                                                                 {\r
419                                                                                         scayt_control.replace(el, s);\r
420                                                                                 }\r
421                                                                         };\r
422                                                                 })( element.$, items_suggestion[i] );\r
423 \r
424                                                         if ( i < editor.config.scayt_maxSuggestions )\r
425                                                         {\r
426                                                                 addButtonCommand( editor, 'button_' + commandName, items_suggestion[i],\r
427                                                                         commandName, exec, 'scayt_suggest', i + 1 );\r
428                                                                 _r[ commandName ] = CKEDITOR.TRISTATE_OFF;\r
429                                                                 mainSuggestions[ commandName ] = CKEDITOR.TRISTATE_OFF;\r
430                                                         }\r
431                                                         else\r
432                                                         {\r
433                                                                 addButtonCommand( editor, 'button_' + commandName, items_suggestion[i],\r
434                                                                         commandName, exec, 'scayt_moresuggest', i + 1 );\r
435                                                                 moreSuggestions[ commandName ] = CKEDITOR.TRISTATE_OFF;\r
436                                                                 moreSuggestionsUnable = true;\r
437                                                         }\r
438                                                 }\r
439                                                 if ( moreSuggestionsUnable )\r
440                                                         // Rgister the More suggestions group;\r
441                                                         editor.addMenuItem( 'scayt_moresuggest',\r
442                                                                 {\r
443                                                                         label : editor.lang.scayt.moreSuggestions,\r
444                                                                         group : 'scayt_moresuggest',\r
445                                                                         order : 10,\r
446                                                                         getItems : function()\r
447                                                                         {\r
448                                                                                 return moreSuggestions;\r
449                                                                         }\r
450                                                                 });\r
451 \r
452 \r
453                                                 var ignore_command =\r
454                                                 {\r
455                                                         exec: function()\r
456                                                         {\r
457                                                                 scayt_control.ignore( element.$ );\r
458                                                         }\r
459                                                 };\r
460                                                 var ignore_all_command =\r
461                                                 {\r
462                                                         exec: function()\r
463                                                         {\r
464                                                                 scayt_control.ignoreAll( element.$ );\r
465                                                         }\r
466                                                 };\r
467                                                 var addword_command =\r
468                                                 {\r
469                                                         exec: function()\r
470                                                         {\r
471                                                                 window.scayt.addWordToUserDictionary( element.$ );\r
472                                                         }\r
473                                                 };\r
474 \r
475                                                 addButtonCommand( editor, 'ignore', editor.lang.scayt.ignore,\r
476                                                         'scayt_ignore', ignore_command, 'scayt_control', 1);\r
477                                                 addButtonCommand( editor, 'ignore_all', editor.lang.scayt.ignoreAll,\r
478                                                         'scayt_ignore_all', ignore_all_command, 'scayt_control', 2);\r
479                                                 addButtonCommand( editor, 'add_word', editor.lang.scayt.addWord,\r
480                                                         'scayt_add_word', addword_command, 'scayt_control', 3);\r
481 \r
482                                                 mainSuggestions[ 'scayt_moresuggest' ] = CKEDITOR.TRISTATE_OFF;\r
483                                                 mainSuggestions[ 'scayt_ignore' ] = CKEDITOR.TRISTATE_OFF;\r
484                                                 mainSuggestions[ 'scayt_ignore_all' ] = CKEDITOR.TRISTATE_OFF;\r
485                                                 mainSuggestions[ 'scayt_add_word' ] = CKEDITOR.TRISTATE_OFF;\r
486 \r
487                                                 if ( scayt_control.fireOnContextMenu )\r
488                                                         scayt_control.fireOnContextMenu( editor );\r
489 \r
490                                                 return mainSuggestions;\r
491                                         });\r
492                         }\r
493 \r
494                         // Start plugin\r
495                         if ( editor.config.scayt_autoStartup )\r
496                         {\r
497                                 var showInitialState = function()\r
498                                 {\r
499                                         editor.removeListener( 'showScaytState', showInitialState );\r
500                                         command.setState( plugin.isScaytEnabled( editor ) ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF );\r
501                                 };\r
502                                 editor.on( 'showScaytState', showInitialState );\r
503 \r
504                                 plugin.loadEngine( editor );\r
505                         }\r
506                 }\r
507         });\r
508 })();\r
509 \r
510 CKEDITOR.config.scayt_maxSuggestions =  5;\r
511 CKEDITOR.config.scayt_autoStartup = false;\r