Adding spellchecker

This commit is contained in:
Martin Kamerbeek 2006-10-19 15:25:50 +00:00
parent fe664733cd
commit 9f232b4049
23 changed files with 2251 additions and 10 deletions

View file

@ -1,5 +1,5 @@
7.2.0
- Added server side spellchecker (Martin Kamerbeek / Procolix)
7.1.2

View file

@ -7,6 +7,15 @@ upgrading from one version to the next, or even between multiple
versions. Be sure to heed the warnings contained herein as they will
save you many hours of grief.
7.2.0
--------------------------------------------------------------------
* Server side spellchecking has been added to this release. You must
install Text::Aspell, and any dictionary you like. If you want to
use spellchecking you have to setup the dictionaries you want your
users to use in the config file. Also you'll have to check the
spellchecker checkbox in the RichEdit asset you're using.
7.0.8
--------------------------------------------------------------------
* 7.0.7 was released with a critical bug that broke the search engine

View file

@ -20,17 +20,21 @@ my $quiet; # this line required
my $session = start(); # this line required
# upgrade functions go here
createDictionaryStorage($session);
finish($session); # this line required
##-------------------------------------------------
#sub exampleFunction {
# my $session = shift;
# print "\tWe're doing some stuff here that you should know about.\n" unless ($quiet);
# # and here's our code
#}
#-------------------------------------------------
sub createDictionaryStorage {
my $session = shift;
print "\tCreating the directory for the personal dictionaries.\n" unless ($quiet);
my $dictionaryDirectory = $session->config->get('uploadsPath') .'/dictionaries';
mkdir $dictionaryDirectory unless (-e $dictionaryDirectory);
mkdir $dictionaryDirectory.'/oldIds' unless (-e $dictionaryDirectory.'/oldIds');
}

View file

@ -402,6 +402,23 @@
"WebGUI::Image::Graph::XYGraph::Line"
],
# Here you can define the dictionaries that are available through the tinyMCE spellchecker. You should set
# id to the name the dictionary is known by ASpell (eg. en or en_US or nl), use the name parameter to set
# the name the dictionary is displayed with in tinyMCE. To set the default dictionary please set the 'default'
# parameter.
#"availableDictionaries" : [
# {
# "id" : "en",
# "name" : "English",
# "default" : "1"
# },
# {
# "id" : "nl",
# "name" : "Dutch"
# }
#],
# Optional script to run upon successful login. The script can contain macros
# ex: /data/WebGUI/sbin/doLogin.pl --configFile=dev.localhost.localdomain.conf --loginPage=^PageUrl();

View file

@ -56,7 +56,10 @@ sub handler {
return Apache2::Const::DECLINED if ($r->uri =~ m/^$url/);
}
my $uploads = $config->get("uploadsURL");
if ($r->uri =~ m/^$uploads/) {
if ($r->uri =~ m!^uploads/dictionaries!) {
# Do not allow web-access to personal dictionaries.
$r->push_handlers(PerlAccessHandler => sub { return 401 } );
} elsif($r->uri =~ m/^$uploads/) {
$r->push_handlers(PerlAccessHandler => sub { return uploadsHandler($r, $configFile); } );
} else {
$r->push_handlers(PerlResponseHandler => sub { return contentHandler($r, $configFile); } );

View file

@ -19,6 +19,7 @@ use WebGUI::Asset;
use WebGUI::Form;
use WebGUI::Utility;
use WebGUI::International;
use Text::Aspell;
our @ISA = qw(WebGUI::Asset);
@ -217,6 +218,7 @@ sub getEditForm {
'fullscreen' => $i18n->get('fullscreen'),
'zoom' => $i18n->get('zoom'),
'print' => $i18n->get('print'),
'spellchecker' => $i18n->get('Server Side Spell Checker'),
# 'advlink' => "Advanced Link",
# 'spacer' => "Toolbar Spacer",
# 'separator' => "Toolbar Separator",
@ -229,6 +231,7 @@ sub getEditForm {
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td></td>
</tr>!,
$i18n->get('button'),
$i18n->get('row 1'),
@ -260,7 +263,11 @@ sub getEditForm {
value=>$key,
name=>"toolbarRow3",
checked=>$checked3
}).'</td>
}).'</td><td>';
if ($key eq 'spellchecker' && !($self->session->config->get('availableDictionaries'))) {
$buttonGrid .= $i18n->get('no dictionaries');
}
$buttonGrid .= '</td>
</tr>
';
}
@ -457,8 +464,14 @@ sub getRichEditor {
theme_advanced_toolbar_location => $self->getValue("toolbarLocation"),
theme_advanced_statusbar_location => "bottom",
valid_elements => $self->getValue("validElements"),
wg_userIsVisitor => $self->session->user->userId eq '1' ? 'true' : 'false',
);
foreach my $button (@toolbarButtons) {
if ($button eq "spellchecker" && $self->session->config->get('availableDictionaries')) {
push(@plugins,"spellchecker");
$config{spellchecker_languages} =
join(',', map { ($_->{default} ? '+' : '').$_->{name}.'='.$_->{id} } @{$self->session->config->get('availableDictionaries')});
}
push(@plugins,"table") if ($button eq "tablecontrols");
push(@plugins,"save") if ($button eq "save");
push(@plugins,"advhr") if ($button eq "advhr");
@ -516,6 +529,7 @@ sub getRichEditor {
push(@directives,$key." : '".$config{$key}."'");
}
}
$self->session->style->setScript($self->session->url->extras('tinymce2/jscripts/tiny_mce/tiny_mce.js'),{type=>"text/javascript"});
$self->session->style->setScript($self->session->url->extras("tinymce2/jscripts/webgui.js"),{type=>"text/javascript"});
return '<script type="text/javascript">

View file

@ -298,6 +298,10 @@ sub getOperations {
'moveColorDown' => 'WebGUI::Operation::Graphics',
'moveColorUp' => 'WebGUI::Operation::Graphics',
'removeColorFromPalette' => 'WebGUI::Operation::Graphics',
'spellCheck' => 'WebGUI::Operation::SpellCheck',
'suggestWords' => 'WebGUI::Operation::SpellCheck',
'addWordToDictionary' => 'WebGUI::Operation::SpellCheck',
};
}

View file

@ -0,0 +1,222 @@
package WebGUI::Operation::SpellCheck;
#-------------------------------------------------------------------
# WebGUI is Copyright 2001-2006 Plain Black Corporation.
#-------------------------------------------------------------------
# Please read the legal notices (docs/legal.txt) and the license
# (docs/license.txt) that came with this distribution before using
# this software.
#-------------------------------------------------------------------
# http://www.plainblack.com info@plainblack.com
#-------------------------------------------------------------------
use strict;
use Encode;
use Text::Aspell;
use WebGUI::Utility;
=head1 NAME
Package WebGUI::Operation::Spellcheck
=head1 DESCRIPTION
Operation for server side spellchecking functions.
=cut
#-------------------------------------------------------------------
=head2 _getSpeller ( session )
Returns an instanciated Text::Aspell object.
=head3 session
An instanciated session object.
=cut
sub _getSpeller {
my ($baseDir, $userDir, $homeDir);
my $session = shift;
my $speller = Text::Aspell->new;
# Get language
my $lang = $session->form->process('lang');
return undef unless (isIn($lang, map {m/^.*?:([^:]*):.*?$/} $speller->list_dictionaries));
# User homedir
my $userId = $session->user->userId;
$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;
}
#-------------------------------------------------------------------
=head2 _processOutput ( session, words, [ id, [ command ] ] )
Processes the wordlist and generates an XML string that the TinyMCE spellchecker
plugin can grok.
=head3 session
The instanciated session object.
=head3 words
An arrayref containing the words that you want to send back to the spellchecker
plugin.
=head3 id
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.
=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 = '<?xml version="1.0" encoding="utf-8" ?>'."\n";
if (scalar(@$words) == 0) {
$output .= '<res id="'.$id.'" cmd="'.$command.'"></res>';
}
else {
$output .= '<res id="'.$id.'" cmd="'.$command.'">'.encode_utf8(join(" ", @$words)).'</res>';
}
return $output;
}
#-------------------------------------------------------------------
=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.
=head3 session
The instanciated session object.
=cut
sub www_spellCheck {
my $session = shift;
my (@result, $output);
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);
}
1;

View file

@ -453,6 +453,16 @@ option is set to Yes, then all whitespace entered into the form will be preserve
lastUpdated => 1128830747,
},
'Server Side Spell Checker' => {
message => q|Server Side Spell Checker|,
lastUpdated => 0,
},
'no dictionaries' => {
message => q|No dictionaries are configured yet. Spellchecking will not be enabled before at least one is.|,
lastUpdated => 0,
},
};
1;

View file

@ -92,6 +92,7 @@ checkModule("URI::Escape","3.28");
checkModule("POSIX");
checkModule("List::Util");
checkModule("Color::Calc");
checkModule("Text::Aspell");
###################################
# Checking WebGUI

View file

@ -0,0 +1,5 @@
Version 1.0.1 (2006-05-05)
Since sourceforge has a serious bug when it comes to replacing files with the same name this release was necessary.
Goggle spellchecker class was added.
Version 1.0 (2006-05-03)
Official first release.

View file

