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'
+});
+