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