@ -0,0 +1,339 @@
<?php
/* Version 0.9, 6th April 2003 - Simon Willison ( http://simon.incutio.com/ )
Manual: http://scripts.incutio.com/httpclient/
*/
class HttpClient {
// Request vars
var $host;
var $port;
var $path;
var $method;
var $postdata = '';
var $cookies = array();
var $referer;
var $accept = 'text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,image/jpeg,image/gif,*/*';
var $accept_encoding = 'gzip';
var $accept_language = 'en-us';
var $user_agent = 'Incutio HttpClient v0.9';
// Options
var $timeout = 20;
var $use_gzip = true;
var $persist_cookies = true; // If true, received cookies are placed in the $this->cookies array ready for the next request
// Note: This currently ignores the cookie path (and time) completely. Time is not important,
// but path could possibly lead to security problems.
var $persist_referers = true; // For each request, sends path of last request as referer
var $debug = false;
var $handle_redirects = true; // Auaomtically redirect if Location or URI header is found
var $max_redirects = 5;
var $headers_only = false; // If true, stops receiving once headers have been read.
// Basic authorization variables
var $username;
var $password;
// Response vars
var $status;
var $headers = array();
var $content = '';
var $errormsg;
// Tracker variables
var $redirect_count = 0;
var $cookie_host = '';
function HttpClient($host, $port=80) {
$this->host = $host;
$this->port = $port;
}
function get($path, $data = false) {
$this->path = $path;
$this->method = 'GET';
if ($data) {
$this->path .= '?'.$this->buildQueryString($data);
}
return $this->doRequest();
}
function post($path, $data) {
$this->path = $path;
$this->method = 'POST';
$this->postdata = $this->buildQueryString($data);
return $this->doRequest();
}
function buildQueryString($data) {
$querystring = '';
if (is_array($data)) {
// Change data in to postable data
foreach ($data as $key => $val) {
if (is_array($val)) {
foreach ($val as $val2) {
$querystring .= urlencode($key).'='.urlencode($val2).'&';
}
} else {
$querystring .= urlencode($key).'='.urlencode($val).'&';
}
}
$querystring = substr($querystring, 0, -1); // Eliminate unnecessary &
} else {
$querystring = $data;
}
return $querystring;
}
function doRequest() {
// Performs the actual HTTP request, returning true or false depending on outcome
if (!$fp = @fsockopen($this->host, $this->port, $errno, $errstr, $this->timeout)) {
// Set error message
switch($errno) {
case -3:
$this->errormsg = 'Socket creation failed (-3)';
case -4:
$this->errormsg = 'DNS lookup failure (-4)';
case -5:
$this->errormsg = 'Connection refused or timed out (-5)';
default:
$this->errormsg = 'Connection failed ('.$errno.')';
$this->errormsg .= ' '.$errstr;
$this->debug($this->errormsg);
}
return false;
}
socket_set_timeout($fp, $this->timeout);
$request = $this->buildRequest();
$this->debug('Request', $request);
fwrite($fp, $request);
// Reset all the variables that should not persist between requests
$this->headers = array();
$this->content = '';
$this->errormsg = '';
// Set a couple of flags
$inHeaders = true;
$atStart = true;
// Now start reading back the response
while (!feof($fp)) {
$line = fgets($fp, 4096);
if ($atStart) {
// Deal with first line of returned data
$atStart = false;
if (!preg_match('/HTTP\/(\\d\\.\\d)\\s*(\\d+)\\s*(.*)/', $line, $m)) {
$this->errormsg = "Status code line invalid: ".htmlentities($line);
$this->debug($this->errormsg);
return false;
}
$http_version = $m[1]; // not used
$this->status = $m[2];
$status_string = $m[3]; // not used
$this->debug(trim($line));
continue;
}
if ($inHeaders) {
if (trim($line) == '') {
$inHeaders = false;
$this->debug('Received Headers', $this->headers);
if ($this->headers_only) {
break; // Skip the rest of the input
}
continue;
}
if (!preg_match('/([^:]+):\\s*(.*)/', $line, $m)) {
// Skip to the next header
continue;
}
$key = strtolower(trim($m[1]));
$val = trim($m[2]);
// Deal with the possibility of multiple headers of same name
if (isset($this->headers[$key])) {
if (is_array($this->headers[$key])) {
$this->headers[$key][] = $val;
} else {
$this->headers[$key] = array($this->headers[$key], $val);
}
} else {
$this->headers[$key] = $val;
}
continue;
}
// We're not in the headers, so append the line to the contents
$this->content .= $line;
}
fclose($fp);
// If data is compressed, uncompress it
if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] == 'gzip') {
$this->debug('Content is gzip encoded, unzipping it');
$this->content = substr($this->content, 10); // See http://www.php.net/manual/en/function.gzencode.php
$this->content = gzinflate($this->content);
}
// If $persist_cookies, deal with any cookies
if ($this->persist_cookies && isset($this->headers['set-cookie']) && $this->host == $this->cookie_host) {
$cookies = $this->headers['set-cookie'];
if (!is_array($cookies)) {
$cookies = array($cookies);
}
foreach ($cookies as $cookie) {
if (preg_match('/([^=]+)=([^;]+);/', $cookie, $m)) {
$this->cookies[$m[1]] = $m[2];
}
}
// Record domain of cookies for security reasons
$this->cookie_host = $this->host;
}
// If $persist_referers, set the referer ready for the next request
if ($this->persist_referers) {
$this->debug('Persisting referer: '.$this->getRequestURL());
$this->referer = $this->getRequestURL();
}
// Finally, if handle_redirects and a redirect is sent, do that
if ($this->handle_redirects) {
if (++$this->redirect_count >= $this->max_redirects) {
$this->errormsg = 'Number of redirects exceeded maximum ('.$this->max_redirects.')';
$this->debug($this->errormsg);
$this->redirect_count = 0;
return false;
}
$location = isset($this->headers['location']) ? $this->headers['location'] : '';
$uri = isset($this->headers['uri']) ? $this->headers['uri'] : '';
if ($location || $uri) {
$url = parse_url($location.$uri);
// This will FAIL if redirect is to a different site
return $this->get($url['path']);
}
}
return true;
}
function buildRequest() {
$headers = array();
$headers[] = "{$this->method} {$this->path} HTTP/1.0"; // Using 1.1 leads to all manner of problems, such as "chunked" encoding
$headers[] = "Host: {$this->host}";
$headers[] = "User-Agent: {$this->user_agent}";
$headers[] = "Accept: {$this->accept}";
if ($this->use_gzip) {
$headers[] = "Accept-encoding: {$this->accept_encoding}";
}
$headers[] = "Accept-language: {$this->accept_language}";
if ($this->referer) {
$headers[] = "Referer: {$this->referer}";
}
// Cookies
if ($this->cookies) {
$cookie = 'Cookie: ';
foreach ($this->cookies as $key => $value) {
$cookie .= "$key=$value; ";
}
$headers[] = $cookie;
}
// Basic authentication
if ($this->username && $this->password) {
$headers[] = 'Authorization: BASIC '.base64_encode($this->username.':'.$this->password);
}
// If this is a POST, set the content type and length
if ($this->postdata) {
$headers[] = 'Content-Type: application/x-www-form-urlencoded';
$headers[] = 'Content-Length: '.strlen($this->postdata);
}
$request = implode("\r\n", $headers)."\r\n\r\n".$this->postdata;
return $request;
}
function getStatus() {
return $this->status;
}
function getContent() {
return $this->content;
}
function getHeaders() {
return $this->headers;
}
function getHeader($header) {
$header = strtolower($header);
if (isset($this->headers[$header])) {
return $this->headers[$header];
} else {
return false;
}
}
function getError() {
return $this->errormsg;
}
function getCookies() {
return $this->cookies;
}
function getRequestURL() {
$url = 'http://'.$this->host;
if ($this->port != 80) {
$url .= ':'.$this->port;
}
$url .= $this->path;
return $url;
}
// Setter methods
function setUserAgent($string) {
$this->user_agent = $string;
}
function setAuthorization($username, $password) {
$this->username = $username;
$this->password = $password;
}
function setCookies($array) {
$this->cookies = $array;
}
// Option setting methods
function useGzip($boolean) {
$this->use_gzip = $boolean;
}
function setPersistCookies($boolean) {
$this->persist_cookies = $boolean;
}
function setPersistReferers($boolean) {
$this->persist_referers = $boolean;
}
function setHandleRedirects($boolean) {
$this->handle_redirects = $boolean;
}
function setMaxRedirects($num) {
$this->max_redirects = $num;
}
function setHeadersOnly($boolean) {
$this->headers_only = $boolean;
}
function setDebug($boolean) {
$this->debug = $boolean;
}
// "Quick" static methods
function quickGet($url) {
$bits = parse_url($url);
$host = $bits['host'];
$port = isset($bits['port']) ? $bits['port'] : 80;
$path = isset($bits['path']) ? $bits['path'] : '/';
if (isset($bits['query'])) {
$path .= '?'.$bits['query'];
}
$client = new HttpClient($host, $port);
if (!$client->get($path)) {
return false;
} else {
return $client->getContent();
}
}
function quickPost($url, $data) {
$bits = parse_url($url);
$host = $bits['host'];
$port = isset($bits['port']) ? $bits['port'] : 80;
$path = isset($bits['path']) ? $bits['path'] : '/';
$client = new HttpClient($host, $port);
if (!$client->post($path, $data)) {
return false;
} else {
return $client->getContent();
}
}
function debug($msg, $object = false) {
if ($this->debug) {
print '<div style="border: 1px solid red; padding: 0.5em; margin: 0.5em;"><strong>HttpClient Debug:</strong> '.$msg;
if ($object) {
ob_start();
print_r($object);
$content = htmlentities(ob_get_contents());
ob_end_clean();
print '<pre>'.$content.'</pre>';
}
print '</div>';
}
}
}
?>

