added TinyMCE spellchecker plugin, configured to use GoogleSpell

release-0.6
svncommit 18 years ago
parent 3acbc508ba
commit 6649b1f0a5

@ -0,0 +1,11 @@
Version 1.0.2 (2006-08-02)
Added new spellchecker_report_mispellings option, contributed by Jeremy B.
Fixed various regexp bugs and issues. Some where contributed by Jeremy B.
Fixed the Google speller class so it uses curl, patch contributed by Yuriy Kramar.
Fixed encoding issues with language specific characters, patch contributed by codepit.
Fixed bug where the spellchecker wasn't working in MSIE if the editor was placed in a P tag.
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.

@ -0,0 +1,107 @@
<?php
/* *
* Tiny Spelling Interface for TinyMCE Spell Checking.
*
* Copyright © 2006 Moxiecode Systems AB
*/
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[] = $this->unhtmlentities(mb_substr($wordstr, $matches[$i][1], $matches[$i][2], "UTF-8"));
return $words;
}
function unhtmlentities($string) {
$string = preg_replace('~&#x([0-9a-f]+);~ei', 'chr(hexdec("\\1"))', $string);
$string = preg_replace('~&#([0-9]+);~e', 'chr(\\1)', $string);
$trans_tbl = get_html_translation_table(HTML_ENTITIES);
$trans_tbl = array_flip($trans_tbl);
return strtr($string, $trans_tbl);
}
// Returns array with suggestions or false if failed.
function getSuggestion($word) {
$sug = array();
$matches = $this->_getMatches($word);
if (count($matches) > 0)
$sug = explode("\t", utf8_encode($this->unhtmlentities($matches[0][4])));
return $sug;
}
function _xmlChars($string) {
$trans = get_html_translation_table(HTML_ENTITIES, ENT_QUOTES);
foreach ($trans as $k => $v)
$trans[$k] = "&#".ord($k).";";
return strtr($string, $trans);
}
function _getMatches($word_list) {
$server = "www.google.com";
$port = 443;
$path = "/tbproxy/spell?lang=" . $this->lang . "&hl=en";
$host = "www.google.com";
$url = "https://" . $server;
// Setup XML request
$xml = '<?xml version="1.0" encoding="utf-8" ?><spellrequest textalreadyclipped="0" ignoredups="0" ignoredigits="1" ignoreallcaps="1"><text>' . $word_list . '</text></spellrequest>';
$header = "POST ".$path." HTTP/1.0 \r\n";
$header .= "MIME-Version: 1.0 \r\n";
$header .= "Content-type: application/PTI26 \r\n";
$header .= "Content-length: ".strlen($xml)." \r\n";
$header .= "Content-transfer-encoding: text \r\n";
$header .= "Request-number: 1 \r\n";
$header .= "Document-type: Request \r\n";
$header .= "Interface-Version: Test 1.4 \r\n";
$header .= "Connection: close \r\n\r\n";
$header .= $xml;
//$this->_debugData($xml);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $header);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
$xml = curl_exec($ch);
curl_close($ch);
//$this->_debugData($xml);
// Grab and parse content
preg_match_all('/<c o="([^"]*)" l="([^"]*)" s="([^"]*)">([^<]*)<\/c>/', $xml, $matches, PREG_SET_ORDER);
return $matches;
}
function _debugData($data) {
$fh = @fopen("debug.log", 'a+');
@fwrite($fh, $data);
@fclose($fh);
}
}
// Setup classname, should be the same as the name of the spellchecker class
$spellCheckerConfig['class'] = "TinyGoogleSpell";
?>

@ -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";
?>

@ -0,0 +1,121 @@
<?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");
if(preg_match("#win#i",php_uname()))
$this->cmd = $config['tinypspellshell.aspell'] . " -a --lang=". $this->lang." --encoding=utf-8 -H < $this->tmpfile 2>&1";
else
$this->cmd = "cat ". $this->tmpfile ." | " . $config['tinypspellshell.aspell'] . " -a --encoding=utf-8 -H --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);
@unlink($this->tmpfile);
$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 (function_exists("mb_convert_encoding"))
$word = mb_convert_encoding($word, "ISO-8859-1", mb_detect_encoding($word, "UTF-8"));
else
$word = utf8_encode($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);
@unlink($this->tmpfile);
$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;
}
function _debugData($data) {
$fh = @fopen("debug.log", 'a+');
@fwrite($fh, $data);
@fclose($fh);
}
}
// Setup classname, should be the same as the name of the spellchecker class
$spellCheckerConfig['class'] = "TinyPspellShell";
?>

@ -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';
?>

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

@ -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;
}

File diff suppressed because one or more lines are too long

