From 5291ce077a9afdb45c82268c7d9b3ddc329950ca Mon Sep 17 00:00:00 2001 From: Blake Robertson Date: Wed, 11 Apr 2012 01:37:59 -0400 Subject: [PATCH] Added SpellCheck Support to TinyMCE --- include/SugarTinyMCE.php | 23 +- .../plugins/spellchecker/changelog.txt | 33 + .../spellchecker/classes/EnchantSpell.php | 71 +++ .../spellchecker/classes/GoogleSpell.php | 159 +++++ .../plugins/spellchecker/classes/PSpell.php | 82 +++ .../spellchecker/classes/PSpellShell.php | 117 ++++ .../spellchecker/classes/SpellChecker.php | 62 ++ .../spellchecker/classes/utils/JSON.php | 595 ++++++++++++++++++ .../spellchecker/classes/utils/Logger.php | 268 ++++++++ .../tiny_mce/plugins/spellchecker/config.php | 27 + .../plugins/spellchecker/editor_plugin.js | 2 +- .../plugins/spellchecker/editor_plugin_src.js | 62 +- .../plugins/spellchecker/includes/general.php | 98 +++ .../tiny_mce/plugins/spellchecker/rpc.php | 112 ++++ 14 files changed, 1669 insertions(+), 42 deletions(-) create mode 100644 include/javascript/tiny_mce/plugins/spellchecker/changelog.txt create mode 100644 include/javascript/tiny_mce/plugins/spellchecker/classes/EnchantSpell.php create mode 100644 include/javascript/tiny_mce/plugins/spellchecker/classes/GoogleSpell.php create mode 100644 include/javascript/tiny_mce/plugins/spellchecker/classes/PSpell.php create mode 100644 include/javascript/tiny_mce/plugins/spellchecker/classes/PSpellShell.php create mode 100644 include/javascript/tiny_mce/plugins/spellchecker/classes/SpellChecker.php create mode 100644 include/javascript/tiny_mce/plugins/spellchecker/classes/utils/JSON.php create mode 100644 include/javascript/tiny_mce/plugins/spellchecker/classes/utils/Logger.php create mode 100644 include/javascript/tiny_mce/plugins/spellchecker/config.php create mode 100644 include/javascript/tiny_mce/plugins/spellchecker/includes/general.php create mode 100644 include/javascript/tiny_mce/plugins/spellchecker/rpc.php diff --git a/include/SugarTinyMCE.php b/include/SugarTinyMCE.php index bed3cdc8..12599229 100644 --- a/include/SugarTinyMCE.php +++ b/include/SugarTinyMCE.php @@ -53,28 +53,29 @@ class SugarTinyMCE { var $buttonConfigs = array( 'default' => array( 'buttonConfig' => "code,help,separator,bold,italic,underline,strikethrough,separator,justifyleft,justifycenter,justifyright, - justifyfull,separator,forecolor,backcolor,separator,styleselect,formatselect,fontselect,fontsizeselect,", + justifyfull,separator,forecolor,backcolor,separator,styleselect,formatselect,fontselect,fontsizeselect,", 'buttonConfig2' => "cut,copy,paste,pastetext,pasteword,selectall,separator,search,replace,separator,bullist,numlist,separator,outdent, indent,separator,ltr,rtl,separator,undo,redo,separator, link,unlink,anchor,image,separator,sub,sup,separator,charmap, - visualaid", - 'buttonConfig3' => "tablecontrols,separator,advhr,hr,removeformat,separator,insertdate,inserttime,separator,preview"), + visualaid", + 'buttonConfig3' => "tablecontrols,separator,advhr,hr,removeformat,separator,insertdate,inserttime,separator,preview,spellchecker"), 'email_compose' => array( 'buttonConfig' => "code,help,separator,bold,italic,underline,strikethrough,separator,bullist,numlist,separator,justifyleft,justifycenter,justifyright, - justifyfull,separator,forecolor,backcolor,separator,styleselect,formatselect,fontselect,fontsizeselect,", - 'buttonConfig2' => "", + justifyfull,separator,forecolor,backcolor,separator,spellchecker,seperator,styleselect,formatselect,fontselect,fontsizeselect", + 'buttonConfig2' => "", 'buttonConfig3' => ""), 'email_compose_light' => array( 'buttonConfig' => "code,help,separator,bold,italic,underline,strikethrough,separator,bullist,numlist,separator,justifyleft,justifycenter,justifyright, - justifyfull,separator,forecolor,backcolor,separator,styleselect,formatselect,fontselect,fontsizeselect,", - 'buttonConfig2' => "", + justifyfull,separator,forecolor,backcolor,separator,spellchecker,seperator,styleselect,formatselect,fontselect,fontsizeselect", + 'buttonConfig2' => "", 'buttonConfig3' => ""), ); - + var $pluginsConfig = array( - 'email_compose_light' => 'insertdatetime,paste,directionality,safari', - 'email_compose' => 'advhr,insertdatetime,table,preview,paste,searchreplace,directionality,fullpage', + 'email_compose_light' => 'insertdatetime,paste,directionality,safari,spellchecker', + 'email_compose' => 'advhr,insertdatetime,table,preview,paste,searchreplace,directionality,fullpage,spellchecker', ); + var $defaultConfig = array( 'convert_urls' => false, 'valid_children' => '+body[style]', @@ -89,7 +90,7 @@ class SugarTinyMCE { 'strict_loading_mode' => true, 'mode' => 'exact', 'language' => 'en', - 'plugins' => 'advhr,insertdatetime,table,preview,paste,searchreplace,directionality', + 'plugins' => 'advhr,insertdatetime,table,preview,paste,searchreplace,directionality,spellchecker', 'elements' => '', 'extended_valid_elements' => 'style[dir|lang|media|title|type],hr[class|width|size|noshade],@[class|style]', 'content_css' => 'include/javascript/tiny_mce/themes/advanced/skins/default/content.css', diff --git a/include/javascript/tiny_mce/plugins/spellchecker/changelog.txt b/include/javascript/tiny_mce/plugins/spellchecker/changelog.txt new file mode 100644 index 00000000..f41ec7fd --- /dev/null +++ b/include/javascript/tiny_mce/plugins/spellchecker/changelog.txt @@ -0,0 +1,33 @@ +Version 2.0.6 (2011-09-29) + Fixed incorrect position of suggestion menu. + Fixed handling of mispelled words with no suggestions in PSpellShell engine. + Fixed PSpellShell command on Windows. + Fixed bug where Javascript error is produced when enchant_dict_suggest() returns unexpected result. +Version 2.0.5 (2011-03-24) + Merged with the latest TinyMCE spellchecker version. +Version 2.0.4 (2010-12-20) + Fixed issue with the JSON class not having the correct number of parameters to ord calls. +Version 2.0.3 (2010-04-19) + Added standalone support. Will use native spellchecker for supported browsers. + Added @package phpdoc comments. Patch contributed by Jacob Santos. + Fixed some PHP missing function issue. +Version 2.0.2 (2008-04-30) + Added new EnchantSpell engine class contributed by Michel Weimerskirch. + Added new general.remote_rpc_url option, enables you to proxy requests to another server. + Fixed security hole in PSpellShell.php file if PSpellShell engine was used. +Version 2.0.1 (2008-03-07) + Fixed bug where spellchecker was auto focusing the editor in IE. +Version 2.0 (2008-01-30) + Fixed bug where the suggestions menu was placed at an incorrect location. +Version 2.0rc1 (2008-01-14) + Moved package from beta to release candidate. +Version 2.0b3 (2007-12-xx) + Fixed bug where the suggestions menu could appear at the wrong location. +Version 2.0b2 (2007-11-29) + Fixed bug where the spellchecker was removing the word when it was ignored. +Version 2.0b1 (2007-11-21) + Moved spellchecker from alpha to beta status. +Version 2.0a2 (2007-11-13) + Updated plugin so it works correctly with the TinyMCE 3.0a3 version. +Version 2.0a1 (2007-11-01) + Rewritten version for TinyMCE 3.0 this new version uses JSON RPC. diff --git a/include/javascript/tiny_mce/plugins/spellchecker/classes/EnchantSpell.php b/include/javascript/tiny_mce/plugins/spellchecker/classes/EnchantSpell.php new file mode 100644 index 00000000..cefc8cf2 --- /dev/null +++ b/include/javascript/tiny_mce/plugins/spellchecker/classes/EnchantSpell.php @@ -0,0 +1,71 @@ += 1.4.1 + * @param Array $words Array of words to check. + * @return Array of misspelled words. + */ + function &checkWords($lang, $words) { + $r = enchant_broker_init(); + + if (enchant_broker_dict_exists($r,$lang)) { + $d = enchant_broker_request_dict($r, $lang); + + $returnData = array(); + foreach($words as $key => $value) { + $correct = enchant_dict_check($d, $value); + if(!$correct) { + $returnData[] = trim($value); + } + } + + return $returnData; + enchant_broker_free_dict($d); + } else { + + } + enchant_broker_free($r); + } + + /** + * Returns suggestions for a specific word. + * + * @param String $lang Selected language code (like en_US or de_DE). Shortcodes like "en" and "de" work with enchant >= 1.4.1 + * @param String $word Specific word to get suggestions for. + * @return Array of suggestions for the specified word. + */ + function &getSuggestions($lang, $word) { + $r = enchant_broker_init(); + + if (enchant_broker_dict_exists($r,$lang)) { + $d = enchant_broker_request_dict($r, $lang); + $suggs = enchant_dict_suggest($d, $word); + + // enchant_dict_suggest() sometimes returns NULL + if (!is_array($suggs)) + $suggs = array(); + + enchant_broker_free_dict($d); + } else { + $suggs = array(); + } + + enchant_broker_free($r); + + return $suggs; + } +} + +?> diff --git a/include/javascript/tiny_mce/plugins/spellchecker/classes/GoogleSpell.php b/include/javascript/tiny_mce/plugins/spellchecker/classes/GoogleSpell.php new file mode 100644 index 00000000..38daa066 --- /dev/null +++ b/include/javascript/tiny_mce/plugins/spellchecker/classes/GoogleSpell.php @@ -0,0 +1,159 @@ +_getMatches($lang, $wordstr); + $words = array(); + + for ($i=0; $i_unhtmlentities(mb_substr($wordstr, $matches[$i][1], $matches[$i][2], "UTF-8")); + + return $words; + } + + /** + * Returns suggestions of for a specific word. + * + * @param {String} $lang Language code like sv or en. + * @param {String} $word Specific word to get suggestions for. + * @return {Array} Array of suggestions for the specified word. + */ + function &getSuggestions($lang, $word) { + $sug = array(); + $osug = array(); + $matches = $this->_getMatches($lang, $word); + + if (count($matches) > 0) + $sug = explode("\t", utf8_encode($this->_unhtmlentities($matches[0][4]))); + + // Remove empty + foreach ($sug as $item) { + if ($item) + $osug[] = $item; + } + + return $osug; + } + + function &_getMatches($lang, $str) { + $server = "www.google.com"; + $port = 443; + $path = "/tbproxy/spell?lang=" . $lang . "&hl=en"; + $host = "www.google.com"; + $url = "https://" . $server; + + // Setup XML request + $xml = '' . $str . ''; + + $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; + + // Use curl if it exists + if (function_exists('curl_init')) { + // Use curl + $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); + } else { + // Use raw sockets + $fp = fsockopen("ssl://" . $server, $port, $errno, $errstr, 30); + if ($fp) { + // Send request + fwrite($fp, $header); + + // Read response + $xml = ""; + while (!feof($fp)) + $xml .= fgets($fp, 128); + + fclose($fp); + } else + echo "Could not open SSL connection to google."; + } + + // Grab and parse content + $matches = array(); + preg_match_all('/([^<]*)<\/c>/', $xml, $matches, PREG_SET_ORDER); + + return $matches; + } + + 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); + } +} + +// Patch in multibyte support +if (!function_exists('mb_substr')) { + function mb_substr($str, $start, $len = '', $encoding="UTF-8"){ + $limit = strlen($str); + + for ($s = 0; $start > 0;--$start) {// found the real start + if ($s >= $limit) + break; + + if ($str[$s] <= "\x7F") + ++$s; + else { + ++$s; // skip length + + while ($str[$s] >= "\x80" && $str[$s] <= "\xBF") + ++$s; + } + } + + if ($len == '') + return substr($str, $s); + else + for ($e = $s; $len > 0; --$len) {//found the real end + if ($e >= $limit) + break; + + if ($str[$e] <= "\x7F") + ++$e; + else { + ++$e;//skip length + + while ($str[$e] >= "\x80" && $str[$e] <= "\xBF" && $e < $limit) + ++$e; + } + } + + return substr($str, $s, $e - $s); + } +} + +?> diff --git a/include/javascript/tiny_mce/plugins/spellchecker/classes/PSpell.php b/include/javascript/tiny_mce/plugins/spellchecker/classes/PSpell.php new file mode 100644 index 00000000..3c6424d8 --- /dev/null +++ b/include/javascript/tiny_mce/plugins/spellchecker/classes/PSpell.php @@ -0,0 +1,82 @@ +_getPLink($lang); + + $outWords = array(); + foreach ($words as $word) { + if (!pspell_check($plink, trim($word))) + $outWords[] = utf8_encode($word); + } + + return $outWords; + } + + /** + * Returns suggestions of for a specific word. + * + * @param {String} $lang Language code like sv or en. + * @param {String} $word Specific word to get suggestions for. + * @return {Array} Array of suggestions for the specified word. + */ + function &getSuggestions($lang, $word) { + $words = pspell_suggest($this->_getPLink($lang), $word); + + for ($i=0; $ithrowError("PSpell support not found in PHP installation."); + + // Setup PSpell link + $plink = pspell_new( + $lang, + $this->_config['PSpell.spelling'], + $this->_config['PSpell.jargon'], + $this->_config['PSpell.encoding'], + $this->_config['PSpell.mode'] + ); + + // Setup PSpell link +/* if (!$plink) { + $pspellConfig = pspell_config_create( + $lang, + $this->_config['PSpell.spelling'], + $this->_config['PSpell.jargon'], + $this->_config['PSpell.encoding'] + ); + + $plink = pspell_new_config($pspell_config); + }*/ + + if (!$plink) + $this->throwError("No PSpell link found opened."); + + return $plink; + } +} + +?> diff --git a/include/javascript/tiny_mce/plugins/spellchecker/classes/PSpellShell.php b/include/javascript/tiny_mce/plugins/spellchecker/classes/PSpellShell.php new file mode 100644 index 00000000..4d6c4ddf --- /dev/null +++ b/include/javascript/tiny_mce/plugins/spellchecker/classes/PSpellShell.php @@ -0,0 +1,117 @@ +_getCMD($lang); + + if ($fh = fopen($this->_tmpfile, "w")) { + fwrite($fh, "!\n"); + + foreach($words as $key => $value) + fwrite($fh, "^" . $value . "\n"); + + fclose($fh); + } else + $this->throwError("PSpell support was not found."); + + $data = shell_exec($cmd); + @unlink($this->_tmpfile); + + $returnData = array(); + $dataArr = preg_split("/[\r\n]/", $data, -1, PREG_SPLIT_NO_EMPTY); + + foreach ($dataArr as $dstr) { + $matches = array(); + + // Skip this line. + if ($dstr[0] == "@") + continue; + + preg_match("/(\&|#) ([^ ]+) .*/i", $dstr, $matches); + + if (!empty($matches[2])) + $returnData[] = utf8_encode(trim($matches[2])); + } + + return $returnData; + } + + /** + * Returns suggestions of for a specific word. + * + * @param {String} $lang Language code like sv or en. + * @param {String} $word Specific word to get suggestions for. + * @return {Array} Array of suggestions for the specified word. + */ + function &getSuggestions($lang, $word) { + $cmd = $this->_getCMD($lang); + + 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 + $this->throwError("Error opening tmp file."); + + $data = shell_exec($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 ($dstr[0] == "@") + continue; + + preg_match("/\&[^:]+:(.*)/i", $dstr, $matches); + + if (!empty($matches[1])) { + $words = array_slice(explode(',', $matches[1]), 0, 10); + + for ($i=0; $i_tmpfile = tempnam($this->_config['PSpellShell.tmp'], "tinyspell"); + + $file = $this->_tmpfile; + $lang = preg_replace("/[^-_a-z]/", "", strtolower($lang)); + $bin = $this->_config['PSpellShell.aspell']; + + if (preg_match("#win#i", php_uname())) + return "$bin -a --lang=$lang --encoding=utf-8 -H < $file 2>&1"; + + return "cat $file | $bin -a --lang=$lang --encoding=utf-8 -H"); + } +} + +?> diff --git a/include/javascript/tiny_mce/plugins/spellchecker/classes/SpellChecker.php b/include/javascript/tiny_mce/plugins/spellchecker/classes/SpellChecker.php new file mode 100644 index 00000000..5d9205fe --- /dev/null +++ b/include/javascript/tiny_mce/plugins/spellchecker/classes/SpellChecker.php @@ -0,0 +1,62 @@ +_config = $config; + } + + /** + * Simple loopback function everything that gets in will be send back. + * + * @param $args.. Arguments. + * @return {Array} Array of all input arguments. + */ + function &loopback(/* args.. */) { + return func_get_args(); + } + + /** + * Spellchecks an array of words. + * + * @param {String} $lang Language code like sv or en. + * @param {Array} $words Array of words to spellcheck. + * @return {Array} Array of misspelled words. + */ + function &checkWords($lang, $words) { + return $words; + } + + /** + * Returns suggestions of for a specific word. + * + * @param {String} $lang Language code like sv or en. + * @param {String} $word Specific word to get suggestions for. + * @return {Array} Array of suggestions for the specified word. + */ + function &getSuggestions($lang, $word) { + return array(); + } + + /** + * Throws an error message back to the user. This will stop all execution. + * + * @param {String} $str Message to send back to user. + */ + function throwError($str) { + die('{"result":null,"id":null,"error":{"errstr":"' . addslashes($str) . '","errfile":"","errline":null,"errcontext":"","level":"FATAL"}}'); + } +} + +?> diff --git a/include/javascript/tiny_mce/plugins/spellchecker/classes/utils/JSON.php b/include/javascript/tiny_mce/plugins/spellchecker/classes/utils/JSON.php new file mode 100644 index 00000000..1f2b92ca --- /dev/null +++ b/include/javascript/tiny_mce/plugins/spellchecker/classes/utils/JSON.php @@ -0,0 +1,595 @@ +_data = $data; + $this->_len = strlen($data); + $this->_pos = -1; + $this->_location = JSON_IN_BETWEEN; + $this->_lastLocations = array(); + $this->_needProp = false; + } + + function getToken() { + return $this->_token; + } + + function getLocation() { + return $this->_location; + } + + function getTokenName() { + switch ($this->_token) { + case JSON_BOOL: + return 'JSON_BOOL'; + + case JSON_INT: + return 'JSON_INT'; + + case JSON_STR: + return 'JSON_STR'; + + case JSON_FLOAT: + return 'JSON_FLOAT'; + + case JSON_NULL: + return 'JSON_NULL'; + + case JSON_START_OBJ: + return 'JSON_START_OBJ'; + + case JSON_END_OBJ: + return 'JSON_END_OBJ'; + + case JSON_START_ARRAY: + return 'JSON_START_ARRAY'; + + case JSON_END_ARRAY: + return 'JSON_END_ARRAY'; + + case JSON_KEY: + return 'JSON_KEY'; + } + + return 'UNKNOWN'; + } + + function getValue() { + return $this->_value; + } + + function readToken() { + $chr = $this->read(); + + if ($chr != null) { + switch ($chr) { + case '[': + $this->_lastLocation[] = $this->_location; + $this->_location = JSON_IN_ARRAY; + $this->_token = JSON_START_ARRAY; + $this->_value = null; + $this->readAway(); + return true; + + case ']': + $this->_location = array_pop($this->_lastLocation); + $this->_token = JSON_END_ARRAY; + $this->_value = null; + $this->readAway(); + + if ($this->_location == JSON_IN_OBJECT) + $this->_needProp = true; + + return true; + + case '{': + $this->_lastLocation[] = $this->_location; + $this->_location = JSON_IN_OBJECT; + $this->_needProp = true; + $this->_token = JSON_START_OBJ; + $this->_value = null; + $this->readAway(); + return true; + + case '}': + $this->_location = array_pop($this->_lastLocation); + $this->_token = JSON_END_OBJ; + $this->_value = null; + $this->readAway(); + + if ($this->_location == JSON_IN_OBJECT) + $this->_needProp = true; + + return true; + + // String + case '"': + case '\'': + return $this->_readString($chr); + + // Null + case 'n': + return $this->_readNull(); + + // Bool + case 't': + case 'f': + return $this->_readBool($chr); + + default: + // Is number + if (is_numeric($chr) || $chr == '-' || $chr == '.') + return $this->_readNumber($chr); + + return true; + } + } + + return false; + } + + function _readBool($chr) { + $this->_token = JSON_BOOL; + $this->_value = $chr == 't'; + + if ($chr == 't') + $this->skip(3); // rue + else + $this->skip(4); // alse + + $this->readAway(); + + if ($this->_location == JSON_IN_OBJECT && !$this->_needProp) + $this->_needProp = true; + + return true; + } + + function _readNull() { + $this->_token = JSON_NULL; + $this->_value = null; + + $this->skip(3); // ull + $this->readAway(); + + if ($this->_location == JSON_IN_OBJECT && !$this->_needProp) + $this->_needProp = true; + + return true; + } + + function _readString($quote) { + $output = ""; + $this->_token = JSON_STR; + $endString = false; + + while (($chr = $this->peek()) != -1) { + switch ($chr) { + case '\\': + // Read away slash + $this->read(); + + // Read escape code + $chr = $this->read(); + switch ($chr) { + case 't': + $output .= "\t"; + break; + + case 'b': + $output .= "\b"; + break; + + case 'f': + $output .= "\f"; + break; + + case 'r': + $output .= "\r"; + break; + + case 'n': + $output .= "\n"; + break; + + case 'u': + $output .= $this->_int2utf8(hexdec($this->read(4))); + break; + + default: + $output .= $chr; + break; + } + + break; + + case '\'': + case '"': + if ($chr == $quote) + $endString = true; + + $chr = $this->read(); + if ($chr != -1 && $chr != $quote) + $output .= $chr; + + break; + + default: + $output .= $this->read(); + } + + // String terminated + if ($endString) + break; + } + + $this->readAway(); + $this->_value = $output; + + // Needed a property + if ($this->_needProp) { + $this->_token = JSON_KEY; + $this->_needProp = false; + return true; + } + + if ($this->_location == JSON_IN_OBJECT && !$this->_needProp) + $this->_needProp = true; + + return true; + } + + function _int2utf8($int) { + $int = intval($int); + + switch ($int) { + case 0: + return chr(0); + + case ($int & 0x7F): + return chr($int); + + case ($int & 0x7FF): + return chr(0xC0 | (($int >> 6) & 0x1F)) . chr(0x80 | ($int & 0x3F)); + + case ($int & 0xFFFF): + return chr(0xE0 | (($int >> 12) & 0x0F)) . chr(0x80 | (($int >> 6) & 0x3F)) . chr (0x80 | ($int & 0x3F)); + + case ($int & 0x1FFFFF): + return chr(0xF0 | ($int >> 18)) . chr(0x80 | (($int >> 12) & 0x3F)) . chr(0x80 | (($int >> 6) & 0x3F)) . chr(0x80 | ($int & 0x3F)); + } + } + + function _readNumber($start) { + $value = ""; + $isFloat = false; + + $this->_token = JSON_INT; + $value .= $start; + + while (($chr = $this->peek()) != -1) { + if (is_numeric($chr) || $chr == '-' || $chr == '.') { + if ($chr == '.') + $isFloat = true; + + $value .= $this->read(); + } else + break; + } + + $this->readAway(); + + if ($isFloat) { + $this->_token = JSON_FLOAT; + $this->_value = floatval($value); + } else + $this->_value = intval($value); + + if ($this->_location == JSON_IN_OBJECT && !$this->_needProp) + $this->_needProp = true; + + return true; + } + + function readAway() { + while (($chr = $this->peek()) != null) { + if ($chr != ':' && $chr != ',' && $chr != ' ') + return; + + $this->read(); + } + } + + function read($len = 1) { + if ($this->_pos < $this->_len) { + if ($len > 1) { + $str = substr($this->_data, $this->_pos + 1, $len); + $this->_pos += $len; + + return $str; + } else + return $this->_data[++$this->_pos]; + } + + return null; + } + + function skip($len) { + $this->_pos += $len; + } + + function peek() { + if ($this->_pos < $this->_len) + return $this->_data[$this->_pos + 1]; + + return null; + } +} + +/** + * This class handles JSON stuff. + * + * @package MCManager.utils + */ +class Moxiecode_JSON { + function Moxiecode_JSON() { + } + + function decode($input) { + $reader = new Moxiecode_JSONReader($input); + + return $this->readValue($reader); + } + + function readValue(&$reader) { + $this->data = array(); + $this->parents = array(); + $this->cur =& $this->data; + $key = null; + $loc = JSON_IN_ARRAY; + + while ($reader->readToken()) { + switch ($reader->getToken()) { + case JSON_STR: + case JSON_INT: + case JSON_BOOL: + case JSON_FLOAT: + case JSON_NULL: + switch ($reader->getLocation()) { + case JSON_IN_OBJECT: + $this->cur[$key] = $reader->getValue(); + break; + + case JSON_IN_ARRAY: + $this->cur[] = $reader->getValue(); + break; + + default: + return $reader->getValue(); + } + break; + + case JSON_KEY: + $key = $reader->getValue(); + break; + + case JSON_START_OBJ: + case JSON_START_ARRAY: + if ($loc == JSON_IN_OBJECT) + $this->addArray($key); + else + $this->addArray(null); + + $cur =& $obj; + + $loc = $reader->getLocation(); + break; + + case JSON_END_OBJ: + case JSON_END_ARRAY: + $loc = $reader->getLocation(); + + if (count($this->parents) > 0) { + $this->cur =& $this->parents[count($this->parents) - 1]; + array_pop($this->parents); + } + break; + } + } + + return $this->data[0]; + } + + // This method was needed since PHP is crapy and doesn't have pointers/references + function addArray($key) { + $this->parents[] =& $this->cur; + $ar = array(); + + if ($key) + $this->cur[$key] =& $ar; + else + $this->cur[] =& $ar; + + $this->cur =& $ar; + } + + function getDelim($index, &$reader) { + switch ($reader->getLocation()) { + case JSON_IN_ARRAY: + case JSON_IN_OBJECT: + if ($index > 0) + return ","; + break; + } + + return ""; + } + + function encode($input) { + switch (gettype($input)) { + case 'boolean': + return $input ? 'true' : 'false'; + + case 'integer': + return (int) $input; + + case 'float': + case 'double': + return (float) $input; + + case 'NULL': + return 'null'; + + case 'string': + return $this->encodeString($input); + + case 'array': + return $this->_encodeArray($input); + + case 'object': + return $this->_encodeArray(get_object_vars($input)); + } + + return ''; + } + + function encodeString($input) { + // Needs to be escaped + if (preg_match('/[^a-zA-Z0-9]/', $input)) { + $output = ''; + + for ($i=0; $i_utf82utf16($char))); + } if (($byte & 0xF0) == 0xE0) { + $char = pack('C*', $byte, ord($input[$i + 1]), ord($input[$i + 2])); + $i += 2; + $output .= sprintf('\u%04s', bin2hex($this->_utf82utf16($char))); + } if (($byte & 0xF8) == 0xF0) { + $char = pack('C*', $byte, ord($input[$i + 1]), ord($input[$i + 2]), ord($input[$i + 3])); + $i += 3; + $output .= sprintf('\u%04s', bin2hex($this->_utf82utf16($char))); + } if (($byte & 0xFC) == 0xF8) { + $char = pack('C*', $byte, ord($input[$i + 1]), ord($input[$i + 2]), ord($input[$i + 3]), ord($input[$i + 4])); + $i += 4; + $output .= sprintf('\u%04s', bin2hex($this->_utf82utf16($char))); + } if (($byte & 0xFE) == 0xFC) { + $char = pack('C*', $byte, ord($input[$i + 1]), ord($input[$i + 2]), ord($input[$i + 3]), ord($input[$i + 4]), ord($input[$i + 5])); + $i += 5; + $output .= sprintf('\u%04s', bin2hex($this->_utf82utf16($char))); + } else if ($byte < 128) + $output .= $input[$i]; + } + } + + return '"' . $output . '"'; + } + + return '"' . $input . '"'; + } + + function _utf82utf16($utf8) { + if (function_exists('mb_convert_encoding')) + return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); + + switch (strlen($utf8)) { + case 1: + return $utf8; + + case 2: + return chr(0x07 & (ord($utf8[0]) >> 2)) . chr((0xC0 & (ord($utf8[0]) << 6)) | (0x3F & ord($utf8[1]))); + + case 3: + return chr((0xF0 & (ord($utf8[0]) << 4)) | (0x0F & (ord($utf8[1]) >> 2))) . chr((0xC0 & (ord($utf8[1]) << 6)) | (0x7F & ord($utf8[2]))); + } + + return ''; + } + + function _encodeArray($input) { + $output = ''; + $isIndexed = true; + + $keys = array_keys($input); + for ($i=0; $iencodeString($keys[$i]) . ':' . $this->encode($input[$keys[$i]]); + $isIndexed = false; + } else + $output .= $this->encode($input[$keys[$i]]); + + if ($i != count($keys) - 1) + $output .= ','; + } + + return $isIndexed ? '[' . $output . ']' : '{' . $output . '}'; + } +} + +?> diff --git a/include/javascript/tiny_mce/plugins/spellchecker/classes/utils/Logger.php b/include/javascript/tiny_mce/plugins/spellchecker/classes/utils/Logger.php new file mode 100644 index 00000000..a1fb4cd0 --- /dev/null +++ b/include/javascript/tiny_mce/plugins/spellchecker/classes/utils/Logger.php @@ -0,0 +1,268 @@ +_path = ""; + $this->_filename = "{level}.log"; + $this->setMaxSize("100k"); + $this->_maxFiles = 10; + $this->_level = MC_LOGGER_DEBUG; + $this->_format = "[{time}] [{level}] {message}"; + } + + /** + * Sets the current log level, use the MC_LOGGER constants. + * + * @param int $level Log level instance for example MC_LOGGER_DEBUG. + */ + function setLevel($level) { + if (is_string($level)) { + switch (strtolower($level)) { + case "debug": + $level = MC_LOGGER_DEBUG; + break; + + case "info": + $level = MC_LOGGER_INFO; + break; + + case "warn": + case "warning": + $level = MC_LOGGER_WARN; + break; + + case "error": + $level = MC_LOGGER_ERROR; + break; + + case "fatal": + $level = MC_LOGGER_FATAL; + break; + + default: + $level = MC_LOGGER_FATAL; + } + } + + $this->_level = $level; + } + + /** + * Returns the current log level for example MC_LOGGER_DEBUG. + * + * @return int Current log level for example MC_LOGGER_DEBUG. + */ + function getLevel() { + return $this->_level; + } + + function setPath($path) { + $this->_path = $path; + } + + function getPath() { + return $this->_path; + } + + function setFileName($file_name) { + $this->_filename = $file_name; + } + + function getFileName() { + return $this->_filename; + } + + function setFormat($format) { + $this->_format = $format; + } + + function getFormat() { + return $this->_format; + } + + function setMaxSize($size) { + // Fix log max size + $logMaxSizeBytes = intval(preg_replace("/[^0-9]/", "", $size)); + + // Is KB + if (strpos((strtolower($size)), "k") > 0) + $logMaxSizeBytes *= 1024; + + // Is MB + if (strpos((strtolower($size)), "m") > 0) + $logMaxSizeBytes *= (1024 * 1024); + + $this->_maxSizeBytes = $logMaxSizeBytes; + $this->_maxSize = $size; + } + + function getMaxSize() { + return $this->_maxSize; + } + + function setMaxFiles($max_files) { + $this->_maxFiles = $max_files; + } + + function getMaxFiles() { + return $this->_maxFiles; + } + + function debug($msg) { + $args = func_get_args(); + $this->_logMsg(MC_LOGGER_DEBUG, implode(', ', $args)); + } + + function info($msg) { + $args = func_get_args(); + $this->_logMsg(MC_LOGGER_INFO, implode(', ', $args)); + } + + function warn($msg) { + $args = func_get_args(); + $this->_logMsg(MC_LOGGER_WARN, implode(', ', $args)); + } + + function error($msg) { + $args = func_get_args(); + $this->_logMsg(MC_LOGGER_ERROR, implode(', ', $args)); + } + + function fatal($msg) { + $args = func_get_args(); + $this->_logMsg(MC_LOGGER_FATAL, implode(', ', $args)); + } + + function isDebugEnabled() { + return $this->_level >= MC_LOGGER_DEBUG; + } + + function isInfoEnabled() { + return $this->_level >= MC_LOGGER_INFO; + } + + function isWarnEnabled() { + return $this->_level >= MC_LOGGER_WARN; + } + + function isErrorEnabled() { + return $this->_level >= MC_LOGGER_ERROR; + } + + function isFatalEnabled() { + return $this->_level >= MC_LOGGER_FATAL; + } + + function _logMsg($level, $message) { + $roll = false; + + if ($level < $this->_level) + return; + + $logFile = $this->toOSPath($this->_path . "/" . $this->_filename); + + switch ($level) { + case MC_LOGGER_DEBUG: + $levelName = "DEBUG"; + break; + + case MC_LOGGER_INFO: + $levelName = "INFO"; + break; + + case MC_LOGGER_WARN: + $levelName = "WARN"; + break; + + case MC_LOGGER_ERROR: + $levelName = "ERROR"; + break; + + case MC_LOGGER_FATAL: + $levelName = "FATAL"; + break; + } + + $logFile = str_replace('{level}', strtolower($levelName), $logFile); + + $text = $this->_format; + $text = str_replace('{time}', date("Y-m-d H:i:s"), $text); + $text = str_replace('{level}', strtolower($levelName), $text); + $text = str_replace('{message}', $message, $text); + $message = $text . "\r\n"; + + // Check filesize + if (file_exists($logFile)) { + $size = @filesize($logFile); + + if ($size + strlen($message) > $this->_maxSizeBytes) + $roll = true; + } + + // Roll if the size is right + if ($roll) { + for ($i=$this->_maxFiles-1; $i>=1; $i--) { + $rfile = $this->toOSPath($logFile . "." . $i); + $nfile = $this->toOSPath($logFile . "." . ($i+1)); + + if (@file_exists($rfile)) + @rename($rfile, $nfile); + } + + @rename($logFile, $this->toOSPath($logFile . ".1")); + + // Delete last logfile + $delfile = $this->toOSPath($logFile . "." . ($this->_maxFiles + 1)); + if (@file_exists($delfile)) + @unlink($delfile); + } + + // Append log line + if (($fp = @fopen($logFile, "a")) != null) { + @fputs($fp, $message); + @fflush($fp); + @fclose($fp); + } + } + + /** + * Converts a Unix path to OS specific path. + * + * @param String $path Unix path to convert. + */ + function toOSPath($path) { + return str_replace("/", DIRECTORY_SEPARATOR, $path); + } +} + +?> \ No newline at end of file diff --git a/include/javascript/tiny_mce/plugins/spellchecker/config.php b/include/javascript/tiny_mce/plugins/spellchecker/config.php new file mode 100644 index 00000000..795495a6 --- /dev/null +++ b/include/javascript/tiny_mce/plugins/spellchecker/config.php @@ -0,0 +1,27 @@ + diff --git a/include/javascript/tiny_mce/plugins/spellchecker/editor_plugin.js b/include/javascript/tiny_mce/plugins/spellchecker/editor_plugin.js index 0c42739c..f7cadd6e 100644 --- a/include/javascript/tiny_mce/plugins/spellchecker/editor_plugin.js +++ b/include/javascript/tiny_mce/plugins/spellchecker/editor_plugin.js @@ -1 +1 @@ -(function(){var a=tinymce.util.JSONRequest,c=tinymce.each,b=tinymce.DOM;tinymce.create("tinymce.plugins.SpellcheckerPlugin",{getInfo:function(){return{longname:"Spellchecker",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/spellchecker",version:tinymce.majorVersion+"."+tinymce.minorVersion}},init:function(e,f){var g=this,d;g.url=f;g.editor=e;g.rpcUrl=e.getParam("spellchecker_rpc_url","{backend}");if(g.rpcUrl=="{backend}"){if(tinymce.isIE){return}g.hasSupport=true;e.onContextMenu.addToTop(function(h,i){if(g.active){return false}})}e.addCommand("mceSpellCheck",function(){if(g.rpcUrl=="{backend}"){g.editor.getBody().spellcheck=g.active=!g.active;return}if(!g.active){e.setProgressState(1);g._sendRPC("checkWords",[g.selectedLang,g._getWords()],function(h){if(h.length>0){g.active=1;g._markWords(h);e.setProgressState(0);e.nodeChanged()}else{e.setProgressState(0);if(e.getParam("spellchecker_report_no_misspellings",true)){e.windowManager.alert("spellchecker.no_mpell")}}})}else{g._done()}});if(e.settings.content_css!==false){e.contentCSS.push(f+"/css/content.css")}e.onClick.add(g._showMenu,g);e.onContextMenu.add(g._showMenu,g);e.onBeforeGetContent.add(function(){if(g.active){g._removeWords()}});e.onNodeChange.add(function(i,h){h.setActive("spellchecker",g.active)});e.onSetContent.add(function(){g._done()});e.onBeforeGetContent.add(function(){g._done()});e.onBeforeExecCommand.add(function(h,i){if(i=="mceFullScreen"){g._done()}});g.languages={};c(e.getParam("spellchecker_languages","+English=en,Danish=da,Dutch=nl,Finnish=fi,French=fr,German=de,Italian=it,Polish=pl,Portuguese=pt,Spanish=es,Swedish=sv","hash"),function(i,h){if(h.indexOf("+")===0){h=h.substring(1);g.selectedLang=i}g.languages[h]=i})},createControl:function(h,d){var f=this,g,e=f.editor;if(h=="spellchecker"){if(f.rpcUrl=="{backend}"){if(f.hasSupport){g=d.createButton(h,{title:"spellchecker.desc",cmd:"mceSpellCheck",scope:f})}return g}g=d.createSplitButton(h,{title:"spellchecker.desc",cmd:"mceSpellCheck",scope:f});g.onRenderMenu.add(function(j,i){i.add({title:"spellchecker.langs","class":"mceMenuItemTitle"}).setDisabled(1);c(f.languages,function(n,m){var p={icon:1},l;p.onclick=function(){if(n==f.selectedLang){return}l.setSelected(1);f.selectedItem.setSelected(0);f.selectedItem=l;f.selectedLang=n};p.title=m;l=i.add(p);l.setSelected(n==f.selectedLang);if(n==f.selectedLang){f.selectedItem=l}})});return g}},_walk:function(i,g){var h=this.editor.getDoc(),e;if(h.createTreeWalker){e=h.createTreeWalker(i,NodeFilter.SHOW_TEXT,null,false);while((i=e.nextNode())!=null){g.call(this,i)}}else{tinymce.walk(i,g,"childNodes")}},_getSeparators:function(){var e="",d,f=this.editor.getParam("spellchecker_word_separator_chars",'\\s!"#$%&()*+,-./:;<=>?@[]^_{|}§©«®±¶·¸»¼½¾¿×÷¤\u201d\u201c');for(d=0;d$2");while((s=p.indexOf(""))!=-1){o=p.substring(0,s);if(o.length){r=j.createTextNode(f.decode(o));q.appendChild(r)}p=p.substring(s+10);s=p.indexOf("");o=p.substring(0,s);p=p.substring(s+11);q.appendChild(f.create("span",{"class":"mceItemHiddenSpellWord"},o))}if(p.length){r=j.createTextNode(f.decode(p));q.appendChild(r)}}else{q.innerHTML=p.replace(e,'$1$2')}f.replace(q,t)}});h.moveToBookmark(i)},_showMenu:function(h,j){var i=this,h=i.editor,d=i._menu,l,k=h.dom,g=k.getViewPort(h.getWin()),f=j.target;j=0;if(!d){d=h.controlManager.createDropMenu("spellcheckermenu",{"class":"mceNoIcons"});i._menu=d}if(k.hasClass(f,"mceItemHiddenSpellWord")){d.removeAll();d.add({title:"spellchecker.wait","class":"mceMenuItemTitle"}).setDisabled(1);i._sendRPC("getSuggestions",[i.selectedLang,k.decode(f.innerHTML)],function(m){var e;d.removeAll();if(m.length>0){d.add({title:"spellchecker.sug","class":"mceMenuItemTitle"}).setDisabled(1);c(m,function(n){d.add({title:n,onclick:function(){k.replace(h.getDoc().createTextNode(n),f);i._checkDone()}})});d.addSeparator()}else{d.add({title:"spellchecker.no_sug","class":"mceMenuItemTitle"}).setDisabled(1)}e=i.editor.getParam("spellchecker_enable_ignore_rpc","");d.add({title:"spellchecker.ignore_word",onclick:function(){var n=f.innerHTML;k.remove(f,1);i._checkDone();if(e){h.setProgressState(1);i._sendRPC("ignoreWord",[i.selectedLang,n],function(o){h.setProgressState(0)})}}});d.add({title:"spellchecker.ignore_words",onclick:function(){var n=f.innerHTML;i._removeWords(k.decode(n));i._checkDone();if(e){h.setProgressState(1);i._sendRPC("ignoreWords",[i.selectedLang,n],function(o){h.setProgressState(0)})}}});if(i.editor.getParam("spellchecker_enable_learn_rpc")){d.add({title:"spellchecker.learn_word",onclick:function(){var n=f.innerHTML;k.remove(f,1);i._checkDone();h.setProgressState(1);i._sendRPC("learnWord",[i.selectedLang,n],function(o){h.setProgressState(0)})}})}d.update()});l=b.getPos(h.getContentAreaContainer());d.settings.offset_x=l.x;d.settings.offset_y=l.y;h.selection.select(f);l=k.getPos(f);d.showMenu(l.x,l.y+f.offsetHeight-g.y);return tinymce.dom.Event.cancel(j)}else{d.hideMenu()}},_checkDone:function(){var e=this,d=e.editor,g=d.dom,f;c(g.select("span"),function(h){if(h&&g.hasClass(h,"mceItemHiddenSpellWord")){f=true;return false}});if(!f){e._done()}},_done:function(){var d=this,e=d.active;if(d.active){d.active=0;d._removeWords();if(d._menu){d._menu.hideMenu()}if(e){d.editor.nodeChanged()}}},_sendRPC:function(e,g,d){var f=this;a.sendRPC({url:f.rpcUrl,method:e,params:g,success:d,error:function(i,h){f.editor.setProgressState(0);f.editor.windowManager.alert(i.errstr||("Error response: "+h.responseText))}})}});tinymce.PluginManager.add("spellchecker",tinymce.plugins.SpellcheckerPlugin)})(); \ No newline at end of file +(function(){var a=tinymce.util.JSONRequest,c=tinymce.each,b=tinymce.DOM;tinymce.create("tinymce.plugins.SpellcheckerPlugin",{getInfo:function(){return{longname:"Spellchecker",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/spellchecker",version:tinymce.majorVersion+"."+tinymce.minorVersion}},init:function(e,f){var g=this,d;g.url=f;g.editor=e;g.rpcUrl=e.getParam("spellchecker_rpc_url",this.url+'/rpc.php');if(g.rpcUrl=="{backend}"){if(tinymce.isIE){return}g.hasSupport=true;e.onContextMenu.addToTop(function(h,i){if(g.active){return false}})}e.addCommand("mceSpellCheck",function(){if(g.rpcUrl=="{backend}"){g.editor.getBody().spellcheck=g.active=!g.active;return}if(!g.active){e.setProgressState(1);g._sendRPC("checkWords",[g.selectedLang,g._getWords()],function(h){if(h.length>0){g.active=1;g._markWords(h);e.setProgressState(0);e.nodeChanged()}else{e.setProgressState(0);if(e.getParam("spellchecker_report_no_misspellings",true)){e.windowManager.alert("spellchecker.no_mpell")}}})}else{g._done()}});if(e.settings.content_css!==false){e.contentCSS.push(f+"/css/content.css")}e.onClick.add(g._showMenu,g);e.onContextMenu.add(g._showMenu,g);e.onBeforeGetContent.add(function(){if(g.active){g._removeWords()}});e.onNodeChange.add(function(i,h){h.setActive("spellchecker",g.active)});e.onSetContent.add(function(){g._done()});e.onBeforeGetContent.add(function(){g._done()});e.onBeforeExecCommand.add(function(h,i){if(i=="mceFullScreen"){g._done()}});g.languages={};c(e.getParam("spellchecker_languages","+English=en,Danish=da,Dutch=nl,Finnish=fi,French=fr,German=de,Italian=it,Polish=pl,Portuguese=pt,Spanish=es,Swedish=sv","hash"),function(i,h){if(h.indexOf("+")===0){h=h.substring(1);g.selectedLang=i}g.languages[h]=i})},createControl:function(h,d){var f=this,g,e=f.editor;if(h=="spellchecker"){if(f.rpcUrl=="{backend}"){if(f.hasSupport){g=d.createButton(h,{title:"spellchecker.desc",cmd:"mceSpellCheck",scope:f})}return g}g=d.createSplitButton(h,{title:"spellchecker.desc",cmd:"mceSpellCheck",scope:f});g.onRenderMenu.add(function(j,i){i.add({title:"spellchecker.langs","class":"mceMenuItemTitle"}).setDisabled(1);c(f.languages,function(n,m){var p={icon:1},l;p.onclick=function(){if(n==f.selectedLang){return}l.setSelected(1);f.selectedItem.setSelected(0);f.selectedItem=l;f.selectedLang=n};p.title=m;l=i.add(p);l.setSelected(n==f.selectedLang);if(n==f.selectedLang){f.selectedItem=l}})});return g}},_walk:function(i,g){var h=this.editor.getDoc(),e;if(h.createTreeWalker){e=h.createTreeWalker(i,NodeFilter.SHOW_TEXT,null,false);while((i=e.nextNode())!=null){g.call(this,i)}}else{tinymce.walk(i,g,"childNodes")}},_getSeparators:function(){var e="",d,f=this.editor.getParam("spellchecker_word_separator_chars",'\\s!"#$%&()*+,-./:;<=>?@[]^_{|}§©«®±¶·¸»¼½¾¿×÷¤\u201d\u201c');for(d=0;d$2");while((s=p.indexOf(""))!=-1){o=p.substring(0,s);if(o.length){r=j.createTextNode(f.decode(o));q.appendChild(r)}p=p.substring(s+10);s=p.indexOf("");o=p.substring(0,s);p=p.substring(s+11);q.appendChild(f.create("span",{"class":"mceItemHiddenSpellWord"},o))}if(p.length){r=j.createTextNode(f.decode(p));q.appendChild(r)}}else{q.innerHTML=p.replace(e,'$1$2')}f.replace(q,t)}});h.moveToBookmark(i)},_showMenu:function(h,j){var i=this,h=i.editor,d=i._menu,l,k=h.dom,g=k.getViewPort(h.getWin()),f=j.target;j=0;if(!d){d=h.controlManager.createDropMenu("spellcheckermenu",{"class":"mceNoIcons"});i._menu=d}if(k.hasClass(f,"mceItemHiddenSpellWord")){d.removeAll();d.add({title:"spellchecker.wait","class":"mceMenuItemTitle"}).setDisabled(1);i._sendRPC("getSuggestions",[i.selectedLang,k.decode(f.innerHTML)],function(m){var e;d.removeAll();if(m.length>0){d.add({title:"spellchecker.sug","class":"mceMenuItemTitle"}).setDisabled(1);c(m,function(n){d.add({title:n,onclick:function(){k.replace(h.getDoc().createTextNode(n),f);i._checkDone()}})});d.addSeparator()}else{d.add({title:"spellchecker.no_sug","class":"mceMenuItemTitle"}).setDisabled(1)}if(h.getParam("show_ignore_words",true)){e=i.editor.getParam("spellchecker_enable_ignore_rpc","");d.add({title:"spellchecker.ignore_word",onclick:function(){var n=f.innerHTML;k.remove(f,1);i._checkDone();if(e){h.setProgressState(1);i._sendRPC("ignoreWord",[i.selectedLang,n],function(o){h.setProgressState(0)})}}});d.add({title:"spellchecker.ignore_words",onclick:function(){var n=f.innerHTML;i._removeWords(k.decode(n));i._checkDone();if(e){h.setProgressState(1);i._sendRPC("ignoreWords",[i.selectedLang,n],function(o){h.setProgressState(0)})}}})}if(i.editor.getParam("spellchecker_enable_learn_rpc")){d.add({title:"spellchecker.learn_word",onclick:function(){var n=f.innerHTML;k.remove(f,1);i._checkDone();h.setProgressState(1);i._sendRPC("learnWord",[i.selectedLang,n],function(o){h.setProgressState(0)})}})}d.update()});l=b.getPos(h.getContentAreaContainer());d.settings.offset_x=l.x;d.settings.offset_y=l.y;h.selection.select(f);l=k.getPos(f);d.showMenu(l.x,l.y+f.offsetHeight-g.y);return tinymce.dom.Event.cancel(j)}else{d.hideMenu()}},_checkDone:function(){var e=this,d=e.editor,g=d.dom,f;c(g.select("span"),function(h){if(h&&g.hasClass(h,"mceItemHiddenSpellWord")){f=true;return false}});if(!f){e._done()}},_done:function(){var d=this,e=d.active;if(d.active){d.active=0;d._removeWords();if(d._menu){d._menu.hideMenu()}if(e){d.editor.nodeChanged()}}},_sendRPC:function(e,g,d){var f=this;a.sendRPC({url:f.rpcUrl,method:e,params:g,success:d,error:function(i,h){f.editor.setProgressState(0);f.editor.windowManager.alert(i.errstr||("Error response: "+h.responseText))}})}});tinymce.PluginManager.add("spellchecker",tinymce.plugins.SpellcheckerPlugin)})(); \ No newline at end of file diff --git a/include/javascript/tiny_mce/plugins/spellchecker/editor_plugin_src.js b/include/javascript/tiny_mce/plugins/spellchecker/editor_plugin_src.js index 50df7343..3a4f6f3e 100644 --- a/include/javascript/tiny_mce/plugins/spellchecker/editor_plugin_src.js +++ b/include/javascript/tiny_mce/plugins/spellchecker/editor_plugin_src.js @@ -27,7 +27,7 @@ t.url = url; t.editor = ed; - t.rpcUrl = ed.getParam("spellchecker_rpc_url", "{backend}"); + t.rpcUrl = ed.getParam("spellchecker_rpc_url", this.url+'/rpc.php'); if (t.rpcUrl == '{backend}') { // Sniff if the browser supports native spellchecking (Don't know of a better way) @@ -314,42 +314,44 @@ } else m.add({title : 'spellchecker.no_sug', 'class' : 'mceMenuItemTitle'}).setDisabled(1); - ignoreRpc = t.editor.getParam("spellchecker_enable_ignore_rpc", ''); - m.add({ - title : 'spellchecker.ignore_word', - onclick : function() { - var word = wordSpan.innerHTML; + if (ed.getParam('show_ignore_words', true)) { + ignoreRpc = t.editor.getParam("spellchecker_enable_ignore_rpc", ''); + m.add({ + title : 'spellchecker.ignore_word', + onclick : function() { + var word = wordSpan.innerHTML; - dom.remove(wordSpan, 1); - t._checkDone(); + dom.remove(wordSpan, 1); + t._checkDone(); - // tell the server if we need to - if (ignoreRpc) { - ed.setProgressState(1); - t._sendRPC('ignoreWord', [t.selectedLang, word], function(r) { - ed.setProgressState(0); - }); + // tell the server if we need to + if (ignoreRpc) { + ed.setProgressState(1); + t._sendRPC('ignoreWord', [t.selectedLang, word], function(r) { + ed.setProgressState(0); + }); + } } - } - }); + }); - m.add({ - title : 'spellchecker.ignore_words', - onclick : function() { - var word = wordSpan.innerHTML; + m.add({ + title : 'spellchecker.ignore_words', + onclick : function() { + var word = wordSpan.innerHTML; - t._removeWords(dom.decode(word)); - t._checkDone(); + t._removeWords(dom.decode(word)); + t._checkDone(); - // tell the server if we need to - if (ignoreRpc) { - ed.setProgressState(1); - t._sendRPC('ignoreWords', [t.selectedLang, word], function(r) { - ed.setProgressState(0); - }); + // tell the server if we need to + if (ignoreRpc) { + ed.setProgressState(1); + t._sendRPC('ignoreWords', [t.selectedLang, word], function(r) { + ed.setProgressState(0); + }); + } } - } - }); + }); + } if (t.editor.getParam("spellchecker_enable_learn_rpc")) { m.add({ diff --git a/include/javascript/tiny_mce/plugins/spellchecker/includes/general.php b/include/javascript/tiny_mce/plugins/spellchecker/includes/general.php new file mode 100644 index 00000000..ffea3a0c --- /dev/null +++ b/include/javascript/tiny_mce/plugins/spellchecker/includes/general.php @@ -0,0 +1,98 @@ + $value) + $newarray[$name] = $value; + + return $newarray; + } + + return $_REQUEST[$name]; +} + +function &getLogger() { + global $mcLogger, $man; + + if (isset($man)) + $mcLogger = $man->getLogger(); + + if (!$mcLogger) { + $mcLogger = new Moxiecode_Logger(); + + // Set logger options + $mcLogger->setPath(dirname(__FILE__) . "/../logs"); + $mcLogger->setMaxSize("100kb"); + $mcLogger->setMaxFiles("10"); + $mcLogger->setFormat("{time} - {message}"); + } + + return $mcLogger; +} + +function debug($msg) { + $args = func_get_args(); + + $log = getLogger(); + $log->debug(implode(', ', $args)); +} + +function info($msg) { + $args = func_get_args(); + + $log = getLogger(); + $log->info(implode(', ', $args)); +} + +function error($msg) { + $args = func_get_args(); + + $log = getLogger(); + $log->error(implode(', ', $args)); +} + +function warn($msg) { + $args = func_get_args(); + + $log = getLogger(); + $log->warn(implode(', ', $args)); +} + +function fatal($msg) { + $args = func_get_args(); + + $log = getLogger(); + $log->fatal(implode(', ', $args)); +} + +?> \ No newline at end of file diff --git a/include/javascript/tiny_mce/plugins/spellchecker/rpc.php b/include/javascript/tiny_mce/plugins/spellchecker/rpc.php new file mode 100644 index 00000000..6a567348 --- /dev/null +++ b/include/javascript/tiny_mce/plugins/spellchecker/rpc.php @@ -0,0 +1,112 @@ +decode($raw); + +// Execute RPC +if (isset($config['general.engine'])) { + $spellchecker = new $config['general.engine']($config); + $result = call_user_func_array(array($spellchecker, $input['method']), $input['params']); +} else + die('{"result":null,"id":null,"error":{"errstr":"You must choose an spellchecker engine in the config.php file.","errfile":"","errline":null,"errcontext":"","level":"FATAL"}}'); + +// Request and response id should always be the same +$output = array( + "id" => $input->id, + "result" => $result, + "error" => null +); + +// Return JSON encoded string +echo $json->encode($output); + +?> -- 2.42.0