View file

@ -0,0 +1,73 @@
<?php
/* *
* Tiny Spelling Interface for TinyMCE Spell Checking.
*
* Copyright © 2006 Moxiecode Systems AB
*/
require_once("HttpClient.class.php");
class TinyGoogleSpell {
var $lang;
function TinyGoogleSpell(&$config, $lang, $mode, $spelling, $jargon, $encoding) {
$this->lang = $lang;
}
// Returns array with bad words or false if failed.
function checkWords($word_array) {
$words = array();
$wordstr = implode(' ', $word_array);
$matches = $this->_getMatches($wordstr);
for ($i=0; $i<count($matches); $i++)
$words[] = substr($wordstr, $matches[$i][1], $matches[$i][2]);
return $words;
}
// Returns array with suggestions or false if failed.
function getSuggestion($word) {
$sug = array();
$matches = $this->_getMatches($word);
if (count($matches) > 0)
$sug = explode("\t", $matches[0][4]);
return $sug;
}
function _getMatches($word_list) {
$xml = "";
// Setup HTTP Client
$client = new HttpClient('www.google.com');
$client->setUserAgent('Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR');
$client->setHandleRedirects(false);
$client->setDebug(false);
// Setup XML request
$xml .= '<?xml version="1.0" encoding="utf-8" ?>';
$xml .= '<spellrequest textalreadyclipped="0" ignoredups="0" ignoredigits="1" ignoreallcaps="1">';
$xml .= '<text>' . htmlentities($word_list) . '</text></spellrequest>';
// Execute HTTP Post to Google
if (!$client->post('/tbproxy/spell?lang=' . $this->lang, $xml)) {
$this->errorMsg[] = 'An error occurred: ' . $client->getError();
return array();
}
// Grab and parse content
$xml = $client->getContent();
preg_match_all('/<c o="([^"]*)" l="([^"]*)" s="([^"]*)">([^<]*)<\/c>/', $xml, $matches, PREG_SET_ORDER);
return $matches;
}
}
// Setup classname, should be the same as the name of the spellchecker class
$spellCheckerConfig['class'] = "TinyGoogleSpell";
?>

View file

@ -0,0 +1,64 @@
<?php
/* *
* Tiny Spelling Interface for TinyMCE Spell Checking.
*
* Copyright © 2006 Moxiecode Systems AB
*
*/
class TinyPSpell {
var $lang;
var $mode;
var $string;
var $plink;
var $errorMsg;
var $jargon;
var $spelling;
var $encoding;
function TinyPSpell(&$config, $lang, $mode, $spelling, $jargon, $encoding) {
$this->lang = $lang;
$this->mode = $mode;
$this->plink = false;
$this->errorMsg = array();
if (!function_exists("pspell_new")) {
$this->errorMsg[] = "PSpell not found.";
return;
}
$this->plink = pspell_new($this->lang, $this->spelling, $this->jargon, $this->encoding, $this->mode);
}
// Returns array with bad words or false if failed.
function checkWords($wordArray) {
if (!$this->plink) {
$this->errorMsg[] = "No PSpell link found for checkWords.";
return array();
}
$wordError = array();
foreach($wordArray as $word) {
if(!pspell_check($this->plink, trim($word)))
$wordError[] = $word;
}
return $wordError;
}
// Returns array with suggestions or false if failed.
function getSuggestion($word) {
if (!$this->plink) {
$this->errorMsg[] = "No PSpell link found for getSuggestion.";
return array();
}
return pspell_suggest($this->plink, $word);
}
}
// Setup classname, should be the same as the name of the spellchecker class
$spellCheckerConfig['class'] = "TinyPspell";
?>

View file

@ -0,0 +1,100 @@
<?php
/* *
* Tiny Spelling Interface for TinyMCE Spell Checking.
*
* Copyright © 2006 Moxiecode Systems AB
*
*/
class TinyPspellShell {
var $lang;
var $mode;
var $string;
var $error;
var $errorMsg;
var $cmd;
var $tmpfile;
var $jargon;
var $spelling;
var $encoding;
function TinyPspellShell(&$config, $lang, $mode, $spelling, $jargon, $encoding) {
$this->lang = $lang;
$this->mode = $mode;
$this->error = false;
$this->errorMsg = array();
$this->tmpfile = tempnam($config['tinypspellshell.tmp'], "tinyspell");
$this->cmd = "cat ". $this->tmpfile ." | " . $config['tinypspellshell.aspell'] . " -a --lang=". $this->lang;
}
// Returns array with bad words or false if failed.
function checkWords($wordArray) {
if ($fh = fopen($this->tmpfile, "w")) {
fwrite($fh, "!\n");
foreach($wordArray as $key => $value)
fwrite($fh, "^" . $value . "\n");
fclose($fh);
} else {
$this->errorMsg[] = "PSpell not found.";
return array();
}
$data = shell_exec($this->cmd);
$returnData = array();
$dataArr = preg_split("/\n/", $data, -1, PREG_SPLIT_NO_EMPTY);
foreach($dataArr as $dstr) {
$matches = array();
// Skip this line.
if (strpos($dstr, "@") === 0)
continue;
preg_match("/\& (.*) .* .*: .*/i", $dstr, $matches);
if (!empty($matches[1]))
$returnData[] = $matches[1];
}
return $returnData;
}
// Returns array with suggestions or false if failed.
function getSuggestion($word) {
if ($fh = fopen($this->tmpfile, "w")) {
fwrite($fh, "!\n");
fwrite($fh, "^$word\n");
fclose($fh);
} else
die("Error opening tmp file.");
$data = shell_exec($this->cmd);
$returnData = array();
$dataArr = preg_split("/\n/", $data, -1, PREG_SPLIT_NO_EMPTY);
foreach($dataArr as $dstr) {
$matches = array();
// Skip this line.
if (strpos($dstr, "@") === 0)
continue;
preg_match("/\& .* .* .*: (.*)/i", $dstr, $matches);
if (!empty($matches[1])) {
// For some reason, the exec version seems to add commas?
$returnData[] = str_replace(",", "", $matches[1]);
}
}
return $returnData;
}
}
// Setup classname, should be the same as the name of the spellchecker class
$spellCheckerConfig['class'] = "TinyPspellShell";
?>

View file

@ -0,0 +1,24 @@
<?php
$spellCheckerConfig = array();
// Spellchecker class use
require_once("classes/TinyPspellShell.class.php"); // Command line pspell
// require_once("classes/TinyGoogleSpell.class.php"); // Google web service
// require_once("classes/TinyPspell.class.php"); // Internal PHP version
// General settings
$spellCheckerConfig['enabled'] = false;
// Default settings
$spellCheckerConfig['default.language'] = 'en';
$spellCheckerConfig['default.mode'] = PSPELL_FAST;
// Normaly not required to configure
$spellCheckerConfig['default.spelling'] = "";
$spellCheckerConfig['default.jargon'] = "";
$spellCheckerConfig['default.encoding'] = "";
// Pspell shell specific settings
$spellCheckerConfig['tinypspellshell.aspell'] = '/usr/bin/aspell';
$spellCheckerConfig['tinypspellshell.tmp'] = '/tmp';
?>

View file

@ -0,0 +1,5 @@
.mceItemHiddenSpellWord {
background: url('../images/wline.gif') repeat-x bottom left;
bo2rder-bottom: 1px dashed red;
cursor: default;
}

View file

@ -0,0 +1,34 @@
.mceMsgBox {
border: 1px solid gray;
padding: 8px;
}
.mceMsgBox span {
vertical-align: top;
color: #555555;
}
/* Misc */
.mceBlockBox {
display: none;
position: absolute;
left: 0;
top: 0;
z-index: 100;
filter:progid:DXImageTransform.Microsoft.Alpha(style=0, opacity=60);
-moz-opacity:0.6;
opacity: 0.6;
background-color: white;
}
.mceMsgBox {
display: none;
z-index: 101;
position: absolute;
left: 0;
top: 0;
font-family: Arial, Verdana, Tahoma, Helvetica;
font-weight: bold;
font-size: 11px;
}

View file