@ -0,0 +1,588 @@
/**
* $Id: editor_plugin_src.js 28 2006-08-01 16:02:56Z spocke $
*
* @author Moxiecode
* @copyright Copyright © 2004-2006, Moxiecode Systems AB, All rights reserved.
*/
tinyMCE.importPluginLanguagePack('spellchecker', 'en,fr,sv,nn,nb');
// Plucin static class
var TinyMCE_SpellCheckerPlugin = {
_contextMenu : new TinyMCE_Menu(),
_menu : new TinyMCE_Menu(),
_counter : 0,
_ajaxPage : '/tinyspell.php',
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=' + encodeURIComponent(elm.innerHTML);
args += '&lang=' + escape(inst.spellCheckerLang);
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.baseURL + self._ajaxPage, 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=' + encodeURIComponent(self._getWordList(inst.getBody())).replace( /\'/g, '%27' );
args += '&lang=' + escape(inst.spellCheckerLang);
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.baseURL + self._ajaxPage, 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(), decodeURIComponent(el.firstChild.nodeValue).split('+'));
inst.selection.moveToBookmark(inst.spellCheckerBookmark);
if(tinyMCE.getParam('spellchecker_report_mispellings', false))
alert(tinyMCE.getLang('lang_spellchecker_mpell_found', '', true, {words : self._countWords(inst)}));
} else
alert(tinyMCE.getLang('lang_spellchecker_no_mpell', '', true));
self._checkDone(inst);
break;
case "suggest":
self._buildMenu(el.firstChild ? decodeURIComponent(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++) {
if (!new RegExp('/SCRIPT|STYLE/').test(nl[i].parentNode.nodeName))
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 && nl[i].length > 0)
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 self = TinyMCE_SpellCheckerPlugin;
var w = self._countWords(inst);
if (w == 0) {
self._removeWords(inst.getDoc());
inst.spellcheckerOn = false;
tinyMCE.switchClass(inst.editorId + '_spellchecker', 'mceMenuButton');
}
},
_countWords : function(inst) {
var i, w = 0, nl = inst.getDoc().getElementsByTagName("span"), c;
var self = TinyMCE_SpellCheckerPlugin;
for (i=nl.length-1; i>=0; i--) {
c = tinyMCE.getAttrib(nl[i], 'class');
if (c == 'mceItemHiddenSpellWord')
w++;
}
return w;
},
_removeWord : function(e) {
if (e != null)
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++) {
if (wl[i].length > 0)
w += wl[i] + ((i == wl.length-1) ? '' : '|');
}
for (i=0; i<nl.length; i++) {
nv = nl[i].nodeValue;
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');
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();
} else
cm.addTitle(tinyMCE.getLang('lang_spellchecker_no_sug', '', true));
cm.addItem(tinyMCE.getLang('lang_spellchecker_ignore_word', '', true), 'tinyMCE.execCommand(\'mceSpellCheckIgnore\');');
cm.addItem(tinyMCE.getLang('lang_spellchecker_ignore_words', '', true), 'tinyMCE.execCommand(\'mceSpellCheckIgnoreAll\');');
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

@ -0,0 +1,15 @@
// UK lang variables
tinyMCE.addToLang('spellchecker',{
desc : 'Toggle spellchecker',
menu : 'Spellchecker settings',
ignore_word : 'Ignore word',
ignore_words : 'Ignore all',
langs : 'Languages',
wait : 'Please wait...',
swait : 'Spellchecking, please wait...',
sug : 'Suggestions',
no_sug : 'No suggestions',
no_mpell : 'No misspellings found.',
mpell_found : 'Found {$words} misspellings.'
});

@ -0,0 +1,142 @@
<?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.
*/
// Ignore the Notice errors for now.
error_reporting(E_ALL ^ E_NOTICE);
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 = urldecode($_REQUEST['check']);
$cmd = sanitize($_REQUEST['cmd']);
$lang = sanitize($_REQUEST['lang'], "strict");
$mode = sanitize($_REQUEST['mode'], "strict");
$spelling = sanitize($_REQUEST['spelling'], "strict");
$jargon = sanitize($_REQUEST['jargon'], "strict");
$encoding = sanitize($_REQUEST['encoding'], "strict");
$sg = sanitize($_REQUEST['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;
}
$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');
$body = '<?xml version="1.0" encoding="utf-8" ?>';
$body .= "\n";
if (count($result) == 0)
$body .= '<res id="' . $id . '" cmd="'. $cmd .'" />';
else
$body .= '<res id="' . $id . '" cmd="'. $cmd .'">'. urlencode(implode(" ", $result)) .'</res>';
echo $body;
break;
case "xmlerror";
header('Content-type: text/xml; charset=utf-8');
$body = '<?xml version="1.0" encoding="utf-8" ?>';
$body .= "\n";
$body .= '<res id="' . $id . '" cmd="'. $cmd .'" error="true" msg="'. implode(" ", $tinyspell->errorMsg) .'" />';
echo $body;
break;
case "html":
var_dump($result);
break;
case "htmlerror":
echo "Error";
break;
}
?>
Loading…
Cancel
Save