diff --git a/docs/changelog/7.x.x.txt b/docs/changelog/7.x.x.txt index 2301cf43c..0973b10b8 100644 --- a/docs/changelog/7.x.x.txt +++ b/docs/changelog/7.x.x.txt @@ -18,6 +18,7 @@ - fixed: DEmote in toolbar menu has PROmote url - fixed: EMS purge now functions correctly - improve behavior of preload.perl for custom lib dirs not ending in lib + - fixed: Server side spellchecker doesn't work 7.5.18 - fixed: Collateral Image Manager broken in Firefox 3 diff --git a/lib/WebGUI/Asset/RichEdit.pm b/lib/WebGUI/Asset/RichEdit.pm index 9fb320daa..111c47aee 100644 --- a/lib/WebGUI/Asset/RichEdit.pm +++ b/lib/WebGUI/Asset/RichEdit.pm @@ -20,9 +20,6 @@ use WebGUI::Form; use WebGUI::Utility; use WebGUI::International; use JSON; -BEGIN { - eval { require Text::Aspell }; # Optional -} our @ISA = qw(WebGUI::Asset); @@ -494,7 +491,9 @@ sub getRichEditor { ); foreach my $button (@toolbarButtons) { if ($button eq "spellchecker" && $self->session->config->get('availableDictionaries')) { - push(@plugins,"spellchecker"); + push(@plugins,"-wgspellchecker"); + $loadPlugins{wgspellchecker} = $self->session->url->extras("tinymce-webgui/plugins/wgspellchecker/editor_plugin.js"); + $config{spellchecker_rpc_url} = $self->session->url->gateway('', "op=spellCheck"); $config{spellchecker_languages} = join(',', map { ($_->{default} ? '+' : '').$_->{name}.'='.$_->{id} } @{$self->session->config->get('availableDictionaries')}); } diff --git a/lib/WebGUI/Operation/SpellCheck.pm b/lib/WebGUI/Operation/SpellCheck.pm index bfea2fcea..1ed056918 100644 --- a/lib/WebGUI/Operation/SpellCheck.pm +++ b/lib/WebGUI/Operation/SpellCheck.pm @@ -11,14 +11,21 @@ package WebGUI::Operation::SpellCheck; #------------------------------------------------------------------- use strict; -use Encode; -# Optional, but if unavailable, spell checking will have no effect. -eval 'use Text::Aspell'; use WebGUI::Utility; +use File::Path qw(mkpath); +# Optional, but if unavailable, spell checking will have no effect. +my $spellerAvailable; +BEGIN { + eval { + require Text::Aspell; + }; + $spellerAvailable = 1 + unless $@; +}; =head1 NAME -Package WebGUI::Operation::Spellcheck +Package WebGUI::Operation::SpellCheck =head1 DESCRIPTION @@ -28,7 +35,7 @@ Operation for server side spellchecking functions. #------------------------------------------------------------------- -=head2 _getSpeller ( session ) +=head2 _getSpeller ( session , language ) Returns an instanciated Text::Aspell object. @@ -36,99 +43,145 @@ Returns an instanciated Text::Aspell object. An instanciated session object. +=head3 language + +The language code to use for spell checking. + =cut sub _getSpeller { - my ($baseDir, $userDir, $homeDir); - my $session = shift; - return undef unless Text::Aspell->can('new'); - my $speller = Text::Aspell->new; + my $session = shift; + my $lang = shift; + die "Server side spellcheck not available\n" + unless $spellerAvailable; + # Get language + my $speller = Text::Aspell->new; + die "Language not available in server side spellcheck" + unless (isIn($lang, map {m/^.*?:([^:]*):.*?$/} $speller->list_dictionaries)); - # Get language - my $lang = $session->form->process('lang'); - return undef unless (isIn($lang, map {m/^.*?:([^:]*):.*?$/} $speller->list_dictionaries)); + # User homedir + my $homeDir = $session->config->get('uploadsPath').'/dictionaries/'; - # User homedir - my $userId = $session->user->userId; + my $userId = $session->user->userId; + if (length($userId) < 22) { + $homeDir .= "oldIds/$userId"; + } + else { + $userId =~ m/^(.{2})(.{2})/; + $homeDir .= "$1/$2/$userId"; + } + mkpath($homeDir) unless (-e $homeDir); - $baseDir = $session->config->get('uploadsPath').'/dictionaries/'; - - if (length($userId) < 22) { - $userDir = 'oldIds/'.$userId; - - mkdir($baseDir.$userDir) unless (-e $baseDir.$userDir); - } else { - $userDir = $userId; - $userDir =~ s/^(.{2})(.{2})*$/$1\/$2\/$userId/; - - mkdir($baseDir.$1) unless (-e $baseDir.$1); - mkdir($baseDir.$1.'/'.$2) unless (-e $baseDir.$1.'/'.$2); - } - - $homeDir = $baseDir.$userDir; - - mkdir($homeDir) unless (-e $homeDir); - - # Set speller options. - $speller->set_option('home-dir', $homeDir); - $speller->set_option('lang', $lang); - - return $speller; + # Set speller options. + $speller->set_option('home-dir', $homeDir); + $speller->set_option('lang', $lang); + return $speller; } #------------------------------------------------------------------- -=head2 _processOutput ( session, words, [ id, [ command ] ] ) +=head2 addWord ( $session, $language, $word ) -Processes the wordlist and generates an XML string that the TinyMCE spellchecker -plugin can grok. +Adds a word sent by the tinymce spellchecker plugin to the personal dictionary +of the the current user. -=head3 session +=head3 $session -The instanciated session object. +The instanciated session object -=head3 words +=head3 $language -An arrayref containing the words that you want to send back to the spellchecker -plugin. +The dictionary language to use. -=head3 id +=head3 $word -The id that the tinyMCE spellchecker plugin assigined to this specific action. -If not specified the value of the formparam 'id' will be sent. - -=head3 command - -The spellchecker plugin command that has been issued. If omitted the value of -formparam 'cmd' will be used. +The word to add to the dictionary. =cut -sub _processOutput { - my $session = shift; - my $words = shift || []; - my $id = shift || $session->form->process('id'); - my $command = shift || $session->form->process('cmd'); - - $session->http->setMimeType('text/xml; charset=utf-8'); - my $output = ''."\n"; +sub addWord { + my $session = shift; + my $language = shift; + my $word = shift; + die "You must be logged in to add words to your dictionary.\n:" + if ($session->user->userId eq '1'); + my $speller = _getSpeller($session, $language); + $speller->add_to_personal($word); + $speller->save_all_word_lists; + return 1; +} - if (scalar(@$words) == 0) { - $output .= ''; - } - else { - $output .= ''.encode_utf8(join(" ", @$words)).''; - } +#------------------------------------------------------------------- - return $output; +=head2 checkWords ( $session, $language, \@words ) + +Check the spelling on a list of words and returns a list of misspelled words as an array reference + +=head3 $session + +The instanciated session object + +=head3 $language + +The dictionary language to use. + +=head3 \@word + +The words to check the spelling of as an array reference. + +=cut + +sub checkWords { + my $session = shift; + my $language = shift; + my $words = shift; + my $speller = _getSpeller($session, $language); + my @result; + foreach my $word (@$words) { + unless ($speller->check($word)) { + push(@result, $word); + } + } + return \@result; +} + +#------------------------------------------------------------------- + +=head2 getSuggestions ( $session, $language, $word ) + +Returns a list of suggested words for a misspelled word sent by the +tinyMCE spellchecker as an array reference. + +=head3 $session + +The instanciated session object. + +=head3 $language + +The dictionary language to use. + +=head3 $word + +The misspelled word to get suggestions for. + +=cut + +sub getSuggestions { + my $session = shift; + my $language = shift; + my $word = shift; + my $speller = _getSpeller($session, $language); + my @result = $speller->suggest($word); + return \@result; } #------------------------------------------------------------------- =head2 www_spellCheck ( session ) -Fetches the the text to be checked as sent by the tinyMCE spellchecker and -returns a list of erroneous words in the correct XML format. +Fetches the JSON data sent by the TinyMCE spell checker and dispatches +to the correct sub to handle each request type. Encodes the result +as a JSON string to be sent back to the client. =head3 session @@ -137,87 +190,37 @@ The instanciated session object. =cut sub www_spellCheck { - my $session = shift; - my (@result, $output); + my $session = shift; + # JSON data is sent directly as POST data, read it into a scalar then decode + my $data = ''; + while ($session->request->read(my $buffer, 1024)) { + $data .= $buffer; + } + my $params = JSON->new->utf8->decode($data); - my $speller = _getSpeller($session); - return _processOutput($session) unless (defined($speller)); - - # Set speller options? - - # Get form params - my $check = $session->form->process('check'); - my $command = $session->form->process('cmd'); - my $language = $session->form->process('lang'); - my $mode = $session->form->process('mode'); - my $id = $session->form->process('id'); - - # Check it! - my @words = split(/\s/, $check); - - foreach my $word (@words) { - unless ($speller->check($word)) { - push(@result, $word); - } - } - - return _processOutput($session, \@result); -} - -#------------------------------------------------------------------- - -=head2 www_suggestWords ( session ) - -Returns a list of suggested words in the correct XML format for a misspelled -word sent by the tinyMCE spellchecker. - -=head3 session - -The instanciated session object. - -=cut - -sub www_suggestWords { - my $session = shift; - - my $speller = _getSpeller($session); - return _processOutput($session) unless (defined($speller)); - my $check = $session->form->process('check'); - - my @result = $speller->suggest($check); - - return _processOutput($session, \@result); -} - -#------------------------------------------------------------------- - -=head2 www_addWordToDictionary ( session ) - -Adds a word sent by the tinymce spellchecker plugin to the personal dictionary -of the the current user. - -=head3 session - -The instanciated session object - -=cut - -sub www_addWordToDictionary { - my $session = shift; - - # Visitors do not have a personal dictionary - return _processOutput($session, ['You must be logged in to add words to your dictionary.']) if ($session->user->userId eq '1'); - - my $speller = _getSpeller($session); - return _processOutput($session) unless (defined($speller)); - my $check = $session->form->process('check'); - - if ($check) { - $speller->add_to_personal($check); - $speller->save_all_word_lists; - } - - return _processOutput($session); + my $result; + # dispatch to different subs based on the 'method' in the JSON data + my %dispatch = ( + checkWords => \&checkWords, + getSuggestions => \&getSuggestions, + addWord => \&addWord, + ); + if (exists $dispatch{$params->{method}}) { + eval { + # get results from sub and build result data + $result = { result => $dispatch{$params->{method}}->($session, @{ $params->{params} }) }; + }; + if ($@) { + $result = {error => {errstr => $@}}; + } + } + else { + $result = {error => {errstr => "Invalid request"}}; + } + # add request id and send to client as JSON blob + $result->{id} = $params->{id}; + $session->http->setMimeType("text/plain; charset=utf-8"); + return JSON->new->encode($result); } 1; diff --git a/www/extras/tinymce-webgui/plugins/wgspellchecker/add-word.diff b/www/extras/tinymce-webgui/plugins/wgspellchecker/add-word.diff new file mode 100644 index 000000000..aea3a1c33 --- /dev/null +++ b/www/extras/tinymce-webgui/plugins/wgspellchecker/add-word.diff @@ -0,0 +1,48 @@ +diff --git a/editor_plugin_src.js b/editor_plugin_src.js +index 4e9ba99..a96e310 100644 +--- a/editor_plugin_src.js ++++ b/editor_plugin_src.js +@@ -7,6 +7,7 @@ + + (function() { + var JSONRequest = tinymce.util.JSONRequest, each = tinymce.each, DOM = tinymce.DOM; ++ tinymce.PluginManager.requireLangPack('wgspellchecker'); + + tinymce.create('tinymce.plugins.SpellcheckerPlugin', { + getInfo : function() { +@@ -269,6 +270,16 @@ + } + }); + ++ m.add({ ++ title : 'spellchecker.add_word', ++ onclick : function() { ++ t._sendRPC('addWord', [t.selectedLang, dom.decode(e.target.innerHTML)], function(r) { ++ t._removeWords(dom.decode(e.target.innerHTML)); ++ t._checkDone(); ++ }); ++ } ++ }); ++ + m.update(); + }); + +@@ -333,5 +344,5 @@ + }); + + // Register plugin +- tinymce.PluginManager.add('spellchecker', tinymce.plugins.SpellcheckerPlugin); +-})(); +\ No newline at end of file ++ tinymce.PluginManager.add('wgspellchecker', tinymce.plugins.SpellcheckerPlugin); ++})(); +diff --git a/dev/null b/langs/en.js +new file mode 100644 +index 0000000..602b23c +--- /dev/null ++++ b/langs/en.js +@@ -0,0 +1,4 @@ ++tinyMCE.addI18n('en.spellchecker',{ ++ add_word : 'Add word to dictionary' ++}); ++ diff --git a/www/extras/tinymce-webgui/plugins/wgspellchecker/css/content.css b/www/extras/tinymce-webgui/plugins/wgspellchecker/css/content.css new file mode 100644 index 000000000..24efa0217 --- /dev/null +++ b/www/extras/tinymce-webgui/plugins/wgspellchecker/css/content.css @@ -0,0 +1 @@ +.mceItemHiddenSpellWord {background:url(../img/wline.gif) repeat-x bottom left; cursor:default;} diff --git a/www/extras/tinymce-webgui/plugins/wgspellchecker/editor_plugin.js b/www/extras/tinymce-webgui/plugins/wgspellchecker/editor_plugin.js new file mode 100644 index 000000000..b57bba602 --- /dev/null +++ b/www/extras/tinymce-webgui/plugins/wgspellchecker/editor_plugin.js @@ -0,0 +1 @@ +(function(){var JSONRequest=tinymce.util.JSONRequest,each=tinymce.each,DOM=tinymce.DOM;tinymce.PluginManager.requireLangPack('wgspellchecker');tinymce.create('tinymce.plugins.SpellcheckerPlugin',{getInfo:function(){return{longname:'Spellchecker',author:'Moxiecode Systems AB',authorurl:'http://tinymce.moxiecode.com',infourl:'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/spellchecker',version:tinymce.majorVersion+"."+tinymce.minorVersion}},init:function(ed,url){var t=this,cm;t.url=url;t.editor=ed;ed.addCommand('mceSpellCheck',function(){if(!t.active){ed.setProgressState(1);t._sendRPC('checkWords',[t.selectedLang,t._getWords()],function(r){if(r.length>0){t.active=1;t._markWords(r);ed.setProgressState(0);ed.nodeChanged()}else{ed.setProgressState(0);ed.windowManager.alert('spellchecker.no_mpell')}})}else t._done()});ed.onInit.add(function(){ed.dom.loadCSS(url+'/css/content.css')});ed.onClick.add(t._showMenu,t);ed.onContextMenu.add(t._showMenu,t);ed.onBeforeGetContent.add(function(){if(t.active)t._removeWords()});ed.onNodeChange.add(function(ed,cm){cm.setActive('spellchecker',t.active)});ed.onSetContent.add(function(){t._done()});ed.onBeforeGetContent.add(function(){t._done()});ed.onBeforeExecCommand.add(function(ed,cmd){if(cmd=='mceFullScreen')t._done()});t.languages={};each(ed.getParam('spellchecker_languages','+English=en,Danish=da,Dutch=nl,Finnish=fi,French=fr,German=de,Italian=it,Polish=pl,Portuguese=pt,Spanish=es,Swedish=sv','hash'),function(v,k){if(k.indexOf('+')===0){k=k.substring(1);t.selectedLang=v}t.languages[k]=v})},createControl:function(n,cm){var t=this,c,ed=t.editor;if(n=='spellchecker'){c=cm.createSplitButton(n,{title:'spellchecker.desc',cmd:'mceSpellCheck',scope:t});c.onRenderMenu.add(function(c,m){m.add({title:'spellchecker.langs','class':'mceMenuItemTitle'}).setDisabled(1);each(t.languages,function(v,k){var o={icon:1},mi;o.onclick=function(){mi.setSelected(1);t.selectedItem.setSelected(0);t.selectedItem=mi;t.selectedLang=v};o.title=k;mi=m.add(o);mi.setSelected(v==t.selectedLang);if(v==t.selectedLang)t.selectedItem=mi})});return c}},_walk:function(n,f){var d=this.editor.getDoc(),w;if(d.createTreeWalker){w=d.createTreeWalker(n,NodeFilter.SHOW_TEXT,null,false);while((n=w.nextNode())!=null)f.call(this,n)}else tinymce.walk(n,f,'childNodes')},_getSeparators:function(){var re='',i,str=this.editor.getParam('spellchecker_word_separator_chars','\\s!"#$%&()*+,-./:;<=>?@[\]^_{|}§©«®±¶·¸»¼½¾¿×÷¤\u201d\u201c');for(i=0;i$1$2');v=v.replace(r3,'$1$2');dom.replace(dom.create('span',{'class':'mceItemHidden'},v),n)}}});se.moveToBookmark(b)},_showMenu:function(ed,e){var t=this,ed=t.editor,m=t._menu,p1,dom=ed.dom,vp=dom.getViewPort(ed.getWin());if(!m){p1=DOM.getPos(ed.getContentAreaContainer());m=ed.controlManager.createDropMenu('spellcheckermenu',{offset_x:p1.x,offset_y:p1.y,'class':'noIcons'});t._menu=m}if(dom.hasClass(e.target,'mceItemHiddenSpellWord')){m.removeAll();m.add({title:'spellchecker.wait','class':'mceMenuItemTitle'}).setDisabled(1);t._sendRPC('getSuggestions',[t.selectedLang,dom.decode(e.target.innerHTML)],function(r){m.removeAll();if(r.length>0){m.add({title:'spellchecker.sug','class':'mceMenuItemTitle'}).setDisabled(1);each(r,function(v){m.add({title:v,onclick:function(){dom.replace(ed.getDoc().createTextNode(v),e.target);t._checkDone()}})});m.addSeparator()}else m.add({title:'spellchecker.no_sug','class':'mceMenuItemTitle'}).setDisabled(1);m.add({title:'spellchecker.ignore_word',onclick:function(){dom.remove(e.target,1);t._checkDone()}});m.add({title:'spellchecker.ignore_words',onclick:function(){t._removeWords(dom.decode(e.target.innerHTML));t._checkDone()}});m.add({title:'spellchecker.add_word',onclick:function(){t._sendRPC('addWord',[t.selectedLang,dom.decode(e.target.innerHTML)],function(r){t._removeWords(dom.decode(e.target.innerHTML));t._checkDone()})}});m.update()});ed.selection.select(e.target);p1=dom.getPos(e.target);m.showMenu(p1.x,p1.y+e.target.offsetHeight-vp.y);return tinymce.dom.Event.cancel(e)}else m.hideMenu()},_checkDone:function(){var t=this,ed=t.editor,dom=ed.dom,o;each(dom.select('span'),function(n){if(n&&dom.hasClass(n,'mceItemHiddenSpellWord')){o=true;return false}});if(!o)t._done()},_done:function(){var t=this,la=t.active;if(t.active){t.active=0;t._removeWords();if(t._menu)t._menu.hideMenu();if(la)t.editor.nodeChanged()}},_sendRPC:function(m,p,cb){var t=this,url=t.editor.getParam("spellchecker_rpc_url","{backend}");if(url=='{backend}'){t.editor.setProgressState(0);alert('Please specify: spellchecker_rpc_url');return}JSONRequest.sendRPC({url:url,method:m,params:p,success:cb,error:function(e,x){t.editor.setProgressState(0);t.editor.windowManager.alert(e.errstr||('Error response: '+x.responseText))}})}});tinymce.PluginManager.add('wgspellchecker',tinymce.plugins.SpellcheckerPlugin)})(); \ No newline at end of file diff --git a/www/extras/tinymce-webgui/plugins/wgspellchecker/editor_plugin_src.js b/www/extras/tinymce-webgui/plugins/wgspellchecker/editor_plugin_src.js new file mode 100644 index 000000000..a96e310ab --- /dev/null +++ b/www/extras/tinymce-webgui/plugins/wgspellchecker/editor_plugin_src.js @@ -0,0 +1,348 @@ +/** + * $Id: editor_plugin_src.js 425 2007-11-21 15:17:39Z spocke $ + * + * @author Moxiecode + * @copyright Copyright © 2004-2008, Moxiecode Systems AB, All rights reserved. + */ + +(function() { + var JSONRequest = tinymce.util.JSONRequest, each = tinymce.each, DOM = tinymce.DOM; + tinymce.PluginManager.requireLangPack('wgspellchecker'); + + tinymce.create('tinymce.plugins.SpellcheckerPlugin', { + getInfo : function() { + return { + longname : 'Spellchecker', + author : 'Moxiecode Systems AB', + authorurl : 'http://tinymce.moxiecode.com', + infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/spellchecker', + version : tinymce.majorVersion + "." + tinymce.minorVersion + }; + }, + + init : function(ed, url) { + var t = this, cm; + + t.url = url; + t.editor = ed; + + // Register commands + ed.addCommand('mceSpellCheck', function() { + if (!t.active) { + ed.setProgressState(1); + t._sendRPC('checkWords', [t.selectedLang, t._getWords()], function(r) { + if (r.length > 0) { + t.active = 1; + t._markWords(r); + ed.setProgressState(0); + ed.nodeChanged(); + } else { + ed.setProgressState(0); + ed.windowManager.alert('spellchecker.no_mpell'); + } + }); + } else + t._done(); + }); + + ed.onInit.add(function() { + ed.dom.loadCSS(url + '/css/content.css'); + }); + + ed.onClick.add(t._showMenu, t); + ed.onContextMenu.add(t._showMenu, t); + ed.onBeforeGetContent.add(function() { + if (t.active) + t._removeWords(); + }); + + ed.onNodeChange.add(function(ed, cm) { + cm.setActive('spellchecker', t.active); + }); + + ed.onSetContent.add(function() { + t._done(); + }); + + ed.onBeforeGetContent.add(function() { + t._done(); + }); + + ed.onBeforeExecCommand.add(function(ed, cmd) { + if (cmd == 'mceFullScreen') + t._done(); + }); + + // Find selected language + t.languages = {}; + each(ed.getParam('spellchecker_languages', '+English=en,Danish=da,Dutch=nl,Finnish=fi,French=fr,German=de,Italian=it,Polish=pl,Portuguese=pt,Spanish=es,Swedish=sv', 'hash'), function(v, k) { + if (k.indexOf('+') === 0) { + k = k.substring(1); + t.selectedLang = v; + } + + t.languages[k] = v; + }); + }, + + createControl : function(n, cm) { + var t = this, c, ed = t.editor; + + if (n == 'spellchecker') { + c = cm.createSplitButton(n, {title : 'spellchecker.desc', cmd : 'mceSpellCheck', scope : t}); + + c.onRenderMenu.add(function(c, m) { + m.add({title : 'spellchecker.langs', 'class' : 'mceMenuItemTitle'}).setDisabled(1); + each(t.languages, function(v, k) { + var o = {icon : 1}, mi; + + o.onclick = function() { + mi.setSelected(1); + t.selectedItem.setSelected(0); + t.selectedItem = mi; + t.selectedLang = v; + }; + + o.title = k; + mi = m.add(o); + mi.setSelected(v == t.selectedLang); + + if (v == t.selectedLang) + t.selectedItem = mi; + }) + }); + + return c; + } + }, + + // Internal functions + + _walk : function(n, f) { + var d = this.editor.getDoc(), w; + + if (d.createTreeWalker) { + w = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false); + + while ((n = w.nextNode()) != null) + f.call(this, n); + } else + tinymce.walk(n, f, 'childNodes'); + }, + + _getSeparators : function() { + var re = '', i, str = this.editor.getParam('spellchecker_word_separator_chars', '\\s!"#$%&()*+,-./:;<=>?@[\]^_{|}§©«®±¶·¸»¼½¾¿×÷¤\u201d\u201c'); + + // Build word separator regexp + for (i=0; i$1$2'); + v = v.replace(r3, '$1$2'); + + dom.replace(dom.create('span', {'class' : 'mceItemHidden'}, v), n); + } + } + }); + + se.moveToBookmark(b); + }, + + _showMenu : function(ed, e) { + var t = this, ed = t.editor, m = t._menu, p1, dom = ed.dom, vp = dom.getViewPort(ed.getWin()); + + if (!m) { + p1 = DOM.getPos(ed.getContentAreaContainer()); + //p2 = DOM.getPos(ed.getContainer()); + + m = ed.controlManager.createDropMenu('spellcheckermenu', { + offset_x : p1.x, + offset_y : p1.y, + 'class' : 'noIcons' + }); + + t._menu = m; + } + + if (dom.hasClass(e.target, 'mceItemHiddenSpellWord')) { + m.removeAll(); + m.add({title : 'spellchecker.wait', 'class' : 'mceMenuItemTitle'}).setDisabled(1); + + t._sendRPC('getSuggestions', [t.selectedLang, dom.decode(e.target.innerHTML)], function(r) { + m.removeAll(); + + if (r.length > 0) { + m.add({title : 'spellchecker.sug', 'class' : 'mceMenuItemTitle'}).setDisabled(1); + each(r, function(v) { + m.add({title : v, onclick : function() { + dom.replace(ed.getDoc().createTextNode(v), e.target); + t._checkDone(); + }}); + }); + + m.addSeparator(); + } else + m.add({title : 'spellchecker.no_sug', 'class' : 'mceMenuItemTitle'}).setDisabled(1); + + m.add({ + title : 'spellchecker.ignore_word', + onclick : function() { + dom.remove(e.target, 1); + t._checkDone(); + } + }); + + m.add({ + title : 'spellchecker.ignore_words', + onclick : function() { + t._removeWords(dom.decode(e.target.innerHTML)); + t._checkDone(); + } + }); + + m.add({ + title : 'spellchecker.add_word', + onclick : function() { + t._sendRPC('addWord', [t.selectedLang, dom.decode(e.target.innerHTML)], function(r) { + t._removeWords(dom.decode(e.target.innerHTML)); + t._checkDone(); + }); + } + }); + + m.update(); + }); + + ed.selection.select(e.target); + p1 = dom.getPos(e.target); + m.showMenu(p1.x, p1.y + e.target.offsetHeight - vp.y); + + return tinymce.dom.Event.cancel(e); + } else + m.hideMenu(); + }, + + _checkDone : function() { + var t = this, ed = t.editor, dom = ed.dom, o; + + each(dom.select('span'), function(n) { + if (n && dom.hasClass(n, 'mceItemHiddenSpellWord')) { + o = true; + return false; + } + }); + + if (!o) + t._done(); + }, + + _done : function() { + var t = this, la = t.active; + + if (t.active) { + t.active = 0; + t._removeWords(); + + if (t._menu) + t._menu.hideMenu(); + + if (la) + t.editor.nodeChanged(); + } + }, + + _sendRPC : function(m, p, cb) { + var t = this, url = t.editor.getParam("spellchecker_rpc_url", "{backend}"); + + if (url == '{backend}') { + t.editor.setProgressState(0); + alert('Please specify: spellchecker_rpc_url'); + return; + } + + JSONRequest.sendRPC({ + url : url, + method : m, + params : p, + success : cb, + error : function(e, x) { + t.editor.setProgressState(0); + t.editor.windowManager.alert(e.errstr || ('Error response: ' + x.responseText)); + } + }); + } + }); + + // Register plugin + tinymce.PluginManager.add('wgspellchecker', tinymce.plugins.SpellcheckerPlugin); +})(); diff --git a/www/extras/tinymce-webgui/plugins/wgspellchecker/img/wline.gif b/www/extras/tinymce-webgui/plugins/wgspellchecker/img/wline.gif new file mode 100644 index 000000000..7d0a4dbca Binary files /dev/null and b/www/extras/tinymce-webgui/plugins/wgspellchecker/img/wline.gif differ diff --git a/www/extras/tinymce-webgui/plugins/wgspellchecker/langs/en.js b/www/extras/tinymce-webgui/plugins/wgspellchecker/langs/en.js new file mode 100644 index 000000000..602b23c24 --- /dev/null +++ b/www/extras/tinymce-webgui/plugins/wgspellchecker/langs/en.js @@ -0,0 +1,4 @@ +tinyMCE.addI18n('en.spellchecker',{ + add_word : 'Add word to dictionary' +}); +