@ -0,0 +1,601 @@
/**
* $RCSfile: editor_plugin_src.js,v $
* $Revision: 1.4 $
* $Date: 2006/03/24 17:24:50 $
*
* @author Moxiecode
* @copyright Copyright © 2004-2006, Moxiecode Systems AB, All rights reserved.
*/
tinyMCE.importPluginLanguagePack('spellchecker', 'en,sv,nn,nb');
// Plucin static class
var TinyMCE_SpellCheckerPlugin = {
_contextMenu : new TinyMCE_Menu(),
_menu : new TinyMCE_Menu(),
_counter : 0,
getInfo : function() {
return {
longname : 'Spellchecker',
author : 'Moxiecode Systems AB',
authorurl : 'http://tinymce.moxiecode.com',
infourl : 'http://tinymce.moxiecode.com/tinymce/docs/plugin_spellchecker.html',
version : tinyMCE.majorVersion + "." + tinyMCE.minorVersion
};
},
handleEvent : function(e) {
var elm = tinyMCE.isMSIE ? e.srcElement : e.target;
var inst = tinyMCE.selectedInstance, args = '';
var self = TinyMCE_SpellCheckerPlugin;
var cm = self._contextMenu;
var p, p2, x, y, sx, sy, h, elm;
// Handle click on word
if ((e.type == "click" || e.type == "contextmenu") && elm) {
do {
if (tinyMCE.getAttrib(elm, 'class') == "mceItemHiddenSpellWord") {
inst.spellCheckerElm = elm;
// Setup arguments
args += 'id=' + inst.editorId + "|" + (++self._counter);
args += '&cmd=suggest&check=' + escape(elm.innerHTML);
args += '&lang=' + escape(inst.spellCheckerLang);
args += '&op=suggestWords';
elm = inst.spellCheckerElm;
p = tinyMCE.getAbsPosition(inst.iframeElement);
p2 = tinyMCE.getAbsPosition(elm);
h = parseInt(elm.offsetHeight);
sx = inst.getBody().scrollLeft;
sy = inst.getBody().scrollTop;
x = p.absLeft + p2.absLeft - sx;
y = p.absTop + p2.absTop - sy + h;
cm.clear();
cm.addTitle(tinyMCE.getLang('lang_spellchecker_wait', '', true));
cm.show();
cm.moveTo(x, y);
inst.selection.selectNode(elm, false, false);
self._sendAjax('/', self._ajaxResponse, 'post', args);
tinyMCE.cancelEvent(e);
return false;
}
} while ((elm = elm.parentNode));
}
return true;
},
initInstance : function(inst) {
var self = TinyMCE_SpellCheckerPlugin, m = self._menu, cm = self._contextMenu, e;
tinyMCE.importCSS(inst.getDoc(), tinyMCE.baseURL + "/plugins/spellchecker/css/content.css");
if (!tinyMCE.hasMenu('spellcheckercontextmenu')) {
tinyMCE.importCSS(document, tinyMCE.baseURL + "/plugins/spellchecker/css/spellchecker.css");
cm.init({drop_menu : false});
tinyMCE.addMenu('spellcheckercontextmenu', cm);
}
if (!tinyMCE.hasMenu('spellcheckermenu')) {
m.init({});
tinyMCE.addMenu('spellcheckermenu', m);
}
inst.spellCheckerLang = 'en';
self._buildSettingsMenu(inst, null);
e = self._getBlockBoxLayer(inst).create('div', 'mceBlockBox', document.getElementById(inst.editorId + '_parent'));
self._getMsgBoxLayer(inst).create('div', 'mceMsgBox', document.getElementById(inst.editorId + '_parent'));
},
_getMsgBoxLayer : function(inst) {
if (!inst.spellCheckerMsgBoxL)
inst.spellCheckerMsgBoxL = new TinyMCE_Layer(inst.editorId + '_spellcheckerMsgBox', false);
return inst.spellCheckerMsgBoxL;
},
_getBlockBoxLayer : function(inst) {
if (!inst.spellCheckerBoxL)
inst.spellCheckerBoxL = new TinyMCE_Layer(inst.editorId + '_spellcheckerBlockBox', false);
return inst.spellCheckerBoxL;
},
_buildSettingsMenu : function(inst, lang) {
var i, ar = tinyMCE.getParam('spellchecker_languages', '+English=en').split(','), p;
var self = TinyMCE_SpellCheckerPlugin, m = self._menu, c;
m.clear();
m.addTitle(tinyMCE.getLang('lang_spellchecker_langs', '', true));
for (i=0; i<ar.length; i++) {
if (ar[i] != '') {
p = ar[i].split('=');
c = 'mceMenuCheckItem';
if (p[0].charAt(0) == '+') {
p[0] = p[0].substring(1);
if (lang == null) {
c = 'mceMenuSelectedItem';
inst.spellCheckerLang = p[1];
}
}
if (lang == p[1])
c = 'mceMenuSelectedItem';
m.add({text : p[0], js : "tinyMCE.execInstanceCommand('" + inst.editorId + "','mceSpellCheckerSetLang',false,'" + p[1] + "');", class_name : c});
}
}
},
setupContent : function(editor_id, body, doc) {
TinyMCE_SpellCheckerPlugin._removeWords(doc);
},
getControlHTML : function(cn) {
switch (cn) {
case "spellchecker":
return TinyMCE_SpellCheckerPlugin._getMenuButtonHTML(cn, 'lang_spellchecker_desc', '{$pluginurl}/images/spellchecker.gif', 'lang_spellchecker_desc', 'mceSpellCheckerMenu', 'mceSpellCheck');
}
return "";
},
/**
* Returns the HTML code for a normal button control.
*
* @param {string} id Button control id, this will be the suffix for the element id, the prefix is the editor id.
* @param {string} lang Language variable key name to insert as the title/alt of the button image.
* @param {string} img Image URL to insert, {$themeurl} and {$pluginurl} will be replaced.
* @param {string} mlang Language variable key name to insert as the title/alt of the menu button image.
* @param {string} mid Menu by id to display when the menu button is pressed.
* @param {string} cmd Command to execute when the user clicks the button.
* @param {string} ui Optional user interface boolean for command.
* @param {string} val Optional value for command.
* @return HTML code for a normal button based in input information.
* @type string
*/
_getMenuButtonHTML : function(id, lang, img, mlang, mid, cmd, ui, val) {
var h = '', m, x;
cmd = 'tinyMCE.hideMenus();tinyMCE.execInstanceCommand(\'{$editor_id}\',\'' + cmd + '\'';
if (typeof(ui) != "undefined" && ui != null)
cmd += ',' + ui;
if (typeof(val) != "undefined" && val != null)
cmd += ",'" + val + "'";
cmd += ');';
// Use tilemaps when enabled and found and never in MSIE since it loads the tile each time from cache if cahce is disabled
if (tinyMCE.getParam('button_tile_map') && (!tinyMCE.isMSIE || tinyMCE.isOpera) && (m = tinyMCE.buttonMap[id]) != null && (tinyMCE.getParam("language") == "en" || img.indexOf('$lang') == -1)) {
// Tiled button
x = 0 - (m * 20) == 0 ? '0' : 0 - (m * 20);
h += '<a id="{$editor_id}_' + id + '" href="javascript:' + cmd + '" onclick="' + cmd + 'return false;" onmousedown="return false;" class="mceTiledButton mceButtonNormal" target="_self">';
h += '<img src="{$themeurl}/images/spacer.gif" style="background-position: ' + x + 'px 0" title="{$' + lang + '}" />';
h += '<img src="{$themeurl}/images/button_menu.gif" title="{$' + lang + '}" class="mceMenuButton" onclick="' + mcmd + 'return false;" />';
h += '</a>';
} else {
if (tinyMCE.isMSIE && !tinyMCE.isOpera)
h += '<span id="{$editor_id}_' + id + '" class="mceMenuButton" onmouseover="tinyMCE.plugins.spellchecker._menuButtonEvent(\'over\',this);" onmouseout="tinyMCE.plugins.spellchecker._menuButtonEvent(\'out\',this);">';
else
h += '<span id="{$editor_id}_' + id + '" class="mceMenuButton">';
h += '<a href="javascript:' + cmd + '" onclick="' + cmd + 'return false;" onmousedown="return false;" class="mceMenuButtonNormal" target="_self">';
h += '<img src="' + img + '" title="{$' + lang + '}" /></a>';
h += '<a href="#" onclick="tinyMCE.plugins.spellchecker._toggleMenu(\'{$editor_id}\',\'' + mid + '\');return false;" onmousedown="return false;"><img src="{$themeurl}/images/button_menu.gif" title="{$' + lang + '}" class="mceMenuButton" />';
h += '</a></span>';
}
return h;
},
_menuButtonEvent : function(e, o) {
if (o.className == 'mceMenuButtonFocus')
return;
if (e == 'over')
o.className = o.className + ' mceMenuHover';
else
o.className = o.className.replace(/\s.*$/, '');
},
_toggleMenu : function(editor_id, id) {
var self = TinyMCE_SpellCheckerPlugin;
var e = document.getElementById(editor_id + '_spellchecker');
var inst = tinyMCE.getInstanceById(editor_id);
if (self._menu.isVisible()) {
tinyMCE.hideMenus();
return;
}
tinyMCE.lastMenuBtnClass = e.className.replace(/\s.*$/, '');
tinyMCE.switchClass(editor_id + '_spellchecker', 'mceMenuButtonFocus');
self._menu.moveRelativeTo(e, 'bl');
self._menu.moveBy(tinyMCE.isMSIE && !tinyMCE.isOpera ? 0 : 1, -1);
if (tinyMCE.isOpera)
self._menu.moveBy(0, -2);
self._onMenuEvent(inst, self._menu, 'show');
self._menu.show();
tinyMCE.lastSelectedMenuBtn = editor_id + '_spellchecker';
},
_onMenuEvent : function(inst, m, n) {
TinyMCE_SpellCheckerPlugin._buildSettingsMenu(inst, inst.spellCheckerLang);
},
execCommand : function(editor_id, element, command, user_interface, value) {
var inst = tinyMCE.getInstanceById(editor_id), self = TinyMCE_SpellCheckerPlugin, args = '', co, bb, mb, nl, i, e;
// Handle commands
switch (command) {
case "mceSpellCheck":
if (!inst.spellcheckerOn) {
inst.spellCheckerBookmark = inst.selection.getBookmark();
// Setup arguments
args += 'id=' + inst.editorId + "|" + (++self._counter);
args += '&cmd=spell&check=' + escape(self._getWordList(inst.getBody())).replace(/%20/g, '+');
args += '&lang=' + escape(inst.spellCheckerLang);
args += '&op=spellCheck';
co = document.getElementById(inst.editorId + '_parent').firstChild;
bb = self._getBlockBoxLayer(inst);
bb.moveRelativeTo(co, 'tl');
bb.resizeTo(co.offsetWidth, co.offsetHeight);
bb.show();
// Setup message box
mb = self._getMsgBoxLayer(inst);
e = mb.getElement();
e.innerHTML = '<span>' + tinyMCE.getLang('lang_spellchecker_swait', '', true) + '</span>';
mb.show();
mb.moveRelativeTo(co, 'cc');
if (tinyMCE.isMSIE && !tinyMCE.isOpera) {
nl = co.getElementsByTagName('select');
for (i=0; i<nl.length; i++)
nl[i].disabled = true;
}
inst.spellcheckerOn = true;
tinyMCE.switchClass(editor_id + '_spellchecker', 'mceMenuButtonSelected');
self._sendAjax('/', self._ajaxResponse, 'post', args);
} else {
self._removeWords(inst.getDoc());
inst.spellcheckerOn = false;
tinyMCE.switchClass(editor_id + '_spellchecker', 'mceMenuButton');
}
return true;
case "mceSpellCheckReplace":
if (inst.spellCheckerElm)
tinyMCE.setOuterHTML(inst.spellCheckerElm, value);
self._checkDone(inst);
self._contextMenu.hide();
self._menu.hide();
return true;
case "mceSpellCheckIgnore":
if (inst.spellCheckerElm)
self._removeWord(inst.spellCheckerElm);
self._checkDone(inst);
self._contextMenu.hide();
self._menu.hide();
return true;
case "mceSpellCheckIgnoreAll":
if (inst.spellCheckerElm)
self._removeWords(inst.getDoc(), inst.spellCheckerElm.innerHTML);
self._checkDone(inst);
self._contextMenu.hide();
self._menu.hide();
return true;
case "mceSpellCheckAddWord":
// Setup arguments
args += 'id=' + inst.editorId + "|" + (++self._counter);
args += '&cmd=addWord&check=' + escape(inst.spellCheckerElm.innerHTML.replace(/%20/g, '+'));
args += '&lang=' + escape(inst.spellCheckerLang);
args += '&op=addWordToDictionary'
if (inst.spellCheckerElm)
self._removeWords(inst.getDoc(), inst.spellCheckerElm.innerHTML);
self._sendAjax('/', self._ajaxResponse, 'post', args);
self._checkDone(inst);
self._contextMenu.hide();
self._menu.hide();
return true;
case "mceSpellCheckerSetLang":
tinyMCE.hideMenus();
inst.spellCheckerLang = value;
self._removeWords(inst.getDoc());
inst.spellcheckerOn = false;
tinyMCE.switchClass(editor_id + '_spellchecker', 'mceMenuButton');
return true;
}
// Pass to next handler in chain
return false;
},
cleanup : function(type, content, inst) {
switch (type) {
case "get_from_editor_dom":
TinyMCE_SpellCheckerPlugin._removeWords(content);
inst.spellcheckerOn = false;
break;
}
return content;
},
// Private plugin specific methods
_displayUI : function(inst) {
var self = TinyMCE_SpellCheckerPlugin;
var bb = self._getBlockBoxLayer(inst);
var mb = self._getMsgBoxLayer(inst);
var nl, i;
var co = document.getElementById(inst.editorId + '_parent').firstChild;
if (tinyMCE.isMSIE && !tinyMCE.isOpera) {
nl = co.getElementsByTagName('select');
for (i=0; i<nl.length; i++)
nl[i].disabled = false;
}
bb.hide();
mb.hide();
},
_ajaxResponse : function(xml) {
var el = xml ? xml.documentElement : null;
var inst = tinyMCE.selectedInstance, self = TinyMCE_SpellCheckerPlugin;
var cmd = el ? el.getAttribute("cmd") : null, err, id = el ? el.getAttribute("id") : null;
if (id)
inst = tinyMCE.getInstanceById(id.substring(0, id.indexOf('|')));
self._displayUI(inst);
// Ignore suggestions for other ajax responses
if (cmd == "suggest" && id != inst.editorId + "|" + self._counter)
return;
if (!el) {
inst.spellcheckerOn = false;
tinyMCE.switchClass(inst.editorId + '_spellchecker', 'mceMenuButton');
alert("Could not execute AJAX call, server didn't return valid a XML.");
return;
}
err = el.getAttribute("error");
if (err == "true") {
inst.spellcheckerOn = false;
tinyMCE.switchClass(inst.editorId + '_spellchecker', 'mceMenuButton');
alert(el.getAttribute("msg"));
return;
}
switch (cmd) {
case "spell":
if (xml.documentElement.firstChild) {
self._markWords(inst.getDoc(), inst.getBody(), el.firstChild.nodeValue.split(' '));
inst.selection.moveToBookmark(inst.spellCheckerBookmark);
} else
alert(tinyMCE.getLang('lang_spellchecker_no_mpell', '', true));
self._checkDone(inst);
break;
case "suggest":
self._buildMenu(el.firstChild ? el.firstChild.nodeValue.split(' ') : null, 10);
self._contextMenu.show();
break;
case "addWord":
if (el.firstChild) {
alert ('An error ocurred when adding a word:\n'+ el.firstChild.nodeValue);
}
break;
}
},
_getWordSeparators : function() {
var i, re = '', ch = tinyMCE.getParam('spellchecker_word_separator_chars', '\\s!"#$%&()*+,-./:;<=>?@[\]^_{|}§©«®±¶·¸»¼½¾¿×÷¤\u201d\u201c');
for (i=0; i<ch.length; i++)
re += '\\' + ch.charAt(i);
return re;
},
_getWordList : function(n) {
var i, x, s, nv = '', nl = tinyMCE.getNodeTree(n, new Array(), 3), wl = new Array();
var re = TinyMCE_SpellCheckerPlugin._getWordSeparators();
for (i=0; i<nl.length; i++)
nv += nl[i].nodeValue + " ";
nv = nv.replace(new RegExp('([0-9]|[' + re + '])', 'g'), ' ');
nv = tinyMCE.trim(nv.replace(/(\s+)/g, ' '));
nl = nv.split(/\s+/);
for (i=0; i<nl.length; i++) {
s = false;
for (x=0; x<wl.length; x++) {
if (wl[x] == nl[i]) {
s = true;
break;
}
}
if (!s)
wl[wl.length] = nl[i];
}
return wl.join(' ');
},
_removeWords : function(doc, word) {
var i, c, nl = doc.getElementsByTagName("span");
var self = TinyMCE_SpellCheckerPlugin;
var inst = tinyMCE.selectedInstance, b = inst ? inst.selection.getBookmark() : null;
word = typeof(word) == 'undefined' ? null : word;
for (i=nl.length-1; i>=0; i--) {
c = tinyMCE.getAttrib(nl[i], 'class');
if ((c == 'mceItemHiddenSpellWord' || c == 'mceItemHidden') && (word == null || nl[i].innerHTML == word))
self._removeWord(nl[i]);
}
if (b)
inst.selection.moveToBookmark(b);
},
_checkDone : function(inst) {
var i, w = 0, nl = inst.getDoc().getElementsByTagName("span")
var self = TinyMCE_SpellCheckerPlugin;
for (i=nl.length-1; i>=0; i--) {
c = tinyMCE.getAttrib(nl[i], 'class');
if (c == 'mceItemHiddenSpellWord')
w++;
}
if (w == 0) {
self._removeWords(inst.getDoc());
inst.spellcheckerOn = false;
tinyMCE.switchClass(inst.editorId + '_spellchecker', 'mceMenuButton');
}
},
_removeWord : function(e) {
tinyMCE.setOuterHTML(e, e.innerHTML);
},
_markWords : function(doc, n, wl) {
var i, nv, nn, nl = tinyMCE.getNodeTree(n, new Array(), 3);
var r1, r2, r3, r4, r5, w = '';
var re = TinyMCE_SpellCheckerPlugin._getWordSeparators();
for (i=0; i<wl.length; i++)
w += wl[i] + ((i == wl.length-1) ? '' : '|');
r1 = new RegExp('([' + re + '])(' + w + ')([' + re + '])', 'g');
r2 = new RegExp('^(' + w + ')', 'g');
r3 = new RegExp('(' + w + ')([' + re + ']?)$', 'g');
r4 = new RegExp('^(' + w + ')([' + re + ']?)$', 'g');
r5 = new RegExp('(' + w + ')([' + re + '])', 'g');
for (i=0; i<nl.length; i++) {
nv = nl[i].nodeValue;
if (r1.test(nv) || r2.test(nv) || r3.test(nv) || r4.test(nv)) {
nv = tinyMCE.xmlEncode(nv);
nv = nv.replace(r5, '<span class="mceItemHiddenSpellWord">$1</span>$2');
nv = nv.replace(r3, '<span class="mceItemHiddenSpellWord">$1</span>$2');
nn = doc.createElement('span');
nn.className = "mceItemHidden";
nn.innerHTML = nv;
// Remove old text node
nl[i].parentNode.replaceChild(nn, nl[i]);
}
}
},
_buildMenu : function(sg, max) {
var i, self = TinyMCE_SpellCheckerPlugin, cm = self._contextMenu;
cm.clear();
if (sg != null) {
cm.addTitle(tinyMCE.getLang('lang_spellchecker_sug', '', true));
for (i=0; i<sg.length && i<max; i++)
cm.addItem(sg[i], 'tinyMCE.execCommand("mceSpellCheckReplace",false,"' + sg[i] + '");');
cm.addSeparator();
if (!tinyMCE.getParam("wg_userIsVisitor")) {
cm.addItem('Add word to dictionary', 'tinyMCE.execCommand(\'mceSpellCheckAddWord\');');
}
cm.addItem(tinyMCE.getLang('lang_spellchecker_ignore_word', '', true), 'tinyMCE.execCommand(\'mceSpellCheckIgnore\');');
cm.addItem(tinyMCE.getLang('lang_spellchecker_ignore_words', '', true), 'tinyMCE.execCommand(\'mceSpellCheckIgnoreAll\');');
} else
cm.addTitle(tinyMCE.getLang('lang_spellchecker_no_sug', '', true));
cm.update();
},
_getAjaxHTTP : function() {
try {
return new ActiveXObject('Msxml2.XMLHTTP')
} catch (e) {
try {
return new ActiveXObject('Microsoft.XMLHTTP')
} catch (e) {
return new XMLHttpRequest();
}
}
},
/**
* Perform AJAX call.
*
* @param {string} u URL of AJAX service.
* @param {function} f Function to call when response arrives.
* @param {string} m Request method post or get.
* @param {Array} a Array with arguments to send.
*/
_sendAjax : function(u, f, m, a) {
var x = TinyMCE_SpellCheckerPlugin._getAjaxHTTP();
x.open(m, u, true);
x.onreadystatechange = function() {
if (x.readyState == 4)
f(x.responseXML);
};
if (m == 'post')
x.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
x.send(a);
}
};
// Register plugin
tinyMCE.addPlugin('spellchecker', TinyMCE_SpellCheckerPlugin);

