plugin.js 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420
  1. 'use strict';
  2. CKEDITOR.plugins.add('scayt', {
  3. //requires : ['menubutton', 'dialog'],
  4. requires: 'menubutton,dialog',
  5. lang: 'af,ar,bg,bn,bs,ca,cs,cy,da,de,el,en-au,en-ca,en-gb,en,eo,es,et,eu,fa,fi,fo,fr-ca,fr,gl,gu,he,hi,hr,hu,is,it,ja,ka,km,ko,lt,lv,mk,mn,ms,nb,nl,no,pl,pt-br,pt,ro,ru,sk,sl,sr-latn,sr,sv,th,tr,ug,uk,vi,zh-cn,zh', // %REMOVE_LINE_CORE%
  6. icons: 'scayt', // %REMOVE_LINE_CORE%
  7. hidpi: true, // %REMOVE_LINE_CORE%
  8. tabToOpen : null,
  9. dialogName: 'scaytDialog',
  10. init: function(editor) {
  11. var self = this,
  12. plugin = CKEDITOR.plugins.scayt;
  13. this.bindEvents(editor);
  14. this.parseConfig(editor);
  15. this.addRule(editor);
  16. // source mode
  17. CKEDITOR.dialog.add(this.dialogName, CKEDITOR.getUrl(this.path + 'dialogs/options.js'));
  18. // end source mode
  19. this.addMenuItems(editor);
  20. var config = editor.config,
  21. lang = editor.lang.scayt,
  22. env = CKEDITOR.env;
  23. editor.ui.add('Scayt', CKEDITOR.UI_MENUBUTTON, {
  24. label : lang.text_title,
  25. title : ( editor.plugins.wsc ? editor.lang.wsc.title : lang.text_title ),
  26. // SCAYT doesn't work in IE Compatibility Mode and IE (8 & 9) Quirks Mode
  27. modes : {wysiwyg: !(env.ie && ( env.version < 8 || env.quirks ) ) },
  28. toolbar: 'spellchecker,20',
  29. refresh: function() {
  30. var buttonState = editor.ui.instances.Scayt.getState();
  31. // check if scayt is created
  32. if(editor.scayt) {
  33. // check if scayt is enabled
  34. if(plugin.state[editor.name]) {
  35. buttonState = CKEDITOR.TRISTATE_ON;
  36. } else {
  37. buttonState = CKEDITOR.TRISTATE_OFF;
  38. }
  39. }
  40. editor.fire('scaytButtonState', buttonState);
  41. },
  42. onRender: function() {
  43. var that = this;
  44. editor.on('scaytButtonState', function(ev) {
  45. if(typeof ev.data !== undefined) {
  46. that.setState(ev.data);
  47. }
  48. });
  49. },
  50. onMenu : function() {
  51. var scaytInstance = editor.scayt;
  52. editor.getMenuItem('scaytToggle').label = editor.lang.scayt[(scaytInstance ? plugin.state[editor.name] : false) ? 'btn_disable' : 'btn_enable'];
  53. // If UI tab is disabled we shouldn't show menu item
  54. var menuDefinition = {
  55. scaytToggle : CKEDITOR.TRISTATE_OFF,
  56. scaytOptions : scaytInstance ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,
  57. scaytLangs : scaytInstance ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,
  58. scaytDict : scaytInstance ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,
  59. scaytAbout : scaytInstance ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,
  60. WSC : editor.plugins.wsc ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED
  61. };
  62. if(!editor.config.scayt_uiTabs[0]) {
  63. delete menuDefinition.scaytOptions;
  64. }
  65. if(!editor.config.scayt_uiTabs[1]) {
  66. delete menuDefinition.scaytLangs;
  67. }
  68. if(!editor.config.scayt_uiTabs[2]) {
  69. delete menuDefinition.scaytDict;
  70. }
  71. return menuDefinition;
  72. }
  73. });
  74. // If the 'contextmenu' plugin is loaded, register the listeners.
  75. if(editor.contextMenu && editor.addMenuItems) {
  76. editor.contextMenu.addListener(function(element, selection) {
  77. var scaytInstance = editor.scayt;
  78. var result;
  79. if(scaytInstance) {
  80. // TODO: implement right lang getter
  81. var selectionNode = scaytInstance.getSelectionNode(),
  82. word;
  83. if(selectionNode) {
  84. word = selectionNode.getAttribute(scaytInstance.getNodeAttribute());
  85. } else {
  86. word = selectionNode;
  87. }
  88. // SCAYT shouldn't build context menu if instance isnot created or word is without misspelling
  89. if(word) {
  90. var items = self.menuGenerator(editor, word, self);
  91. scaytInstance.showBanner('.' + editor.contextMenu._.definition.panel.className.split(' ').join(' .'));
  92. result = items;
  93. }
  94. }
  95. return result;
  96. });
  97. editor.contextMenu._.onHide = CKEDITOR.tools.override(editor.contextMenu._.onHide, function(org) {
  98. return function() {
  99. var scaytInstance = editor.scayt;
  100. if(scaytInstance) {
  101. scaytInstance.hideBanner();
  102. }
  103. return org.apply(this);
  104. };
  105. });
  106. }
  107. },
  108. addMenuItems: function(editor) {
  109. var self = this,
  110. plugin = CKEDITOR.plugins.scayt,
  111. menuGroup = 'scaytButton';
  112. editor.addMenuGroup(menuGroup);
  113. var items_order = editor.config.scayt_contextMenuItemsOrder.split('|');
  114. if(items_order && items_order.length) {
  115. for(var pos = 0 ; pos < items_order.length ; pos++) {
  116. editor.addMenuGroup('scayt_' + items_order[pos], pos - 10);
  117. }
  118. }
  119. editor.addCommand( 'scaytToggle', {
  120. exec: function(editor) {
  121. var scaytInstance = editor.scayt;
  122. plugin.state[editor.name] = !plugin.state[editor.name];
  123. if(plugin.state[editor.name] === true) {
  124. if(!scaytInstance) {
  125. plugin.createScayt(editor);
  126. }
  127. } else {
  128. if(scaytInstance) {
  129. plugin.destroy(editor);
  130. }
  131. }
  132. }
  133. } );
  134. editor.addCommand( 'scaytAbout', {
  135. exec: function(editor) {
  136. var scaytInstance = editor.scayt;
  137. scaytInstance.tabToOpen = 'about';
  138. editor.lockSelection();
  139. editor.openDialog(self.dialogName);
  140. }
  141. } );
  142. editor.addCommand( 'scaytOptions', {
  143. exec: function(editor) {
  144. var scaytInstance = editor.scayt;
  145. scaytInstance.tabToOpen = 'options';
  146. editor.lockSelection();
  147. editor.openDialog(self.dialogName);
  148. }
  149. } );
  150. editor.addCommand( 'scaytLangs', {
  151. exec: function(editor) {
  152. var scaytInstance = editor.scayt;
  153. scaytInstance.tabToOpen = 'langs';
  154. editor.lockSelection();
  155. editor.openDialog(self.dialogName);
  156. }
  157. } );
  158. editor.addCommand( 'scaytDict', {
  159. exec: function(editor) {
  160. var scaytInstance = editor.scayt;
  161. scaytInstance.tabToOpen = 'dictionaries';
  162. editor.lockSelection();
  163. editor.openDialog(self.dialogName);
  164. }
  165. } );
  166. var uiMenuItems = {
  167. scaytToggle: {
  168. label : editor.lang.scayt.btn_enable,
  169. group : menuGroup,
  170. command: 'scaytToggle'
  171. },
  172. scaytAbout: {
  173. label : editor.lang.scayt.btn_about,
  174. group : menuGroup,
  175. command: 'scaytAbout'
  176. },
  177. scaytOptions: {
  178. label : editor.lang.scayt.btn_options,
  179. group : menuGroup,
  180. command: 'scaytOptions'
  181. },
  182. scaytLangs: {
  183. label : editor.lang.scayt.btn_langs,
  184. group : menuGroup,
  185. command: 'scaytLangs'
  186. },
  187. scaytDict: {
  188. label : editor.lang.scayt.btn_dictionaries,
  189. group : menuGroup,
  190. command: 'scaytDict'
  191. }
  192. };
  193. if(editor.plugins.wsc) {
  194. uiMenuItems.WSC = {
  195. label : editor.lang.wsc.toolbar,
  196. group : menuGroup,
  197. onClick: function() {
  198. var inlineMode = (editor.elementMode == CKEDITOR.ELEMENT_MODE_INLINE),
  199. plugin = CKEDITOR.plugins.scayt,
  200. scaytInstance = editor.scayt,
  201. text = inlineMode ? editor.container.getText() : editor.document.getBody().getText();
  202. text = text.replace(/\s/g, '');
  203. if(text) {
  204. if(scaytInstance && plugin.state[editor.name] && scaytInstance.setMarkupPaused) {
  205. scaytInstance.setMarkupPaused(true);
  206. }
  207. editor.lockSelection();
  208. editor.execCommand('checkspell');
  209. } else {
  210. alert('Nothing to check!');
  211. }
  212. }
  213. }
  214. }
  215. editor.addMenuItems(uiMenuItems);
  216. },
  217. bindEvents: function(editor) {
  218. var self = this,
  219. plugin = CKEDITOR.plugins.scayt,
  220. inline_mode = (editor.elementMode == CKEDITOR.ELEMENT_MODE_INLINE);
  221. var scaytDestroy = function() {
  222. plugin.destroy(editor);
  223. };
  224. var contentDomReady = function() {
  225. // The event is fired when editable iframe node was reinited so we should restart our service
  226. if (plugin.state[editor.name] && !editor.readOnly && !editor.scayt) {
  227. plugin.createScayt(editor);
  228. }
  229. };
  230. var addMarkupStateHandlers = function() {
  231. var editable = editor.editable();
  232. editable.attachListener( editable, 'focus', function( evt ) {
  233. if( CKEDITOR.plugins.scayt && !editor.scayt ) {
  234. setTimeout(contentDomReady, 0); // we need small timeout in order to correctly set initial 'focused' option value in SCAYT core
  235. }
  236. var pluginStatus = CKEDITOR.plugins.scayt && CKEDITOR.plugins.scayt.state[editor.name] && editor.scayt,
  237. selectedElement, ranges, textLength, range;
  238. if((inline_mode ? true : pluginStatus) && editor._.savedSelection) {
  239. selectedElement = editor._.savedSelection.getSelectedElement();
  240. ranges = !selectedElement && editor._.savedSelection.getRanges();
  241. for(var i = 0; i < ranges.length; i++) {
  242. range = ranges[i];
  243. // we need to check type of node value in order to avoid error in IE when accessing 'nodeValue' property
  244. if(typeof range.startContainer.$.nodeValue === 'string') {
  245. textLength = range.startContainer.getText().length;
  246. if(textLength < range.startOffset || textLength < range.endOffset) {
  247. editor.unlockSelection(false);
  248. }
  249. }
  250. }
  251. }
  252. }, this, null, -10 ); // priority "-10" is set to call SCAYT CKEDITOR.editor#unlockSelection before CKEDITOR.editor#unlockSelection call
  253. };
  254. var contentDomHandler = function() {
  255. if(inline_mode) {
  256. editor.on('blur', scaytDestroy);
  257. editor.on('focus', contentDomReady);
  258. // We need to check if editor has focus(created) right now.
  259. // If editor is active - make attempt to create scayt
  260. if(editor.focusManager.hasFocus) {
  261. contentDomReady();
  262. }
  263. } else {
  264. contentDomReady();
  265. }
  266. addMarkupStateHandlers();
  267. };
  268. editor.on('contentDom', contentDomHandler);
  269. editor.on('beforeCommandExec', function(ev) {
  270. var scaytInstance;
  271. // TODO: after switching in source mode not recreate SCAYT instance, try to just rerun markuping to don't make requests to server
  272. if(ev.data.name in plugin.options.disablingCommandExec && editor.mode == 'wysiwyg') {
  273. scaytInstance = editor.scayt;
  274. if(scaytInstance) {
  275. plugin.destroy(editor);
  276. editor.fire('scaytButtonState', CKEDITOR.TRISTATE_DISABLED);
  277. }
  278. } else if(ev.data.name === 'bold' || ev.data.name === 'italic' || ev.data.name === 'underline' || ev.data.name === 'strike' || ev.data.name === 'subscript' || ev.data.name === 'superscript' || ev.data.name === 'enter') {
  279. scaytInstance = editor.scayt;
  280. if(scaytInstance) {
  281. scaytInstance.removeMarkupInSelectionNode();
  282. setTimeout(function() {
  283. scaytInstance.fire('startSpellCheck');
  284. }, 0);
  285. }
  286. }
  287. });
  288. editor.on('beforeSetMode', function(ev) {
  289. var scaytInstance;
  290. // needed when we use:
  291. // CKEDITOR.instances.editor_ID.setMode("source")
  292. // CKEDITOR.instances.editor_ID.setMode("wysiwyg")
  293. // can't be implemented in editor.on('mode', function(ev) {});
  294. if (ev.data == 'source') {
  295. scaytInstance = editor.scayt;
  296. if(scaytInstance) {
  297. plugin.destroy(editor);
  298. editor.fire('scaytButtonState', CKEDITOR.TRISTATE_DISABLED);
  299. }
  300. // remove custom data from body, to prevent waste properties showing in IE8
  301. if(editor.document) { //GitHub #84 : make sure that document exists(e.g. when startup mode set to 'source')
  302. editor.document.getBody().removeAttribute('_jquid');
  303. }
  304. }
  305. });
  306. editor.on('afterCommandExec', function(ev) {
  307. var scaytInstance;
  308. if(editor.mode == 'wysiwyg' && (ev.data.name == 'undo' || ev.data.name == 'redo')) {
  309. scaytInstance = editor.scayt;
  310. if(scaytInstance) {
  311. setTimeout(function() {
  312. scaytInstance.fire('startSpellCheck');
  313. }, 250);
  314. }
  315. }
  316. });
  317. // handle readonly changes
  318. editor.on('readOnly', function(ev) {
  319. var scaytInstance;
  320. if(ev) {
  321. scaytInstance = editor.scayt;
  322. if(ev.editor.readOnly === true) {
  323. if(scaytInstance) {
  324. scaytInstance.fire('removeMarkupInDocument', {});
  325. }
  326. } else {
  327. if(scaytInstance) {
  328. scaytInstance.fire('startSpellCheck');
  329. } else if(ev.editor.mode == 'wysiwyg' && plugin.state[ev.editor.name] === true) {
  330. plugin.createScayt(editor);
  331. ev.editor.fire('scaytButtonState', CKEDITOR.TRISTATE_ON);
  332. }
  333. }
  334. }
  335. });
  336. // we need to destroy SCAYT before CK editor will be completely destroyed
  337. editor.on('beforeDestroy', scaytDestroy);
  338. //#9439 after SetData method fires contentDom event and SCAYT create additional instanse
  339. // This way we should destroy SCAYT on setData event when contenteditable Iframe was re-created
  340. editor.on('setData', function() {
  341. scaytDestroy();
  342. // in inline mode SetData does not fire contentDom event
  343. if(editor.elementMode == CKEDITOR.ELEMENT_MODE_INLINE || editor.plugins.divarea) {
  344. contentDomHandler();
  345. }
  346. }, this, null, 50);
  347. function reloadMarkupScayt() {
  348. var scaytInstance = editor.scayt;
  349. if (scaytInstance) {
  350. scaytInstance.removeMarkupInSelectionNode();
  351. scaytInstance.fire('startSpellCheck');
  352. }
  353. }
  354. // Reload spell-checking for current word after insertion completed.
  355. editor.on('insertElement', function() {
  356. // IE bug: we need wait here to make sure that focus is returned to editor, and we can store the selection before we proceed with markup
  357. if ( CKEDITOR.env.ie ) {
  358. setTimeout(function() {
  359. reloadMarkupScayt();
  360. }, 50);
  361. } else {
  362. reloadMarkupScayt();
  363. }
  364. }, this, null, 50);
  365. editor.on('insertHtml', function() {
  366. reloadMarkupScayt();
  367. }, this, null, 50);
  368. editor.on('insertText', function() {
  369. reloadMarkupScayt();
  370. }, this, null, 50);
  371. // The event is listening to open necessary dialog tab
  372. editor.on('scaytDialogShown', function(ev) {
  373. var dialog = ev.data,
  374. scaytInstance = editor.scayt;
  375. dialog.selectPage(scaytInstance.tabToOpen);
  376. });
  377. editor.on('paste', function(ev) {
  378. var scaytInstance = editor.scayt;
  379. if(scaytInstance) {
  380. scaytInstance.removeMarkupInSelectionNode();
  381. }
  382. }, null, null, 0);
  383. },
  384. parseConfig: function(editor) {
  385. var plugin = CKEDITOR.plugins.scayt;
  386. // preprocess config for backward compatibility
  387. plugin.replaceOldOptionsNames(editor.config);
  388. // Checking editor's config after initialization
  389. if(typeof editor.config.scayt_autoStartup !== 'boolean') {
  390. editor.config.scayt_autoStartup = false;
  391. }
  392. plugin.state[editor.name] = editor.config.scayt_autoStartup;
  393. if(!editor.config.scayt_contextCommands) {
  394. editor.config.scayt_contextCommands = 'ignore|ignoreall|add';
  395. }
  396. if(!editor.config.scayt_contextMenuItemsOrder) {
  397. editor.config.scayt_contextMenuItemsOrder = 'suggest|moresuggest|control';
  398. }
  399. if(!editor.config.scayt_sLang) {
  400. editor.config.scayt_sLang = 'en_US';
  401. }
  402. if(editor.config.scayt_maxSuggestions === undefined || typeof editor.config.scayt_maxSuggestions != 'number' || editor.config.scayt_maxSuggestions < 0) {
  403. editor.config.scayt_maxSuggestions = 5;
  404. }
  405. if(editor.config.scayt_minWordLength === undefined || typeof editor.config.scayt_minWordLength != 'number' || editor.config.scayt_minWordLength < 1) {
  406. editor.config.scayt_minWordLength = 4;
  407. }
  408. if(editor.config.scayt_customDictionaryIds === undefined || typeof editor.config.scayt_customDictionaryIds !== 'string') {
  409. editor.config.scayt_customDictionaryIds = '';
  410. }
  411. if(editor.config.scayt_userDictionaryName === undefined || typeof editor.config.scayt_userDictionaryName !== 'string') {
  412. editor.config.scayt_userDictionaryName = null;
  413. }
  414. if (typeof editor.config.scayt_uiTabs === 'string' && editor.config.scayt_uiTabs.split(',').length === 3) {
  415. var scayt_uiTabs = [], _tempUITabs = [];
  416. editor.config.scayt_uiTabs = editor.config.scayt_uiTabs.split(',');
  417. CKEDITOR.tools.search(editor.config.scayt_uiTabs, function(value) {
  418. if (Number(value) === 1 || Number(value) === 0) {
  419. _tempUITabs.push(true);
  420. scayt_uiTabs.push(Number(value));
  421. } else {
  422. _tempUITabs.push(false);
  423. }
  424. });
  425. if (CKEDITOR.tools.search(_tempUITabs, false) === null) {
  426. editor.config.scayt_uiTabs = scayt_uiTabs;
  427. } else {
  428. editor.config.scayt_uiTabs = [1,1,1];
  429. }
  430. } else {
  431. editor.config.scayt_uiTabs = [1,1,1];
  432. }
  433. if(typeof editor.config.scayt_serviceProtocol != 'string') {
  434. editor.config.scayt_serviceProtocol = null;
  435. }
  436. if(typeof editor.config.scayt_serviceHost != 'string') {
  437. editor.config.scayt_serviceHost = null;
  438. }
  439. if(typeof editor.config.scayt_servicePort != 'string') {
  440. editor.config.scayt_servicePort = null;
  441. }
  442. if(typeof editor.config.scayt_servicePath != 'string') {
  443. editor.config.scayt_servicePath = null;
  444. }
  445. if(!editor.config.scayt_moreSuggestions) {
  446. editor.config.scayt_moreSuggestions = 'on';
  447. }
  448. if(typeof editor.config.scayt_customerId !== 'string') {
  449. editor.config.scayt_customerId = '1:WvF0D4-UtPqN1-43nkD4-NKvUm2-daQqk3-LmNiI-z7Ysb4-mwry24-T8YrS3-Q2tpq2';
  450. }
  451. if(typeof editor.config.scayt_srcUrl !== 'string') {
  452. var protocol = document.location.protocol;
  453. protocol = protocol.search(/https?:/) != -1 ? protocol : 'http:';
  454. editor.config.scayt_srcUrl = protocol + '//svc.webspellchecker.net/spellcheck31/lf/scayt3/ckscayt/ckscayt.js';
  455. }
  456. if(typeof CKEDITOR.config.scayt_handleCheckDirty !== 'boolean') {
  457. CKEDITOR.config.scayt_handleCheckDirty = true;
  458. }
  459. if(typeof CKEDITOR.config.scayt_handleUndoRedo !== 'boolean') {
  460. CKEDITOR.config.scayt_handleUndoRedo = true;
  461. }
  462. if( editor.config.scayt_disableOptionsStorage ) {
  463. var userOptions = CKEDITOR.tools.isArray( editor.config.scayt_disableOptionsStorage ) ? editor.config.scayt_disableOptionsStorage : ( typeof editor.config.scayt_disableOptionsStorage === 'string' ) ? [ editor.config.scayt_disableOptionsStorage ] : undefined,
  464. availableValue = [ 'all', 'options', 'lang', 'ignore-all-caps-words', 'ignore-domain-names', 'ignore-words-with-mixed-cases', 'ignore-words-with-numbers'],
  465. valuesOption = ['lang', 'ignore-all-caps-words', 'ignore-domain-names', 'ignore-words-with-mixed-cases', 'ignore-words-with-numbers'],
  466. search = CKEDITOR.tools.search,
  467. indexOf = CKEDITOR.tools.indexOf;
  468. var isValidOption = function( option ) {
  469. return !!search( availableValue, option );
  470. };
  471. var makeOptionsToStorage = function( options ) {
  472. var retval = [];
  473. for (var i = 0; i < options.length; i++) {
  474. var value = options[i],
  475. isGroupOptionInUserOptions = !!search( options, 'options' );
  476. if( !isValidOption( value ) || isGroupOptionInUserOptions && !!search( valuesOption, function( val ) { if( val === 'lang' ) { return false; } } ) ) {
  477. return;
  478. }
  479. if( !!search( valuesOption, value ) ) {
  480. valuesOption.splice( indexOf( valuesOption, value ), 1 );
  481. }
  482. if( value === 'all' || isGroupOptionInUserOptions && !!search( options, 'lang' )) {
  483. return [];
  484. }
  485. if( value === 'options' ) {
  486. valuesOption = [ 'lang' ];
  487. }
  488. }
  489. retval = retval.concat( valuesOption );
  490. return retval;
  491. };
  492. editor.config.scayt_disableOptionsStorage = makeOptionsToStorage( userOptions );
  493. }
  494. },
  495. addRule: function(editor) {
  496. var dataProcessor = editor.dataProcessor,
  497. htmlFilter = dataProcessor && dataProcessor.htmlFilter,
  498. pathFilters = editor._.elementsPath && editor._.elementsPath.filters,
  499. dataFilter = dataProcessor && dataProcessor.dataFilter,
  500. removeFormatFilter = editor.addRemoveFormatFilter,
  501. scaytFilter = function scaytFilter(element) {
  502. var plugin = CKEDITOR.plugins.scayt,
  503. scaytInstance = editor.scayt;
  504. if(scaytInstance && element.hasAttribute(plugin.options.data_attribute_name)) {
  505. return false;
  506. }
  507. },
  508. removeFormatFilterTemplate = function(element) {
  509. var plugin = CKEDITOR.plugins.scayt,
  510. scaytInstance = editor.scayt,
  511. result = true;
  512. if(scaytInstance && element.hasAttribute(plugin.options.data_attribute_name)) {
  513. result = false;
  514. }
  515. return result;
  516. };
  517. if(pathFilters) {
  518. pathFilters.push(scaytFilter);
  519. }
  520. if(dataFilter) {
  521. var dataFilterRules = {
  522. elements: {
  523. span: function(element) {
  524. var plugin = CKEDITOR.plugins.scayt;
  525. if(plugin && plugin.state[editor.name] && element.classes && CKEDITOR.tools.search(element.classes, plugin.options.misspelled_word_class)) {
  526. if (element.classes && element.parent.type === CKEDITOR.NODE_DOCUMENT_FRAGMENT) {
  527. delete element.attributes['style'];
  528. delete element.name;
  529. } else {
  530. delete element.classes[CKEDITOR.tools.indexOf(element.classes, plugin.options.misspelled_word_class)];
  531. }
  532. }
  533. return element;
  534. }
  535. }
  536. };
  537. dataFilter.addRules(dataFilterRules);
  538. }
  539. if (htmlFilter) {
  540. var htmlFilterRules = {
  541. elements: {
  542. span: function(element) {
  543. var plugin = CKEDITOR.plugins.scayt;
  544. if(plugin && plugin.state[editor.name] && element.hasClass(plugin.options.misspelled_word_class) && element.attributes[plugin.options.data_attribute_name]) {
  545. element.removeClass(plugin.options.misspelled_word_class);
  546. delete element.attributes[plugin.options.data_attribute_name];
  547. delete element.name;
  548. }
  549. return element;
  550. }
  551. }
  552. };
  553. htmlFilter.addRules(htmlFilterRules);
  554. }
  555. if(removeFormatFilter) {
  556. removeFormatFilter.call(editor, removeFormatFilterTemplate);
  557. }
  558. },
  559. scaytMenuDefinition: function(editor) {
  560. var self = this,
  561. plugin = CKEDITOR.plugins.scayt,
  562. scayt_instance = editor.scayt;
  563. return {
  564. scayt_ignore: {
  565. label: scayt_instance.getLocal('btn_ignore'),
  566. group : 'scayt_control',
  567. order : 1,
  568. exec: function(editor) {
  569. var scaytInstance = editor.scayt;
  570. scaytInstance.ignoreWord();
  571. }
  572. },
  573. scayt_ignoreall: {
  574. label : scayt_instance.getLocal('btn_ignoreAll'),
  575. group : 'scayt_control',
  576. order : 2,
  577. exec: function(editor) {
  578. var scaytInstance = editor.scayt;
  579. scaytInstance.ignoreAllWords();
  580. }
  581. },
  582. scayt_add: {
  583. label : scayt_instance.getLocal('btn_addWord'),
  584. group : 'scayt_control',
  585. order : 3,
  586. exec : function(editor) {
  587. var scaytInstance = editor.scayt;
  588. // @TODO: We need to add set/restore bookmark logic to 'addWordToUserDictionary' method inside dictionarymanager.
  589. // Timeout is used as tmp fix for IE9, when after hitting 'Add word' menu item, document container was blurred.
  590. setTimeout(function() {
  591. scaytInstance.addWordToUserDictionary();
  592. }, 10);
  593. }
  594. },
  595. option:{
  596. label : scayt_instance.getLocal('btn_options'),
  597. group : 'scayt_control',
  598. order : 4,
  599. exec: function(editor) {
  600. var scaytInstance = editor.scayt;
  601. scaytInstance.tabToOpen = 'options';
  602. editor.lockSelection();
  603. editor.openDialog(self.dialogName);
  604. },
  605. verification: function(editor) {
  606. return (editor.config.scayt_uiTabs[0] == 1) ? true : false;
  607. }
  608. },
  609. language: {
  610. label : scayt_instance.getLocal('btn_langs'),
  611. group : 'scayt_control',
  612. order : 5,
  613. exec: function(editor) {
  614. var scaytInstance = editor.scayt;
  615. scaytInstance.tabToOpen = 'langs';
  616. editor.lockSelection();
  617. editor.openDialog(self.dialogName);
  618. },
  619. verification: function(editor) {
  620. return (editor.config.scayt_uiTabs[1] == 1) ? true : false;
  621. }
  622. },
  623. dictionary: {
  624. label : scayt_instance.getLocal('btn_dictionaries'),
  625. group : 'scayt_control',
  626. order : 6,
  627. exec: function(editor) {
  628. var scaytInstance = editor.scayt;
  629. scaytInstance.tabToOpen = 'dictionaries';
  630. editor.lockSelection();
  631. editor.openDialog(self.dialogName);
  632. },
  633. verification: function(editor) {
  634. return (editor.config.scayt_uiTabs[2] == 1) ? true : false;
  635. }
  636. },
  637. about: {
  638. label : scayt_instance.getLocal('btn_about'),
  639. group : 'scayt_control',
  640. order : 7,
  641. exec: function(editor) {
  642. var scaytInstance = editor.scayt;
  643. scaytInstance.tabToOpen = 'about';
  644. editor.lockSelection();
  645. editor.openDialog(self.dialogName);
  646. }
  647. }
  648. };
  649. },
  650. buildSuggestionMenuItems: function(editor, suggestions) {
  651. var self = this,
  652. itemList = {},
  653. subItemList = {},
  654. plugin = CKEDITOR.plugins.scayt,
  655. scayt_instance = editor.scayt;
  656. if(suggestions.length > 0 && suggestions[0] !== 'no_any_suggestions') {
  657. for(var i = 0; i < suggestions.length; i++) {
  658. var commandName = 'scayt_suggest_' + CKEDITOR.plugins.scayt.suggestions[i].replace(' ', '_');
  659. editor.addCommand(commandName, self.createCommand(CKEDITOR.plugins.scayt.suggestions[i]));
  660. if(i < editor.config.scayt_maxSuggestions) {
  661. // mainSuggestions
  662. editor.addMenuItem(commandName, {
  663. label: suggestions[i],
  664. command: commandName,
  665. group: 'scayt_suggest',
  666. order: i + 1
  667. });
  668. itemList[commandName] = CKEDITOR.TRISTATE_OFF;
  669. } else {
  670. // moreSuggestions
  671. editor.addMenuItem(commandName, {
  672. label: suggestions[i],
  673. command: commandName,
  674. group: 'scayt_moresuggest',
  675. order: i + 1
  676. });
  677. subItemList[commandName] = CKEDITOR.TRISTATE_OFF;
  678. if(editor.config.scayt_moreSuggestions === 'on') {
  679. editor.addMenuItem('scayt_moresuggest', {
  680. label : scayt_instance.getLocal('btn_moreSuggestions'),
  681. group : 'scayt_moresuggest',
  682. order : 10,
  683. getItems : function() {
  684. return subItemList;
  685. }
  686. });
  687. itemList['scayt_moresuggest'] = CKEDITOR.TRISTATE_OFF;
  688. }
  689. }
  690. }
  691. } else {
  692. var noSuggestionsCommand = 'no_scayt_suggest';
  693. itemList[noSuggestionsCommand] = CKEDITOR.TRISTATE_DISABLED;
  694. editor.addCommand(noSuggestionsCommand, {
  695. exec: function() {
  696. }
  697. });
  698. editor.addMenuItem(noSuggestionsCommand, {
  699. label : scayt_instance.getLocal('btn_noSuggestions') || noSuggestionsCommand,
  700. command: noSuggestionsCommand,
  701. group : 'scayt_suggest',
  702. order : 0
  703. });
  704. }
  705. return itemList;
  706. },
  707. menuGenerator: function(editor, word) {
  708. var self = this,
  709. scaytInstance = editor.scayt,
  710. menuItem = this.scaytMenuDefinition(editor),
  711. itemList = {},
  712. allowedOption = editor.config.scayt_contextCommands.split('|');
  713. scaytInstance.fire('getSuggestionsList', {lang: scaytInstance.getLang(), word: word});
  714. itemList = this.buildSuggestionMenuItems(editor, CKEDITOR.plugins.scayt.suggestions);
  715. if(editor.config.scayt_contextCommands == 'off') {
  716. return itemList;
  717. }
  718. for(var key in menuItem) {
  719. if(CKEDITOR.tools.indexOf(allowedOption, key.replace('scayt_', '')) == -1 && editor.config.scayt_contextCommands != 'all') {
  720. continue;
  721. }
  722. itemList[key] = CKEDITOR.TRISTATE_OFF;
  723. // delete item from context menu if its state isn't verified as allowed
  724. if(typeof menuItem[key].verification === 'function' && !menuItem[key].verification(editor)) {
  725. // itemList[key] = (menuItem[key].verification(editor)) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED;
  726. delete itemList[key];
  727. }
  728. editor.addCommand(key, {
  729. exec: menuItem[key].exec
  730. });
  731. editor.addMenuItem(key, {
  732. label : editor.lang.scayt[menuItem[key].label] || menuItem[key].label,
  733. command: key,
  734. group : menuItem[key].group,
  735. order : menuItem[key].order
  736. });
  737. }
  738. return itemList;
  739. },
  740. createCommand: function(suggestion) {
  741. return {
  742. exec: function(editor) {
  743. var scaytInstance = editor.scayt;
  744. scaytInstance.replaceSelectionNode({word: suggestion});
  745. }
  746. };
  747. }
  748. });
  749. CKEDITOR.plugins.scayt = {
  750. state: {},
  751. suggestions: [],
  752. loadingHelper: {
  753. loadOrder: []
  754. },
  755. isLoading: false,
  756. options: {
  757. disablingCommandExec: {
  758. source: true,
  759. newpage: true,
  760. templates: true
  761. },
  762. data_attribute_name: 'data-scayt-word',
  763. misspelled_word_class: 'scayt-misspell-word'
  764. },
  765. backCompatibilityMap: {
  766. 'scayt_service_protocol': 'scayt_serviceProtocol',
  767. 'scayt_service_host' : 'scayt_serviceHost',
  768. 'scayt_service_port' : 'scayt_servicePort',
  769. 'scayt_service_path' : 'scayt_servicePath',
  770. 'scayt_customerid' : 'scayt_customerId'
  771. },
  772. replaceOldOptionsNames: function(config) {
  773. for(var key in config) {
  774. if(key in this.backCompatibilityMap) {
  775. config[this.backCompatibilityMap[key]] = config[key];
  776. delete config[key];
  777. }
  778. }
  779. },
  780. createScayt : function(editor) {
  781. var self = this;
  782. this.loadScaytLibrary(editor, function(_editor) {
  783. var _scaytInstanceOptions = {
  784. lang : _editor.config.scayt_sLang,
  785. container : _editor.editable().$.nodeName == 'BODY' ? _editor.document.getWindow().$.frameElement : _editor.editable().$,
  786. customDictionary : _editor.config.scayt_customDictionaryIds,
  787. userDictionaryName : _editor.config.scayt_userDictionaryName,
  788. localization : _editor.langCode,
  789. customer_id : _editor.config.scayt_customerId,
  790. debug : _editor.config.scayt_debug,
  791. data_attribute_name : self.options.data_attribute_name,
  792. misspelled_word_class: self.options.misspelled_word_class,
  793. 'options-to-restore': _editor.config.scayt_disableOptionsStorage,
  794. focused : _editor.editable().hasFocus, // #30260 we need to set focused=true if CKEditor is focused before SCAYT initialization
  795. ignoreElementsRegex : _editor.config.scayt_elementsToIgnore,
  796. minWordLength : _editor.config.scayt_minWordLength
  797. };
  798. if(_editor.config.scayt_serviceProtocol) {
  799. _scaytInstanceOptions['service_protocol'] = _editor.config.scayt_serviceProtocol;
  800. }
  801. if(_editor.config.scayt_serviceHost) {
  802. _scaytInstanceOptions['service_host'] = _editor.config.scayt_serviceHost;
  803. }
  804. if(_editor.config.scayt_servicePort) {
  805. _scaytInstanceOptions['service_port'] = _editor.config.scayt_servicePort;
  806. }
  807. if(_editor.config.scayt_servicePath) {
  808. _scaytInstanceOptions['service_path'] = _editor.config.scayt_servicePath;
  809. }
  810. var _scaytInstance = new SCAYT.CKSCAYT(_scaytInstanceOptions, function() {
  811. // success callback
  812. }, function() {
  813. // error callback
  814. }),
  815. wordsPrefix = 'word_';
  816. _scaytInstance.subscribe('suggestionListSend', function(data) {
  817. // TODO: 1. Maybe store suggestions for specific editor
  818. // TODO: 2. Fix issue with suggestion duplicates on on server
  819. //CKEDITOR.plugins.scayt.suggestions = data.suggestionList;
  820. var _wordsCollection = {},
  821. _suggestionList =[];
  822. for (var i = 0; i < data.suggestionList.length; i++) {
  823. if (!_wordsCollection[wordsPrefix + data.suggestionList[i]]) {
  824. _wordsCollection[wordsPrefix + data.suggestionList[i]] = data.suggestionList[i];
  825. _suggestionList.push(data.suggestionList[i]);
  826. }
  827. }
  828. CKEDITOR.plugins.scayt.suggestions = _suggestionList;
  829. });
  830. // if selection has changed programmatically by SCAYT we need to react appropriately
  831. _scaytInstance.subscribe('selectionIsChanged', function(data) {
  832. var selection = _editor.getSelection();
  833. if(selection.isLocked) {
  834. _editor.lockSelection();
  835. }
  836. });
  837. _editor.scayt = _scaytInstance;
  838. _editor.fire('scaytButtonState', _editor.readOnly ? CKEDITOR.TRISTATE_DISABLED : CKEDITOR.TRISTATE_ON);
  839. });
  840. },
  841. destroy: function(editor) {
  842. if(editor.scayt) {
  843. editor.scayt.destroy();
  844. }
  845. delete editor.scayt;
  846. editor.fire('scaytButtonState', CKEDITOR.TRISTATE_OFF);
  847. },
  848. loadScaytLibrary: function(editor, callback) {
  849. var self = this,
  850. date,
  851. timestamp,
  852. scaytUrl;
  853. if(typeof window.SCAYT === 'undefined' || typeof window.SCAYT.CKSCAYT !== 'function') {
  854. // no need to process load requests from same editor as it can cause bugs with
  855. // loading ckscayt app due to subsequent calls of some events
  856. if(this.loadingHelper[editor.name]) return;
  857. // add onLoad callbacks for editors while SCAYT is loading
  858. this.loadingHelper[editor.name] = callback;
  859. this.loadingHelper.loadOrder.push(editor.name);
  860. //creating unique timestamp for SCAYT URL
  861. date = new Date();
  862. timestamp = date.getTime();
  863. scaytUrl = editor.config.scayt_srcUrl + '?' + timestamp;
  864. CKEDITOR.scriptLoader.load(scaytUrl, function(success) {
  865. var editorName;
  866. CKEDITOR.fireOnce('scaytReady');
  867. for(var i = 0; i < self.loadingHelper.loadOrder.length; i++) {
  868. editorName = self.loadingHelper.loadOrder[i];
  869. if(typeof self.loadingHelper[editorName] === 'function') {
  870. self.loadingHelper[editorName](CKEDITOR.instances[editorName]);
  871. }
  872. delete self.loadingHelper[editorName];
  873. }
  874. self.loadingHelper.loadOrder = [];
  875. });
  876. } else if(window.SCAYT && typeof window.SCAYT.CKSCAYT === 'function') {
  877. CKEDITOR.fireOnce('scaytReady');
  878. if(!editor.scayt) {
  879. if(typeof callback === 'function') {
  880. callback(editor);
  881. }
  882. }
  883. }
  884. }
  885. };
  886. CKEDITOR.on('dialogDefinition', function(dialogDefinitionEvent) {
  887. if (dialogDefinitionEvent.data.name === 'scaytDialog') {
  888. var dialogDefinition = dialogDefinitionEvent.data.definition;
  889. dialogDefinition.dialog.on('cancel', function(cancelEvent) {
  890. return false;
  891. }, this, null, -1);
  892. }
  893. });
  894. CKEDITOR.on('scaytReady', function() {
  895. // Override editor.checkDirty method avoid CK checkDirty functionality to fix SCAYT issues with incorrect checkDirty behavior.
  896. if(CKEDITOR.config.scayt_handleCheckDirty === true) {
  897. var editorCheckDirty = CKEDITOR.editor.prototype;
  898. editorCheckDirty.checkDirty = CKEDITOR.tools.override(editorCheckDirty.checkDirty, function(org) {
  899. return function() {
  900. var retval = null,
  901. pluginStatus = CKEDITOR.plugins.scayt && CKEDITOR.plugins.scayt.state[this.name] && this.scayt,
  902. scaytInstance = this.scayt;
  903. if(!pluginStatus) {
  904. retval = org.call(this);
  905. } else {
  906. retval = (this.status == 'ready');
  907. if (retval) {
  908. var currentData = scaytInstance.removeMarkupFromString(this.getSnapshot()),
  909. prevData = scaytInstance.removeMarkupFromString(this._.previousValue);
  910. retval = (retval && (prevData !== currentData))
  911. }
  912. }
  913. return retval;
  914. };
  915. });
  916. editorCheckDirty.resetDirty = CKEDITOR.tools.override(editorCheckDirty.resetDirty, function(org) {
  917. return function() {
  918. var pluginStatus = CKEDITOR.plugins.scayt && CKEDITOR.plugins.scayt.state[this.name] && this.scayt,
  919. scaytInstance = this.scayt;//CKEDITOR.plugins.scayt.getScayt(this);
  920. if(!pluginStatus) {
  921. org.call(this);
  922. } else {
  923. this._.previousValue = scaytInstance.removeMarkupFromString(this.getSnapshot());
  924. }
  925. };
  926. });
  927. }
  928. if(CKEDITOR.config.scayt_handleUndoRedo === true) {
  929. var undoImagePrototype = CKEDITOR.plugins.undo.Image.prototype;
  930. // add backword compatibility for CKEDITOR 4.2. method equals was repleced on other method
  931. var equalsContentMethodName = (typeof undoImagePrototype.equalsContent == "function") ? 'equalsContent' : 'equals';
  932. undoImagePrototype[equalsContentMethodName] = CKEDITOR.tools.override(undoImagePrototype[equalsContentMethodName], function(org) {
  933. return function(otherImage) {
  934. var pluginState = CKEDITOR.plugins.scayt && CKEDITOR.plugins.scayt.state[otherImage.editor.name] && otherImage.editor.scayt,
  935. scaytInstance = otherImage.editor.scayt,
  936. thisContents = this.contents,
  937. otherContents = otherImage.contents,
  938. retval = null;
  939. // Making the comparison based on content without SCAYT word markers.
  940. if(pluginState) {
  941. this.contents = scaytInstance.removeMarkupFromString(thisContents) || '';
  942. otherImage.contents = scaytInstance.removeMarkupFromString(otherContents) || '';
  943. }
  944. var retval = org.apply(this, arguments);
  945. this.contents = thisContents;
  946. otherImage.contents = otherContents;
  947. return retval;
  948. };
  949. });
  950. }
  951. });
  952. /**
  953. * The parameter turns on/off SCAYT on the autostartup. If 'true', turns on SCAYT automatically after loading the editor.
  954. *
  955. * config.scayt_autoStartup = true;
  956. *
  957. * @cfg {Boolean} [scayt_autoStartup=false]
  958. * @member CKEDITOR.config
  959. */
  960. /**
  961. * The parameter defines the number of SCAYT suggestions to show in the main context menu.
  962. * Possible values are:
  963. *
  964. * * `0` (zero) &ndash; No suggestions are shown in the main context menu. All
  965. * entries will be listed in the "More Suggestions" sub-menu.
  966. * * Positive number &ndash; The maximum number of suggestions to show in the context
  967. * menu. Other entries will be shown in the "More Suggestions" sub-menu.
  968. * * Negative number &ndash; 5 suggestions are shown in the main context menu. All other
  969. * entries will be listed in the "More Suggestions" sub-menu.
  970. *
  971. * Examples:
  972. *
  973. * // Display only three suggestions in the main context menu.
  974. * config.scayt_maxSuggestions = 3;
  975. *
  976. * // Do not show the suggestions directly.
  977. * config.scayt_maxSuggestions = 0;
  978. *
  979. * @cfg {Number} [scayt_maxSuggestions=5]
  980. * @member CKEDITOR.config
  981. */
  982. /**
  983. * The parameter defines minimum length of the words that will be collected from editor's text for spell checking.
  984. * Possible value is any positive number.
  985. *
  986. * Examples:
  987. *
  988. * // Set minimum length of the words that will be collected from text.
  989. * config.scayt_minWordLength = 5;
  990. *
  991. * @cfg {Number} [scayt_minWordLength=4]
  992. * @member CKEDITOR.config
  993. */
  994. /**
  995. * The parameter sets the customer ID for SCAYT. Used for hosted users only. Required for migration from free
  996. * to trial or paid versions.
  997. *
  998. * // Load SCAYT using my customer ID.
  999. * config.scayt_customerId = 'your-encrypted-customer-id';
  1000. *
  1001. * @cfg {String} [scayt_customerId='1:WvF0D4-UtPqN1-43nkD4-NKvUm2-daQqk3-LmNiI-z7Ysb4-mwry24-T8YrS3-Q2tpq2']
  1002. * @member CKEDITOR.config
  1003. */
  1004. /**
  1005. * The parameter enables/disables the "More Suggestions" sub-menu in the context menu.
  1006. * Possible values are `'on'` and `'off'`.
  1007. *
  1008. * // Disables the "More Suggestions" sub-menu.
  1009. * config.scayt_moreSuggestions = 'off';
  1010. *
  1011. * @cfg {String} [scayt_moreSuggestions='on']
  1012. * @member CKEDITOR.config
  1013. */
  1014. /**
  1015. * The parameter customizes the display of SCAYT context menu commands ("Add Word", "Ignore",
  1016. * "Ignore All", "Options", "Languages", "Dictionaries" and "About").
  1017. * This must be a string with one or more of the following
  1018. * words separated by a pipe character (`'|'`):
  1019. *
  1020. * * `off` &ndash; disables all options.
  1021. * * `all` &ndash; enables all options.
  1022. * * `ignore` &ndash; enables the "Ignore" option.
  1023. * * `ignoreall` &ndash; enables the "Ignore All" option.
  1024. * * `add` &ndash; enables the "Add Word" option.
  1025. * * `option` &ndash; enables "Options" menu item.
  1026. * * `language` &ndash; enables "Languages" menu item.
  1027. * * `dictionary` &ndash; enables "Dictionaries" menu item.
  1028. * * `about` &ndash; enables "About" menu item.
  1029. *
  1030. * Note, that availability of 'Options', 'Languages' and 'Dictionaries' items
  1031. * depends on scayt_uiTabs option also.
  1032. * Example:
  1033. *
  1034. * // Show only "Add Word" and "Ignore All" in the context menu.
  1035. * config.scayt_contextCommands = 'add|ignoreall';
  1036. *
  1037. * @cfg {String} [scayt_contextCommands='ignore|ignoreall|add']
  1038. * @member CKEDITOR.config
  1039. */
  1040. /**
  1041. * The parameter sets the default spell checking language for SCAYT. Possible values are:
  1042. * `'en_US'`, `'en_GB'`, `'pt_BR'`, `'da_DK'`,
  1043. * `'nl_NL'`, `'en_CA'`, `'fi_FI'`, `'fr_FR'`,
  1044. * `'fr_CA'`, `'de_DE'`, `'el_GR'`, `'it_IT'`,
  1045. * `'nb_NO'`, `'pt_PT'`, `'es_ES'`, `'sv_SE'`.
  1046. *
  1047. * // Sets SCAYT to German.
  1048. * config.scayt_sLang = 'de_DE';
  1049. *
  1050. * @cfg {String} [scayt_sLang='en_US']
  1051. * @member CKEDITOR.config
  1052. */
  1053. /**
  1054. * The parameter customizes the SCAYT dialog and SCAYT toolbar menu to show particular tabs/items.
  1055. * This setting must contain a `1` (enabled) or `0`
  1056. * (disabled) value for each of the following entries, in this precise order,
  1057. * separated by a comma (`','`): `'Options'`, `'Languages'`, and `'Dictionary'`.
  1058. *
  1059. * // Hides the "Languages" tab.
  1060. * config.scayt_uiTabs = '1,0,1';
  1061. *
  1062. * @cfg {String} [scayt_uiTabs='1,1,1']
  1063. * @member CKEDITOR.config
  1064. */
  1065. /**
  1066. * The parameter allows to specify protocol for WSC service (ssrv.cgi) full path.
  1067. *
  1068. * // Defines protocol for WSC service (ssrv.cgi) full path.
  1069. * config.scayt_serviceProtocol = 'https';
  1070. *
  1071. * @cfg {String} [scayt_serviceProtocol='http']
  1072. * @member CKEDITOR.config
  1073. */
  1074. /**
  1075. * The parameter allows to specify host for WSC service (ssrv.cgi) full path.
  1076. *
  1077. * // Defines host for WSC service (ssrv.cgi) full path.
  1078. * config.scayt_serviceHost = 'my-host';
  1079. *
  1080. * @cfg {String} [scayt_serviceHost='svc.webspellchecker.net']
  1081. * @member CKEDITOR.config
  1082. */
  1083. /**
  1084. * The parameter allows to specify port for WSC service (ssrv.cgi) full path.
  1085. *
  1086. * // Defines port for WSC service (ssrv.cgi) full path.
  1087. * config.scayt_servicePort = '2330';
  1088. *
  1089. * @cfg {String} [scayt_servicePort='80']
  1090. * @member CKEDITOR.config
  1091. */
  1092. /**
  1093. * The parameter allows to specify path for WSC service (ssrv.cgi) full path.
  1094. *
  1095. * // Defines host for WSC service (ssrv.cgi) full path.
  1096. * config.scayt_servicePath = 'my-path/ssrv.cgi';
  1097. *
  1098. * @cfg {String} [scayt_servicePath='spellcheck31/script/ssrv.cgi']
  1099. * @member CKEDITOR.config
  1100. */
  1101. /**
  1102. * The parameter sets the URL to SCAYT core. Required to switch to the licensed version of SCAYT application.
  1103. *
  1104. * Further details available at [http://wiki.webspellchecker.net/doku.php?id=migration:hosredfreetolicensedck](http://wiki.webspellchecker.net/doku.php?id=migration:hosredfreetolicensedck)
  1105. *
  1106. * config.scayt_srcUrl = "http://my-host/spellcheck/lf/scayt/scayt.js";
  1107. *
  1108. * @cfg {String} [scayt_srcUrl='//svc.webspellchecker.net/spellcheck31/lf/scayt3/ckscayt/ckscayt.js']
  1109. * @member CKEDITOR.config
  1110. */
  1111. /**
  1112. * The parameter links SCAYT to custom dictionaries. This is a string containing dictionary IDs
  1113. * separated by commas (`','`). Available only for the licensed version.
  1114. *
  1115. * Further details at [http://wiki.webspellchecker.net/doku.php?id=installationandconfiguration:customdictionaries:licensed](http://wiki.webspellchecker.net/doku.php?id=installationandconfiguration:customdictionaries:licensed)
  1116. *
  1117. * config.scayt_customDictionaryIds = '3021,3456,3478';
  1118. *
  1119. * @cfg {String} [scayt_customDictionaryIds='']
  1120. * @member CKEDITOR.config
  1121. */
  1122. /**
  1123. * The parameter activates a User Dictionary in SCAYT. The user
  1124. * dictionary name must be used. Available only for the licensed version.
  1125. *
  1126. * config.scayt_userDictionaryName = 'MyDictionary';
  1127. *
  1128. * @cfg {String} [scayt_userDictionaryName='']
  1129. * @member CKEDITOR.config
  1130. */
  1131. /**
  1132. * The parameter defines the order SCAYT context menu items by groups.
  1133. * This must be a string with one or more of the following
  1134. * words separated by a pipe character (`'|'`):
  1135. *
  1136. * * `suggest` &ndash; main suggestion word list,
  1137. * * `moresuggest` &ndash; more suggestions word list,
  1138. * * `control` &ndash; SCAYT commands, such as "Ignore" and "Add Word".
  1139. *
  1140. * Example:
  1141. *
  1142. * config.scayt_contextMenuItemsOrder = 'moresuggest|control|suggest';
  1143. *
  1144. * @cfg {String} [scayt_contextMenuItemsOrder='suggest|moresuggest|control']
  1145. * @member CKEDITOR.config
  1146. */
  1147. /**
  1148. * If set to `true` &ndash; overrides checkDirty functionality of CK
  1149. * to fix SCAYT issues with incorrect checkDirty behavior. If set to `false`,
  1150. * provides better performance on big preloaded text.
  1151. *
  1152. * config.scayt_handleCheckDirty = 'false';
  1153. *
  1154. * @cfg {String} [scayt_handleCheckDirty='true']
  1155. * @member CKEDITOR.config
  1156. */
  1157. /**
  1158. * If set to `true` &ndash; overrides undo\redo functionality of CK
  1159. * to fix SCAYT issues with incorrect undo\redo behavior. If set to `false`,
  1160. * provides better performance on undo\redo text.
  1161. *
  1162. * config.scayt_handleUndoRedo = 'false';
  1163. *
  1164. * @cfg {String} [scayt_handleUndoRedo='true']
  1165. * @member CKEDITOR.config
  1166. */
  1167. /**
  1168. * Disabling of SCAYT Options storing during several sessions. Options storing will be turned off after page refresh.
  1169. *
  1170. * * `'options'` - disables all SCAYT Ignore options
  1171. * * `'ignore-all-caps-words'` - disables 'Ignore All-Caps Words' option
  1172. * * `'ignore-domain-names'` - disables 'Ignore Domain Names' option
  1173. * * `'ignore-words-with-mixed-cases'` - disables 'Ignore Words with Mixed Case' option
  1174. * * `'ignore-words-with-numbers'` - disables 'Ignore Words with Numbers' option
  1175. * * `'lang'` - disables SCAYT spell check language storing
  1176. * * `'all'` - disables all SCAYT options storing
  1177. *
  1178. * Example:
  1179. *
  1180. * // One options disabling.
  1181. * config.scayt_disableOptionsStorage = 'all';
  1182. *
  1183. * // Several options disabling.
  1184. * config.scayt_disableOptionsStorage = ['lang', 'ignore-domain-names', 'ignore-words-with-numbers'];
  1185. *
  1186. *
  1187. * @cfg {String|Array} [scayt_disableOptionsStorage ='']
  1188. * @member CKEDITOR.config
  1189. */
  1190. /**
  1191. * Specifies the names of tags that will be skipped while spell checking. This is a string containing tag names
  1192. * separated by commas (`','`). Please note that `'style'` tag would be added to specified tags list.
  1193. *
  1194. * config.scayt_elementsToIgnore = 'del,pre';
  1195. *
  1196. * @cfg {String} [scayt_elementsToIgnore='style']
  1197. * @member CKEDITOR.config
  1198. */