View file

@ -0,0 +1,575 @@
/**
* $RCSfile: editor_plugin_src.js,v $
* $Revision: 1.4 $
* $Date: 2006/03/24 17:24:50 $
*
* @author Moxiecode
* @copyright Copyright © 2004-2006, Moxiecode Systems AB, All rights reserved.
*/
tinyMCE.importPluginLanguagePack('spellchecker', 'en,sv,nn,nb');
// Plucin static class
var TinyMCE_SpellCheckerPlugin = {
_contextMenu : new TinyMCE_Menu(),
_menu : new TinyMCE_Menu(),
_counter : 0,
getInfo : function() {
return {
longname : 'Spellchecker',
author : 'Moxiecode Systems AB',
authorurl : 'http://tinymce.moxiecode.com',
infourl : 'http://tinymce.moxiecode.com/tinymce/docs/plugin_spellchecker.html',
version : tinyMCE.majorVersion + "." + tinyMCE.minorVersion
};
},
handleEvent : function(e) {
var elm = tinyMCE.isMSIE ? e.srcElement : e.target;
var inst = tinyMCE.selectedInstance, args = '';
var self = TinyMCE_SpellCheckerPlugin;
var cm = self._contextMenu;
var p, p2, x, y, sx, sy, h, elm;
// Handle click on word
if ((e.type == "click" || e.type == "contextmenu") && elm) {
do {
if (tinyMCE.getAttrib(elm, 'class') == "mceItemHiddenSpellWord") {
inst.spellCheckerElm = elm;
// Setup arguments
args += 'id=' + inst.editorId + "|" + (++self._counter);
args += '&cmd=suggest&check=' + escape(elm.innerHTML);
args += '&lang=' + escape(inst.spellCheckerLang);
args += '&op=spellCheck';
elm = inst.spellCheckerElm;
p = tinyMCE.getAbsPosition(inst.iframeElement);
p2 = tinyMCE.getAbsPosition(elm);
h = parseInt(elm.offsetHeight);
sx = inst.getBody().scrollLeft;
sy = inst.getBody().scrollTop;
x = p.absLeft + p2.absLeft - sx;
y = p.absTop + p2.absTop - sy + h;
cm.clear();
cm.addTitle(tinyMCE.getLang('lang_spellchecker_wait', '', true));
cm.show();
cm.moveTo(x, y);
inst.selection.selectNode(elm, false, false);
self._sendAjax('/', self._ajaxResponse, 'post', args);
tinyMCE.cancelEvent(e);
return false;
}
} while ((elm = elm.parentNode));
}
return true;
},
initInstance : function(inst) {
var self = TinyMCE_SpellCheckerPlugin, m = self._menu, cm = self._contextMenu, e;
tinyMCE.importCSS(inst.getDoc(), tinyMCE.baseURL + "/plugins/spellchecker/css/content.css");
if (!tinyMCE.hasMenu('spellcheckercontextmenu')) {
tinyMCE.importCSS(document, tinyMCE.baseURL + "/plugins/spellchecker/css/spellchecker.css");
cm.init({drop_menu : false});
tinyMCE.addMenu('spellcheckercontextmenu', cm);
}
if (!tinyMCE.hasMenu('spellcheckermenu')) {
m.init({});
tinyMCE.addMenu('spellcheckermenu', m);
}
inst.spellCheckerLang = 'en';
self._buildSettingsMenu(inst, null);
e = self._getBlockBoxLayer(inst).create('div', 'mceBlockBox', document.getElementById(inst.editorId + '_parent'));
self._getMsgBoxLayer(inst).create('div', 'mceMsgBox', document.getElementById(inst.editorId + '_parent'));
},
_getMsgBoxLayer : function(inst) {
if (!inst.spellCheckerMsgBoxL)
inst.spellCheckerMsgBoxL = new TinyMCE_Layer(inst.editorId + '_spellcheckerMsgBox', false);
return inst.spellCheckerMsgBoxL;
},
_getBlockBoxLayer : function(inst) {
if (!inst.spellCheckerBoxL)
inst.spellCheckerBoxL = new TinyMCE_Layer(inst.editorId + '_spellcheckerBlockBox', false);
return inst.spellCheckerBoxL;
},
_buildSettingsMenu : function(inst, lang) {
var i, ar = tinyMCE.getParam('spellchecker_languages', '+English=en').split(','), p;
var self = TinyMCE_SpellCheckerPlugin, m = self._menu, c;
m.clear();
m.addTitle(tinyMCE.getLang('lang_spellchecker_langs', '', true));
for (i=0; i<ar.length; i++) {
if (ar[i] != '') {
p = ar[i].split('=');
c = 'mceMenuCheckItem';
if (p[0].charAt(0) == '+') {
p[0] = p[0].substring(1);
if (lang == null) {
c = 'mceMenuSelectedItem';
inst.spellCheckerLang = p[1];
}
}
if (lang == p[1])
c = 'mceMenuSelectedItem';
m.add({text : p[0], js : "tinyMCE.execInstanceCommand('" + inst.editorId + "','mceSpellCheckerSetLang',false,'" + p[1] + "');", class_name : c});
}
}
},
setupContent : function(editor_id, body, doc) {
TinyMCE_SpellCheckerPlugin._removeWords(doc);
},
getControlHTML : function(cn) {
switch (cn) {
case "spellchecker":
return TinyMCE_SpellCheckerPlugin._getMenuButtonHTML(cn, 'lang_spellchecker_desc', '{$pluginurl}/images/spellchecker.gif', 'lang_spellchecker_desc', 'mceSpellCheckerMenu', 'mceSpellCheck');
}
return "";
},
/**
* Returns the HTML code for a normal button control.
*
* @param {string} id Button control id, this will be the suffix for the element id, the prefix is the editor id.
* @param {string} lang Language variable key name to insert as the title/alt of the button image.
* @param {string} img Image URL to insert, {$themeurl} and {$pluginurl} will be replaced.
* @param {string} mlang Language variable key name to insert as the title/alt of the menu button image.
* @param {string} mid Menu by id to display when the menu button is pressed.
* @param {string} cmd Command to execute when the user clicks the button.
* @param {string} ui Optional user interface boolean for command.
* @param {string} val Optional value for command.
* @return HTML code for a normal button based in input information.
* @type string
*/
_getMenuButtonHTML : function(id, lang, img, mlang, mid, cmd, ui, val) {
var h = '', m, x;
cmd = 'tinyMCE.hideMenus();tinyMCE.execInstanceCommand(\'{$editor_id}\',\'' + cmd + '\'';
if (typeof(ui) != "undefined" && ui != null)
cmd += ',' + ui;
if (typeof(val) != "undefined" && val != null)
cmd += ",'" + val + "'";
cmd += ');';
// Use tilemaps when enabled and found and never in MSIE since it loads the tile each time from cache if cahce is disabled
if (tinyMCE.getParam('button_tile_map') && (!tinyMCE.isMSIE || tinyMCE.isOpera) && (m = tinyMCE.buttonMap[id]) != null && (tinyMCE.getParam("language") == "en" || img.indexOf('$lang') == -1)) {
// Tiled button
x = 0 - (m * 20) == 0 ? '0' : 0 - (m * 20);
h += '<a id="{$editor_id}_' + id + '" href="javascript:' + cmd + '" onclick="' + cmd + 'return false;" onmousedown="return false;" class="mceTiledButton mceButtonNormal" target="_self">';
h += '<img src="{$themeurl}/images/spacer.gif" style="background-position: ' + x + 'px 0" title="{$' + lang + '}" />';
h += '<img src="{$themeurl}/images/button_menu.gif" title="{$' + lang + '}" class="mceMenuButton" onclick="' + mcmd + 'return false;" />';
h += '</a>';
} else {
if (tinyMCE.isMSIE && !tinyMCE.isOpera)
h += '<span id="{$editor_id}_' + id + '" class="mceMenuButton" onmouseover="tinyMCE.plugins.spellchecker._menuButtonEvent(\'over\',this);" onmouseout="tinyMCE.plugins.spellchecker._menuButtonEvent(\'out\',this);">';
else
h += '<span id="{$editor_id}_' + id + '" class="mceMenuButton">';
h += '<a href="javascript:' + cmd + '" onclick="' + cmd + 'return false;" onmousedown="return false;" class="mceMenuButtonNormal" target="_self">';
h += '<img src="' + img + '" title="{$' + lang + '}" /></a>';
h += '<a href="#" onclick="tinyMCE.plugins.spellchecker._toggleMenu(\'{$editor_id}\',\'' + mid + '\');return false;" onmousedown="return false;"><img src="{$themeurl}/images/button_menu.gif" title="{$' + lang + '}" class="mceMenuButton" />';
h += '</a></span>';
}
return h;
},
_menuButtonEvent : function(e, o) {
if (o.className == 'mceMenuButtonFocus')
return;
if (e == 'over')
o.className = o.className + ' mceMenuHover';
else
o.className = o.className.replace(/\s.*$/, '');
},
_toggleMenu : function(editor_id, id) {
var self = TinyMCE_SpellCheckerPlugin;
var e = document.getElementById(editor_id + '_spellchecker');
var inst = tinyMCE.getInstanceById(editor_id);
if (self._menu.isVisible()) {
tinyMCE.hideMenus();
return;
}
tinyMCE.lastMenuBtnClass = e.className.replace(/\s.*$/, '');
tinyMCE.switchClass(editor_id + '_spellchecker', 'mceMenuButtonFocus');
self._menu.moveRelativeTo(e, 'bl');
self._menu.moveBy(tinyMCE.isMSIE && !tinyMCE.isOpera ? 0 : 1, -1);
if (tinyMCE.isOpera)
self._menu.moveBy(0, -2);
self._onMenuEvent(inst, self._menu, 'show');
self._menu.show();
tinyMCE.lastSelectedMenuBtn = editor_id + '_spellchecker';
},
_onMenuEvent : function(inst, m, n) {
TinyMCE_SpellCheckerPlugin._buildSettingsMenu(inst, inst.spellCheckerLang);
},
execCommand : function(editor_id, element, command, user_interface, value) {
var inst = tinyMCE.getInstanceById(editor_id), self = TinyMCE_SpellCheckerPlugin, args = '', co, bb, mb, nl, i, e;
// Handle commands
switch (command) {
case "mceSpellCheck":
if (!inst.spellcheckerOn) {
inst.spellCheckerBookmark = inst.selection.getBookmark();
// Setup arguments
args += 'id=' + inst.editorId + "|" + (++self._counter);
args += '&cmd=spell&check=' + escape(self._getWordList(inst.getBody())).replace(/%20/g, '+');
args += '&lang=' + escape(inst.spellCheckerLang);
args += '&op=spellCheck';
co = document.getElementById(inst.editorId + '_parent').firstChild;
bb = self._getBlockBoxLayer(inst);
bb.moveRelativeTo(co, 'tl');
bb.resizeTo(co.offsetWidth, co.offsetHeight);
bb.show();
// Setup message box
mb = self._getMsgBoxLayer(inst);
e = mb.getElement();
e.innerHTML = '<span>' + tinyMCE.getLang('lang_spellchecker_swait', '', true) + '</span>';
mb.show();
mb.moveRelativeTo(co, 'cc');
if (tinyMCE.isMSIE && !tinyMCE.isOpera) {
nl = co.getElementsByTagName('select');
for (i=0; i<nl.length; i++)
nl[i].disabled = true;
}
inst.spellcheckerOn = true;
tinyMCE.switchClass(editor_id + '_spellchecker', 'mceMenuButtonSelected');
self._sendAjax('/', self._ajaxResponse, 'post', args);
} else {
self._removeWords(inst.getDoc());
inst.spellcheckerOn = false;
tinyMCE.switchClass(editor_id + '_spellchecker', 'mceMenuButton');
}
return true;
case "mceSpellCheckReplace":
if (inst.spellCheckerElm)
tinyMCE.setOuterHTML(inst.spellCheckerElm, value);
self._checkDone(inst);
self._contextMenu.hide();
self._menu.hide();
return true;
case "mceSpellCheckIgnore":
if (inst.spellCheckerElm)
self._removeWord(inst.spellCheckerElm);
self._checkDone(inst);
self._contextMenu.hide();
self._menu.hide();
return true;
case "mceSpellCheckIgnoreAll":
if (inst.spellCheckerElm)
self._removeWords(inst.getDoc(), inst.spellCheckerElm.innerHTML);
self._checkDone(inst);
self._contextMenu.hide();
self._menu.hide();
return true;
case "mceSpellCheckerSetLang":
tinyMCE.hideMenus();
inst.spellCheckerLang = value;
self._removeWords(inst.getDoc());
inst.spellcheckerOn = false;
tinyMCE.switchClass(editor_id + '_spellchecker', 'mceMenuButton');
return true;
}
// Pass to next handler in chain
return false;
},
cleanup : function(type, content, inst) {
switch (type) {
case "get_from_editor_dom":
TinyMCE_SpellCheckerPlugin._removeWords(content);
inst.spellcheckerOn = false;
break;
}
return content;
},
// Private plugin specific methods
_displayUI : function(inst) {
var self = TinyMCE_SpellCheckerPlugin;
var bb = self._getBlockBoxLayer(inst);
var mb = self._getMsgBoxLayer(inst);
var nl, i;
var co = document.getElementById(inst.editorId + '_parent').firstChild;
if (tinyMCE.isMSIE && !tinyMCE.isOpera) {
nl = co.getElementsByTagName('select');
for (i=0; i<nl.length; i++)
nl[i].disabled = false;
}
bb.hide();
mb.hide();
},
_ajaxResponse : function(xml) {
var el = xml ? xml.documentElement : null;
var inst = tinyMCE.selectedInstance, self = TinyMCE_SpellCheckerPlugin;
var cmd = el ? el.getAttribute("cmd") : null, err, id = el ? el.getAttribute("id") : null;
if (id)
inst = tinyMCE.getInstanceById(id.substring(0, id.indexOf('|')));
self._displayUI(inst);
// Ignore suggestions for other ajax responses
if (cmd == "suggest" && id != inst.editorId + "|" + self._counter)
return;
if (!el) {
inst.spellcheckerOn = false;
tinyMCE.switchClass(inst.editorId + '_spellchecker', 'mceMenuButton');
alert("Could not execute AJAX call, server didn't return valid a XML.");
return;
}
err = el.getAttribute("error");
if (err == "true") {
inst.spellcheckerOn = false;
tinyMCE.switchClass(inst.editorId + '_spellchecker', 'mceMenuButton');
alert(el.getAttribute("msg"));
return;
}
switch (cmd) {
case "spell":
if (xml.documentElement.firstChild) {
self._markWords(inst.getDoc(), inst.getBody(), el.firstChild.nodeValue.split(' '));
inst.selection.moveToBookmark(inst.spellCheckerBookmark);
} else
alert(tinyMCE.getLang('lang_spellchecker_no_mpell', '', true));
self._checkDone(inst);
break;
case "suggest":
self._buildMenu(el.firstChild ? el.firstChild.nodeValue.split(' ') : null, 10);
self._contextMenu.show();
break;
}
},
_getWordSeparators : function() {
var i, re = '', ch = tinyMCE.getParam('spellchecker_word_separator_chars', '\\s!"#$%&()*+,-./:;<=>?@[\]^_{|}§©«®±¶·¸»¼½¾¿×÷¤\u201d\u201c');
for (i=0; i<ch.length; i++)
re += '\\' + ch.charAt(i);
return re;
},
_getWordList : function(n) {
var i, x, s, nv = '', nl = tinyMCE.getNodeTree(n, new Array(), 3), wl = new Array();
var re = TinyMCE_SpellCheckerPlugin._getWordSeparators();
for (i=0; i<nl.length; i++)
nv += nl[i].nodeValue + " ";
nv = nv.replace(new RegExp('([0-9]|[' + re + '])', 'g'), ' ');
nv = tinyMCE.trim(nv.replace(/(\s+)/g, ' '));
nl = nv.split(/\s+/);
for (i=0; i<nl.length; i++) {
s = false;
for (x=0; x<wl.length; x++) {
if (wl[x] == nl[i]) {
s = true;
break;
}
}
if (!s)
wl[wl.length] = nl[i];
}
return wl.join(' ');
},
_removeWords : function(doc, word) {
var i, c, nl = doc.getElementsByTagName("span");
var self = TinyMCE_SpellCheckerPlugin;
var inst = tinyMCE.selectedInstance, b = inst ? inst.selection.getBookmark() : null;
word = typeof(word) == 'undefined' ? null : word;
for (i=nl.length-1; i>=0; i--) {
c = tinyMCE.getAttrib(nl[i], 'class');
if ((c == 'mceItemHiddenSpellWord' || c == 'mceItemHidden') && (word == null || nl[i].innerHTML == word))
self._removeWord(nl[i]);
}
if (b)
inst.selection.moveToBookmark(b);
},
_checkDone : function(inst) {
var i, w = 0, nl = inst.getDoc().getElementsByTagName("span")
var self = TinyMCE_SpellCheckerPlugin;
for (i=nl.length-1; i>=0; i--) {
c = tinyMCE.getAttrib(nl[i], 'class');
if (c == 'mceItemHiddenSpellWord')
w++;
}
if (w == 0) {
self._removeWords(inst.getDoc());
inst.spellcheckerOn = false;
tinyMCE.switchClass(inst.editorId + '_spellchecker', 'mceMenuButton');
}
},
_removeWord : function(e) {
tinyMCE.setOuterHTML(e, e.innerHTML);
},
_markWords : function(doc, n, wl) {
var i, nv, nn, nl = tinyMCE.getNodeTree(n, new Array(), 3);
var r1, r2, r3, r4, r5, w = '';
var re = TinyMCE_SpellCheckerPlugin._getWordSeparators();
for (i=0; i<wl.length; i++)
w += wl[i] + ((i == wl.length-1) ? '' : '|');
r1 = new RegExp('([' + re + '])(' + w + ')([' + re + '])', 'g');
r2 = new RegExp('^(' + w + ')', 'g');
r3 = new RegExp('(' + w + ')([' + re + ']?)$', 'g');
r4 = new RegExp('^(' + w + ')([' + re + ']?)$', 'g');
r5 = new RegExp('(' + w + ')([' + re + '])', 'g');
for (i=0; i<nl.length; i++) {
nv = nl[i].nodeValue;
if (r1.test(nv) || r2.test(nv) || r3.test(nv) || r4.test(nv)) {
nv = tinyMCE.xmlEncode(nv);
nv = nv.replace(r5, '<span class="mceItemHiddenSpellWord">$1</span>$2');
nv = nv.replace(r3, '<span class="mceItemHiddenSpellWord">$1</span>$2');
nn = doc.createElement('span');
nn.className = "mceItemHidden";
nn.innerHTML = nv;
// Remove old text node
nl[i].parentNode.replaceChild(nn, nl[i]);
}
}
},
_buildMenu : function(sg, max) {
var i, self = TinyMCE_SpellCheckerPlugin, cm = self._contextMenu;
cm.clear();
if (sg != null) {
cm.addTitle(tinyMCE.getLang('lang_spellchecker_sug', '', true));
for (i=0; i<sg.length && i<max; i++)
cm.addItem(sg[i], 'tinyMCE.execCommand("mceSpellCheckReplace",false,"' + sg[i] + '");');
cm.addSeparator();
cm.addItem(tinyMCE.getLang('lang_spellchecker_ignore_word', '', true), 'tinyMCE.execCommand(\'mceSpellCheckIgnore\');');
cm.addItem(tinyMCE.getLang('lang_spellchecker_ignore_words', '', true), 'tinyMCE.execCommand(\'mceSpellCheckIgnoreAll\');');
} else
cm.addTitle(tinyMCE.getLang('lang_spellchecker_no_sug', '', true));
cm.update();
},
_getAjaxHTTP : function() {
try {
return new ActiveXObject('Msxml2.XMLHTTP')
} catch (e) {
try {
return new ActiveXObject('Microsoft.XMLHTTP')
} catch (e) {
return new XMLHttpRequest();
}
}
},
/**
* Perform AJAX call.
*
* @param {string} u URL of AJAX service.
* @param {function} f Function to call when response arrives.
* @param {string} m Request method post or get.
* @param {Array} a Array with arguments to send.
*/
_sendAjax : function(u, f, m, a) {
var x = TinyMCE_SpellCheckerPlugin._getAjaxHTTP();
x.open(m, u, true);
x.onreadystatechange = function() {
if (x.readyState == 4)
f(x.responseXML);
};
if (m == 'post')
x.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
x.send(a);
}
};
// Register plugin
tinyMCE.addPlugin('spellchecker', TinyMCE_SpellCheckerPlugin);

Binary file not shown.

After

Width:  |  Height:  |  Size: 591 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 B

View file

@ -0,0 +1,137 @@
<?php
/**
* $RCSfile: tinyspell.php,v $
* $Revision: 1.1 $
* $Date: 2006/03/14 17:33:47 $
*
* @author Moxiecode
* @copyright Copyright © 2004-2006, Moxiecode Systems AB, All rights reserved.
*/
require_once("config.php");
$id = sanitize($_POST['id'], "loose");
if (!$spellCheckerConfig['enabled']) {
header('Content-type: text/xml; charset=utf-8');
echo '<?xml version="1.0" encoding="utf-8" ?><res id="' . $id . '" error="true" msg="You must enable the spellchecker by modifying the config.php file." />';
die;
}
// Basic config
$defaultLanguage = $spellCheckerConfig['default.language'];
$defaultMode = $spellCheckerConfig['default.mode'];
// Normaly not required to configure
$defaultSpelling = $spellCheckerConfig['default.spelling'];
$defaultJargon = $spellCheckerConfig['default.jargon'];
$defaultEncoding = $spellCheckerConfig['default.encoding'];
$outputType = "xml"; // Do not change
// Get input parameters.
$check = $_POST['check'];
$cmd = sanitize($_POST['cmd']);
$lang = sanitize($_POST['lang'], "strict");
$mode = sanitize($_POST['mode'], "strict");
$spelling = sanitize($_POST['spelling'], "strict");
$jargon = sanitize($_POST['jargon'], "strict");
$encoding = sanitize($_POST['encoding'], "strict");
$sg = sanitize($_POST['sg'], "bool");
$words = array();
$validRequest = true;
if (empty($check))
$validRequest = false;
if (empty($lang))
$lang = $defaultLanguage;
if (empty($mode))
$mode = $defaultMode;
if (empty($spelling))
$spelling = $defaultSpelling;
if (empty($jargon))
$jargon = $defaultJargon;
if (empty($encoding))
$encoding = $defaultEncoding;
function sanitize($str, $type="strict") {
switch ($type) {
case "strict":
$str = preg_replace("/[^a-zA-Z0-9_\-]/i", "", $str);
break;
case "loose":
$str = preg_replace("/</i", "&gt;", $str);
$str = preg_replace("/>/i", "&lt;", $str);
break;
case "bool":
if ($str == "true" || $str == true)
$str = true;
else
$str = false;
break;
}
return $str;
}
/////////////////////////// Hierboven alleen maar snaity checking. Verders niets. /////////////////
$result = array();
$tinyspell = new $spellCheckerConfig['class']($spellCheckerConfig, $lang, $mode, $spelling, $jargon, $encoding);
if (count($tinyspell->errorMsg) == 0) {
switch($cmd) {
case "spell":
// Space for non-exec version and \n for the exec version.
$words = preg_split("/ |\n/", $check, -1, PREG_SPLIT_NO_EMPTY);
$result = $tinyspell->checkWords($words);
break;
case "suggest":
$result = $tinyspell->getSuggestion($check);
break;
default:
// Just use this for now.
$tinyspell->errorMsg[] = "No command.";
$outputType = $outputType . "error";
break;
}
} else
$outputType = $outputType . "error";
if (!$result)
$result = array();
// Output data
switch($outputType) {
case "xml":
header('Content-type: text/xml; charset=utf-8');
echo '<?xml version="1.0" encoding="utf-8" ?>';
echo "\n";
if (count($result) == 0)
echo '<res id="' . $id . '" cmd="'. $cmd .'" />';
else
echo '<res id="' . $id . '" cmd="'. $cmd .'">'. utf8_encode(implode(" ", $result)) .'</res>';
break;
case "xmlerror";
header('Content-type: text/xml; charset=utf-8');
echo '<?xml version="1.0" encoding="utf-8" ?>';
echo "\n";
echo '<res id="' . $id . '" cmd="'. $cmd .'" error="true" msg="'. implode(" ", $tinyspell->errorMsg) .'" />';
break;
case "html":
var_dump($result);
break;
case "htmlerror":
echo "Error";
break;
}
?>