- Merge devel-spellcheck branch:
- Added spellchecker exceptions dictionary (shared or per-user)
- Added possibility to ignore words containing caps, numbers, symbols (spellcheck_ignore_* options)
| | |
| | | CHANGELOG Roundcube Webmail |
| | | =========================== |
| | | |
| | | - Added spellchecker exceptions dictionary (shared or per-user) |
| | | - Added possibility to ignore words containing caps, numbers, symbols (spellcheck_ignore_* options) |
| | | |
| | | RELEASE 0.6-rc |
| | | ---------------- |
| | | - Send X-Frame-Options headers to protect from clickjacking (#1487037) |
| | |
| | | ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
|
| | | GO
|
| | |
|
| | | CREATE TABLE [dbo].[dictionary] (
|
| | | [user_id] [int] ,
|
| | | [language] [varchar] (5) COLLATE Latin1_General_CI_AI NOT NULL ,
|
| | | [data] [text] COLLATE Latin1_General_CI_AI NOT NULL |
| | | ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
|
| | | GO
|
| | |
|
| | | ALTER TABLE [dbo].[cache] WITH NOCHECK ADD
|
| | | PRIMARY KEY CLUSTERED
|
| | | (
|
| | |
| | | CREATE INDEX [IX_users_alias] ON [dbo].[users]([alias]) ON [PRIMARY]
|
| | | GO
|
| | |
|
| | | CREATE UNIQUE INDEX [IX_dictionary_user_language] ON [dbo].[dictionary]([user_id],[language]) ON [PRIMARY]
|
| | | GO
|
| | |
|
| | | ALTER TABLE [dbo].[identities] ADD CONSTRAINT [FK_identities_user_id]
|
| | | FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id])
|
| | | ON DELETE CASCADE ON UPDATE CASCADE
|
| | |
| | | GO
|
| | | DELETE FROM [dbo].[cache]
|
| | | GO
|
| | |
|
| | | -- Updates from version 0.6-stable
|
| | |
|
| | | CREATE TABLE [dbo].[dictionary] (
|
| | | [user_id] [int] ,
|
| | | [language] [varchar] (5) COLLATE Latin1_General_CI_AI NOT NULL ,
|
| | | [data] [text] COLLATE Latin1_General_CI_AI NOT NULL |
| | | ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
|
| | | GO
|
| | | CREATE UNIQUE INDEX [IX_dictionary_user_language] ON [dbo].[dictionary]([user_id],[language]) ON [PRIMARY]
|
| | | GO
|
| | |
| | | ) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; |
| | | |
| | | |
| | | -- Table structure for table `dictionary` |
| | | |
| | | CREATE TABLE `dictionary` ( |
| | | `user_id` int(10) UNSIGNED DEFAULT NULL, |
| | | `language` varchar(5) NOT NULL, |
| | | `data` longtext NOT NULL, |
| | | CONSTRAINT `user_id_fk_dictionary` FOREIGN KEY (`user_id`) |
| | | REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE, |
| | | UNIQUE `uniqueness` (`user_id`, `language`) |
| | | ) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; |
| | | |
| | | /*!40014 SET FOREIGN_KEY_CHECKS=1 */; |
| | |
| | | |
| | | TRUNCATE TABLE `messages`; |
| | | TRUNCATE TABLE `cache`; |
| | | |
| | | -- Updates from version 0.6-stable |
| | | |
| | | CREATE TABLE `dictionary` ( |
| | | `user_id` int(10) UNSIGNED DEFAULT NULL, |
| | | `language` varchar(5) NOT NULL, |
| | | `data` longtext NOT NULL, |
| | | CONSTRAINT `user_id_fk_dictionary` FOREIGN KEY (`user_id`) |
| | | REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE, |
| | | UNIQUE `uniqueness` (`user_id`, `language`) |
| | | ) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; |
| | |
| | | |
| | | CREATE INDEX messages_index_idx ON messages (user_id, cache_key, idx); |
| | | CREATE INDEX messages_created_idx ON messages (created); |
| | | |
| | | -- |
| | | -- Table "dictionary" |
| | | -- Name: dictionary; Type: TABLE; Schema: public; Owner: postgres |
| | | -- |
| | | |
| | | CREATE TABLE dictionary ( |
| | | user_id integer DEFAULT NULL |
| | | REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE, |
| | | "language" varchar(5) NOT NULL, |
| | | data text NOT NULL, |
| | | CONSTRAINT dictionary_user_id_language_key UNIQUE (user_id, "language") |
| | | ); |
| | |
| | | |
| | | TRUNCATE messages; |
| | | TRUNCATE cache; |
| | | |
| | | -- Updates from version 0.6-stable |
| | | |
| | | CREATE TABLE dictionary ( |
| | | user_id integer DEFAULT NULL |
| | | REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE, |
| | | "language" varchar(5) NOT NULL, |
| | | data text NOT NULL, |
| | | CONSTRAINT dictionary_user_id_language_key UNIQUE (user_id, "language") |
| | | ); |
| | |
| | | CREATE UNIQUE INDEX ix_messages_user_cache_uid ON messages (user_id,cache_key,uid); |
| | | CREATE INDEX ix_messages_index ON messages (user_id,cache_key,idx); |
| | | CREATE INDEX ix_messages_created ON messages (created); |
| | | |
| | | -- -------------------------------------------------------- |
| | | |
| | | -- |
| | | -- Table structure for table dictionary |
| | | -- |
| | | |
| | | CREATE TABLE dictionary ( |
| | | user_id integer DEFAULT NULL, |
| | | "language" varchar(5) NOT NULL, |
| | | data text NOT NULL |
| | | ); |
| | | |
| | | CREATE UNIQUE INDEX ix_dictionary_user_language ON dictionary (user_id, "language"); |
| | | |
| | |
| | | DELETE FROM messages; |
| | | DELETE FROM cache; |
| | | CREATE INDEX ix_contactgroupmembers_contact_id ON contactgroupmembers (contact_id); |
| | | |
| | | -- Updates from version 0.6-stable |
| | | |
| | | CREATE TABLE dictionary ( |
| | | user_id integer DEFAULT NULL, |
| | | "language" varchar(5) NOT NULL, |
| | | data text NOT NULL |
| | | ); |
| | | |
| | | CREATE UNIQUE INDEX ix_dictionary_user_language ON dictionary (user_id, "language"); |
| | |
| | | // requires to be compiled with Open SSL support |
| | | $rcmail_config['enable_spellcheck'] = true; |
| | | |
| | | // Enables spellchecker exceptions dictionary. |
| | | // Setting it to 'shared' will make the dictionary shared by all users. |
| | | $rcmail_config['spellcheck_dictionary'] = false; |
| | | |
| | | // Set the spell checking engine. 'googie' is the default. 'pspell' is also available, |
| | | // but requires the Pspell extensions. When using Nox Spell Server, also set 'googie' here. |
| | | $rcmail_config['spellcheck_engine'] = 'googie'; |
| | |
| | | // Leave empty for default set of available language. |
| | | $rcmail_config['spellcheck_languages'] = NULL; |
| | | |
| | | // Makes that words with all letters capitalized will be ignored (e.g. GOOGLE) |
| | | $rcmail_config['spellcheck_ignore_caps'] = false; |
| | | |
| | | // Makes that words with numbers will be ignored (e.g. g00gle) |
| | | $rcmail_config['spellcheck_ignore_nums'] = false; |
| | | |
| | | // Makes that words with symbols will be ignored (e.g. g@@gle) |
| | | $rcmail_config['spellcheck_ignore_syms'] = false; |
| | | |
| | | // don't let users set pagesize to more than this value if set |
| | | $rcmail_config['max_pagesize'] = 200; |
| | | |
| | |
| | | $hook = $RCMAIL->plugins->exec_hook('html_editor', array('mode' => $mode)); |
| | | |
| | | if ($hook['abort']) |
| | | return; |
| | | return; |
| | | |
| | | $lang = strtolower($_SESSION['language']); |
| | | |
| | |
| | | |
| | | $RCMAIL->output->include_script('tiny_mce/tiny_mce.js'); |
| | | $RCMAIL->output->include_script('editor.js'); |
| | | $RCMAIL->output->add_script(sprintf("rcmail_editor_init('\$__skin_path', '%s', %d, '%s');", |
| | | JQ($lang), intval($CONFIG['enable_spellcheck']), $mode), |
| | | 'foot'); |
| | | $RCMAIL->output->add_script(sprintf("rcmail_editor_init(%s)", |
| | | json_encode(array( |
| | | 'mode' => $mode, |
| | | 'skin_path' => '$__skin_path', |
| | | 'lang' => $lang, |
| | | 'spellcheck' => intval($CONFIG['enable_spellcheck']), |
| | | 'spelldict' => intval($CONFIG['spellcheck_dictionary']), |
| | | ))), 'foot'); |
| | | } |
| | | |
| | | |
| | |
| | | private $lang; |
| | | private $rc; |
| | | private $error; |
| | | private $separator = '/[ !"#$%&()*+\\,\/\n:;<=>?@\[\]^_{|}-]+|\.[^\w]/'; |
| | | |
| | | private $separator = '/[\s\r\n\t\(\)\/\[\]{}<>\\"]+|[:;?!,\.]([^\w]|$)/'; |
| | | private $options = array(); |
| | | private $dict; |
| | | private $have_dict; |
| | | |
| | | |
| | | // default settings |
| | | const GOOGLE_HOST = 'ssl://www.google.com'; |
| | |
| | | */ |
| | | function __construct($lang = 'en') |
| | | { |
| | | $this->rc = rcmail::get_instance(); |
| | | $this->rc = rcmail::get_instance(); |
| | | $this->engine = $this->rc->config->get('spellcheck_engine', 'googie'); |
| | | $this->lang = $lang ? $lang : 'en'; |
| | | $this->lang = $lang ? $lang : 'en'; |
| | | |
| | | if ($this->engine == 'pspell' && !extension_loaded('pspell')) { |
| | | raise_error(array( |
| | |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Pspell extension not available"), true, true); |
| | | } |
| | | |
| | | $this->options = array( |
| | | 'ignore_syms' => $this->rc->config->get('spellcheck_ignore_syms'), |
| | | 'ignore_nums' => $this->rc->config->get('spellcheck_ignore_nums'), |
| | | 'ignore_caps' => $this->rc->config->get('spellcheck_ignore_caps'), |
| | | 'dictionary' => $this->rc->config->get('spellcheck_dictionary'), |
| | | ); |
| | | } |
| | | |
| | | |
| | |
| | | * |
| | | * @return bool True when no mispelling found, otherwise false |
| | | */ |
| | | function check($text, $is_html=false) |
| | | function check($text, $is_html = false) |
| | | { |
| | | // convert to plain text |
| | | if ($is_html) { |
| | |
| | | return $this->_pspell_suggestions($word); |
| | | } |
| | | |
| | | return $this->_googie_suggestions($word); |
| | | return $this->_googie_suggestions($word); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Returns mispelled words |
| | |
| | | $result[$word] = is_array($item[4]) ? implode("\t", $item[4]) : $item[4]; |
| | | } |
| | | |
| | | return $out; |
| | | return $result; |
| | | } |
| | | |
| | | |
| | |
| | | // tokenize |
| | | $text = preg_split($this->separator, $text, NULL, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_OFFSET_CAPTURE); |
| | | |
| | | $diff = 0; |
| | | $matches = array(); |
| | | $diff = 0; |
| | | $matches = array(); |
| | | |
| | | foreach ($text as $w) { |
| | | $word = trim($w[0]); |
| | | $pos = $w[1] - $diff; |
| | | $len = mb_strlen($word); |
| | | |
| | | if ($word && preg_match('/[^0-9\.]/', $word) && !pspell_check($this->plink, $word)) { |
| | | // skip exceptions |
| | | if ($this->is_exception($word)) { |
| | | } |
| | | else if (!pspell_check($this->plink, $word)) { |
| | | $suggestions = pspell_suggest($this->plink, $word); |
| | | |
| | | if (sizeof($suggestions) > self::MAX_SUGGESTIONS) |
| | |
| | | */ |
| | | private function _pspell_words($text = null, $is_html=false) |
| | | { |
| | | $result = array(); |
| | | |
| | | if ($text) { |
| | | // init spellchecker |
| | | $this->_pspell_init(); |
| | |
| | | |
| | | foreach ($text as $w) { |
| | | $word = trim($w[0]); |
| | | if ($word && preg_match('/[^0-9\.]/', $word) && !pspell_check($this->plink, $word)) { |
| | | |
| | | // skip exceptions |
| | | if ($this->is_exception($word)) { |
| | | continue; |
| | | } |
| | | |
| | | if (!pspell_check($this->plink, $word)) { |
| | | $result[] = $word; |
| | | } |
| | | } |
| | | |
| | | return $result; |
| | | } |
| | | |
| | | $result = array(); |
| | | |
| | | foreach ($this->matches as $m) { |
| | | $result[] = $m[0]; |
| | |
| | | } |
| | | |
| | | // Google has some problem with spaces, use \n instead |
| | | $text = str_replace(' ', "\n", $text); |
| | | $gtext = str_replace(' ', "\n", $text); |
| | | |
| | | $text = '<?xml version="1.0" encoding="utf-8" ?>' |
| | | $gtext = '<?xml version="1.0" encoding="utf-8" ?>' |
| | | .'<spellrequest textalreadyclipped="0" ignoredups="0" ignoredigits="1" ignoreallcaps="1">' |
| | | .'<text>' . $text . '</text>' |
| | | .'<text>' . $gtext . '</text>' |
| | | .'</spellrequest>'; |
| | | |
| | | $store = ''; |
| | | if ($fp = fsockopen($host, $port, $errno, $errstr, 30)) { |
| | | $out = "POST $path HTTP/1.0\r\n"; |
| | | $out .= "Host: " . str_replace('ssl://', '', $host) . "\r\n"; |
| | | $out .= "Content-Length: " . strlen($text) . "\r\n"; |
| | | $out .= "Content-Length: " . strlen($gtext) . "\r\n"; |
| | | $out .= "Content-Type: application/x-www-form-urlencoded\r\n"; |
| | | $out .= "Connection: Close\r\n\r\n"; |
| | | $out .= $text; |
| | | $out .= $gtext; |
| | | fwrite($fp, $out); |
| | | |
| | | while (!feof($fp)) |
| | |
| | | } |
| | | |
| | | preg_match_all('/<c o="([^"]*)" l="([^"]*)" s="([^"]*)">([^<]*)<\/c>/', $store, $matches, PREG_SET_ORDER); |
| | | |
| | | // skip exceptions (if appropriate options are enabled) |
| | | if (!empty($this->options['ignore_syms']) || !empty($this->options['ignore_nums']) |
| | | || !empty($this->options['ignore_caps']) || !empty($this->options['dictionary']) |
| | | ) { |
| | | foreach ($matches as $idx => $m) { |
| | | $word = mb_substr($text, $m[1], $m[2], RCMAIL_CHARSET); |
| | | // skip exceptions |
| | | if ($this->is_exception($word)) { |
| | | unset($matches[$idx]); |
| | | } |
| | | } |
| | | } |
| | | |
| | | return $matches; |
| | | } |
| | |
| | | $h2t = new html2text($text, false, true, 0); |
| | | return $h2t->get_text(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Check if the specified word is an exception accoring to |
| | | * spellcheck options. |
| | | * |
| | | * @param string $word The word |
| | | * |
| | | * @return bool True if the word is an exception, False otherwise |
| | | */ |
| | | public function is_exception($word) |
| | | { |
| | | // Contain only symbols (e.g. "+9,0", "2:2") |
| | | if (!$word || preg_match('/^[0-9@#$%^&_+~*=:;?!,.-]+$/', $word)) |
| | | return true; |
| | | |
| | | // Contain symbols (e.g. "g@@gle"), all symbols excluding separators |
| | | if (!empty($this->options['ignore_syms']) && preg_match('/[@#$%^&_+~*=-]/', $word)) |
| | | return true; |
| | | |
| | | // Contain numbers (e.g. "g00g13") |
| | | if (!empty($this->options['ignore_nums']) && preg_match('/[0-9]/', $word)) |
| | | return true; |
| | | |
| | | // Blocked caps (e.g. "GOOGLE") |
| | | if (!empty($this->options['ignore_caps']) && $word == mb_strtoupper($word)) |
| | | return true; |
| | | |
| | | // Use exceptions from dictionary |
| | | if (!empty($this->options['dictionary'])) { |
| | | $this->load_dict(); |
| | | |
| | | // @TODO: should dictionary be case-insensitive? |
| | | if (!empty($this->dict) && in_array($word, $this->dict)) |
| | | return true; |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Add a word to dictionary |
| | | * |
| | | * @param string $word The word to add |
| | | */ |
| | | public function add_word($word) |
| | | { |
| | | $this->load_dict(); |
| | | |
| | | foreach (explode(' ', $word) as $word) { |
| | | // sanity check |
| | | if (strlen($word) < 512) { |
| | | $this->dict[] = $word; |
| | | $valid = true; |
| | | } |
| | | } |
| | | |
| | | if ($valid) { |
| | | $this->dict = array_unique($this->dict); |
| | | $this->update_dict(); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Remove a word from dictionary |
| | | * |
| | | * @param string $word The word to remove |
| | | */ |
| | | public function remove_word($word) |
| | | { |
| | | $this->load_dict(); |
| | | |
| | | if (($key = array_search($word, $this->dict)) !== false) { |
| | | unset($this->dict[$key]); |
| | | $this->update_dict(); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Update dictionary row in DB |
| | | */ |
| | | private function update_dict() |
| | | { |
| | | if (strcasecmp($this->options['dictionary'], 'shared') != 0) { |
| | | $userid = (int) $this->rc->user->ID; |
| | | } |
| | | |
| | | $plugin = $this->rc->plugins->exec_hook('spell_dictionary_save', array( |
| | | 'userid' => $userid, 'language' => $this->lang, 'dictionary' => $this->dict)); |
| | | |
| | | if (!empty($plugin['abort'])) { |
| | | return; |
| | | } |
| | | |
| | | if ($this->have_dict) { |
| | | if (!empty($this->dict)) { |
| | | $this->rc->db->query( |
| | | "UPDATE ".get_table_name('dictionary') |
| | | ." SET data = ?" |
| | | ." WHERE user_id " . ($plugin['userid'] ? "= ".$plugin['userid'] : "IS NULL") |
| | | ." AND " . $this->rc->db->quoteIdentifier('language') . " = ?", |
| | | implode(' ', $plugin['dictionary']), $plugin['language']); |
| | | } |
| | | // don't store empty dict |
| | | else { |
| | | $this->rc->db->query( |
| | | "DELETE FROM " . get_table_name('dictionary') |
| | | ." WHERE user_id " . ($plugin['userid'] ? "= ".$plugin['userid'] : "IS NULL") |
| | | ." AND " . $this->rc->db->quoteIdentifier('language') . " = ?", |
| | | $plugin['language']); |
| | | } |
| | | } |
| | | else if (!empty($this->dict)) { |
| | | $this->rc->db->query( |
| | | "INSERT INTO " .get_table_name('dictionary') |
| | | ." (user_id, " . $this->rc->db->quoteIdentifier('language') . ", data) VALUES (?, ?, ?)", |
| | | $plugin['userid'], $plugin['language'], implode(' ', $plugin['dictionary'])); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Get dictionary from DB |
| | | */ |
| | | private function load_dict() |
| | | { |
| | | if (is_array($this->dict)) { |
| | | return $this->dict; |
| | | } |
| | | |
| | | if (strcasecmp($this->options['dictionary'], 'shared') != 0) { |
| | | $userid = (int) $this->rc->user->ID; |
| | | } |
| | | |
| | | $plugin = $this->rc->plugins->exec_hook('spell_dictionary_get', array( |
| | | 'userid' => $userid, 'language' => $this->lang, 'dictionary' => array())); |
| | | |
| | | if (empty($plugin['abort'])) { |
| | | $dict = array(); |
| | | $this->rc->db->query( |
| | | "SELECT data FROM ".get_table_name('dictionary') |
| | | ." WHERE user_id ". ($plugin['userid'] ? "= ".$plugin['userid'] : "IS NULL") |
| | | ." AND " . $this->rc->db->quoteIdentifier('language') . " = ?", |
| | | $plugin['language']); |
| | | |
| | | if ($sql_arr = $this->rc->db->fetch_assoc($sql_result)) { |
| | | $this->have_dict = true; |
| | | if (!empty($sql_arr['data'])) { |
| | | $dict = explode(' ', $sql_arr['data']); |
| | | } |
| | | } |
| | | |
| | | $plugin['dictionary'] = array_merge((array)$plugin['dictionary'], $dict); |
| | | } |
| | | |
| | | if (!empty($plugin['dictionary']) && is_array($plugin['dictionary'])) { |
| | | $this->dict = $plugin['dictionary']; |
| | | } |
| | | else { |
| | | $this->dict = array(); |
| | | } |
| | | |
| | | return $this->dict; |
| | | } |
| | | |
| | | } |
| | |
| | | */ |
| | | |
| | | // Initialize HTML editor |
| | | function rcmail_editor_init(skin_path, editor_lang, spellcheck, mode) |
| | | function rcmail_editor_init(config) |
| | | { |
| | | var ret, conf = { |
| | | mode: 'textareas', |
| | | editor_selector: 'mce_editor', |
| | | apply_source_formatting: true, |
| | | theme: 'advanced', |
| | | language: editor_lang, |
| | | content_css: skin_path + '/editor_content.css', |
| | | language: config.lang, |
| | | content_css: config.skin_path + '/editor_content.css', |
| | | theme_advanced_toolbar_location: 'top', |
| | | theme_advanced_toolbar_align: 'left', |
| | | theme_advanced_buttons3: '', |
| | |
| | | rc_client: rcmail |
| | | }; |
| | | |
| | | if (mode == 'identity') |
| | | if (config.mode == 'identity') |
| | | $.extend(conf, { |
| | | plugins: 'paste,tabfocus', |
| | | theme_advanced_buttons1: 'bold,italic,underline,strikethrough,justifyleft,justifycenter,justifyright,justifyfull,separator,outdent,indent,charmap,hr,link,unlink,code,forecolor', |
| | |
| | | }); |
| | | else // mail compose |
| | | $.extend(conf, { |
| | | plugins: 'paste,emotions,media,nonbreaking,table,searchreplace,visualchars,directionality,tabfocus' + (spellcheck ? ',spellchecker' : ''), |
| | | plugins: 'paste,emotions,media,nonbreaking,table,searchreplace,visualchars,directionality,tabfocus' + (config.spellcheck ? ',spellchecker' : ''), |
| | | theme_advanced_buttons1: 'bold,italic,underline,|,justifyleft,justifycenter,justifyright,justifyfull,|,bullist,numlist,outdent,indent,ltr,rtl,blockquote,|,forecolor,backcolor,fontselect,fontsizeselect', |
| | | theme_advanced_buttons2: 'link,unlink,table,|,emotions,charmap,image,media,|,code,search' + (spellcheck ? ',spellchecker' : '') + ',undo,redo', |
| | | theme_advanced_buttons2: 'link,unlink,table,|,emotions,charmap,image,media,|,code,search' + (config.spellcheck ? ',spellchecker' : '') + ',undo,redo', |
| | | spellchecker_languages: (rcmail.env.spellcheck_langs ? rcmail.env.spellcheck_langs : 'Dansk=da,Deutsch=de,+English=en,Espanol=es,Francais=fr,Italiano=it,Nederlands=nl,Polski=pl,Portugues=pt,Suomi=fi,Svenska=sv'), |
| | | spellchecker_rpc_url: '?_task=utils&_action=spell_html', |
| | | spellchecker_enable_learn_rpc: config.spelldict, |
| | | accessibility_focus: false, |
| | | oninit: 'rcmail_editor_callback' |
| | | }); |
| | |
| | | /* |
| | | SpellCheck |
| | | jQuery'fied spell checker based on GoogieSpell 4.0 |
| | | Copyright Amir Salihefendic 2006 |
| | | Copyright Aleksander Machniak 2009 |
| | | Copyright (C) 2006 Amir Salihefendic |
| | | Copyright (C) 2009 Aleksander Machniak |
| | | Copyright (C) 2011 Kolab Systems AG |
| | | LICENSE |
| | | GPL |
| | | AUTHORS |
| | |
| | | var GOOGIE_CUR_LANG, |
| | | GOOGIE_DEFAULT_LANG = 'en'; |
| | | |
| | | function GoogieSpell(img_dir, server_url) { |
| | | function GoogieSpell(img_dir, server_url, has_dict) |
| | | { |
| | | var ref = this, |
| | | cookie_value = getCookie('language'); |
| | | |
| | |
| | | this.lang_rsm_edt = "Resume editing"; |
| | | this.lang_no_error_found = "No spelling errors found"; |
| | | this.lang_no_suggestions = "No suggestions"; |
| | | this.lang_learn_word = "Add to dictionary"; |
| | | |
| | | this.show_spell_img = false; // roundcube mod. |
| | | this.decoration = true; |
| | |
| | | this.extra_menu_items = []; |
| | | this.custom_spellcheck_starter = null; |
| | | this.main_controller = true; |
| | | this.has_dictionary = has_dict; |
| | | |
| | | // Observers |
| | | this.lang_state_observer = null; |
| | |
| | | }); |
| | | |
| | | |
| | | this.decorateTextarea = function(id) { |
| | | this.decorateTextarea = function(id) |
| | | { |
| | | this.text_area = typeof id === 'string' ? document.getElementById(id) : id; |
| | | |
| | | if (this.text_area) { |
| | |
| | | ////// |
| | | // API Functions (the ones that you can call) |
| | | ///// |
| | | this.setSpellContainer = function(id) { |
| | | this.setSpellContainer = function(id) |
| | | { |
| | | this.spell_container = typeof id === 'string' ? document.getElementById(id) : id; |
| | | }; |
| | | |
| | | this.setLanguages = function(lang_dict) { |
| | | this.setLanguages = function(lang_dict) |
| | | { |
| | | this.lang_to_word = lang_dict; |
| | | this.langlist_codes = this.array_keys(lang_dict); |
| | | }; |
| | | |
| | | this.setCurrentLanguage = function(lan_code) { |
| | | this.setCurrentLanguage = function(lan_code) |
| | | { |
| | | GOOGIE_CUR_LANG = lan_code; |
| | | |
| | | //Set cookie |
| | |
| | | setCookie('language', lan_code, now); |
| | | }; |
| | | |
| | | this.setForceWidthHeight = function(width, height) { |
| | | this.setForceWidthHeight = function(width, height) |
| | | { |
| | | // Set to null if you want to use one of them |
| | | this.force_width = width; |
| | | this.force_height = height; |
| | | }; |
| | | |
| | | this.setDecoration = function(bool) { |
| | | this.setDecoration = function(bool) |
| | | { |
| | | this.decoration = bool; |
| | | }; |
| | | |
| | | this.dontUseCloseButtons = function() { |
| | | this.dontUseCloseButtons = function() |
| | | { |
| | | this.use_close_btn = false; |
| | | }; |
| | | |
| | | this.appendNewMenuItem = function(name, call_back_fn, checker) { |
| | | this.appendNewMenuItem = function(name, call_back_fn, checker) |
| | | { |
| | | this.extra_menu_items.push([name, call_back_fn, checker]); |
| | | }; |
| | | |
| | | this.appendCustomMenuBuilder = function(eval, builder) { |
| | | this.appendCustomMenuBuilder = function(eval, builder) |
| | | { |
| | | this.custom_menu_builder.push([eval, builder]); |
| | | }; |
| | | |
| | | this.setFocus = function() { |
| | | this.setFocus = function() |
| | | { |
| | | try { |
| | | this.focus_link_b.focus(); |
| | | this.focus_link_t.focus(); |
| | |
| | | ////// |
| | | // Set functions (internal) |
| | | ///// |
| | | this.setStateChanged = function(current_state) { |
| | | this.setStateChanged = function(current_state) |
| | | { |
| | | this.state = current_state; |
| | | if (this.spelling_state_observer != null && this.report_state_change) |
| | | this.spelling_state_observer(current_state, this); |
| | | }; |
| | | |
| | | this.setReportStateChange = function(bool) { |
| | | this.setReportStateChange = function(bool) |
| | | { |
| | | this.report_state_change = bool; |
| | | }; |
| | | |
| | |
| | | ////// |
| | | // Request functions |
| | | ///// |
| | | this.getUrl = function() { |
| | | this.getUrl = function() |
| | | { |
| | | return this.server_url + GOOGIE_CUR_LANG; |
| | | }; |
| | | |
| | | this.escapeSpecial = function(val) { |
| | | this.escapeSpecial = function(val) |
| | | { |
| | | return val.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">"); |
| | | }; |
| | | |
| | | this.createXMLReq = function (text) { |
| | | this.createXMLReq = function (text) |
| | | { |
| | | return '<?xml version="1.0" encoding="utf-8" ?>' |
| | | + '<spellrequest textalreadyclipped="0" ignoredups="0" ignoredigits="1" ignoreallcaps="1">' |
| | | + '<text>' + text + '</text></spellrequest>'; |
| | | }; |
| | | |
| | | this.spellCheck = function(ignore) { |
| | | this.spellCheck = function(ignore) |
| | | { |
| | | this.prepare(ignore); |
| | | |
| | | var req_text = this.escapeSpecial(this.orginal_text), |
| | | ref = this; |
| | | |
| | | $.ajax({ type: 'POST', url: this.getUrl(), |
| | | data: this.createXMLReq(req_text), dataType: 'text', |
| | | $.ajax({ type: 'POST', url: this.getUrl(), data: this.createXMLReq(req_text), dataType: 'text', |
| | | error: function(o) { |
| | | if (ref.custom_ajax_error) |
| | | ref.custom_ajax_error(ref); |
| | |
| | | ref.custom_no_spelling_error(ref); |
| | | } |
| | | ref.removeIndicator(); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | this.learnWord = function(word, id) |
| | | { |
| | | word = this.escapeSpecial(word.innerHTML); |
| | | |
| | | var ref = this, |
| | | req_text = '<?xml version="1.0" encoding="utf-8" ?><learnword><text>' + word + '</text></learnword>'; |
| | | |
| | | $.ajax({ type: 'POST', url: this.getUrl(), data: req_text, dataType: 'text', |
| | | error: function(o) { |
| | | if (ref.custom_ajax_error) |
| | | ref.custom_ajax_error(ref); |
| | | else |
| | | alert('An error was encountered on the server. Please try again later.'); |
| | | }, |
| | | success: function(data) { |
| | | } |
| | | }); |
| | | }; |
| | |
| | | this.orginal_text = $(this.text_area).val(); |
| | | }; |
| | | |
| | | this.parseResult = function(r_text) { |
| | | this.parseResult = function(r_text) |
| | | { |
| | | // Returns an array: result[item] -> ['attrs'], ['suggestions'] |
| | | var re_split_attr_c = /\w+="(\d+|true)"/g, |
| | | re_split_text = /\t/g, |
| | |
| | | ////// |
| | | // Error menu functions |
| | | ///// |
| | | this.createErrorWindow = function() { |
| | | this.createErrorWindow = function() |
| | | { |
| | | this.error_window = document.createElement('div'); |
| | | $(this.error_window).addClass('googie_window popupmenu').attr('googie_action_btn', '1'); |
| | | }; |
| | | |
| | | this.isErrorWindowShown = function() { |
| | | this.isErrorWindowShown = function() |
| | | { |
| | | return $(this.error_window).is(':visible'); |
| | | }; |
| | | |
| | | this.hideErrorWindow = function() { |
| | | this.hideErrorWindow = function() |
| | | { |
| | | $(this.error_window).hide(); |
| | | $(this.error_window_iframe).hide(); |
| | | }; |
| | | |
| | | this.updateOrginalText = function(offset, old_value, new_value, id) { |
| | | this.updateOrginalText = function(offset, old_value, new_value, id) |
| | | { |
| | | var part_1 = this.orginal_text.substring(0, offset), |
| | | part_2 = this.orginal_text.substring(offset+old_value.length), |
| | | add_2_offset = new_value.length - old_value.length; |
| | |
| | | elm.old_value = old_value; |
| | | }; |
| | | |
| | | this.createListSeparator = function() { |
| | | this.createListSeparator = function() |
| | | { |
| | | var td = document.createElement('td'), |
| | | tr = document.createElement('tr'); |
| | | |
| | | $(td).html(' ').attr('googie_action_btn', '1') |
| | | .css({'cursor': 'default', 'font-size': '3px', 'border-top': '1px solid #ccc', 'padding-top': '3px'}); |
| | | .css({'cursor': 'default', 'font-size': '3px', 'border-top': '1px solid #ccc', 'padding-top': '3px'}); |
| | | tr.appendChild(td); |
| | | |
| | | return tr; |
| | | }; |
| | | |
| | | this.correctError = function(id, elm, l_elm, rm_pre_space) { |
| | | this.correctError = function(id, elm, l_elm, rm_pre_space) |
| | | { |
| | | var old_value = elm.innerHTML, |
| | | new_value = l_elm.nodeType == 3 ? l_elm.nodeValue : l_elm.innerHTML, |
| | | offset = this.results[id]['attrs']['o']; |
| | |
| | | this.errorFixed(); |
| | | }; |
| | | |
| | | this.showErrorWindow = function(elm, id) { |
| | | this.ignoreError = function(elm, id) |
| | | { |
| | | // @TODO: ignore all same words |
| | | $(elm).removeAttr('class').css('color', '').unbind(); |
| | | this.hideErrorWindow(); |
| | | }; |
| | | |
| | | this.showErrorWindow = function(elm, id) |
| | | { |
| | | if (this.show_menu_observer) |
| | | this.show_menu_observer(this); |
| | | |
| | |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (!changed) { |
| | | // Build up the result list |
| | | var suggestions = this.results[id]['suggestions'], |
| | |
| | | len = this.results[id]['attrs']['l'], |
| | | row, item, dummy; |
| | | |
| | | // [Add to dictionary] button |
| | | if (this.has_dictionary && !$(elm).attr('is_corrected')) { |
| | | row = document.createElement('tr'), |
| | | item = document.createElement('td'), |
| | | dummy = document.createElement('span'); |
| | | |
| | | $(dummy).text(this.lang_learn_word); |
| | | $(item).attr('googie_action_btn', '1').css('cursor', 'default') |
| | | .mouseover(ref.item_onmouseover) |
| | | .mouseout(ref.item_onmouseout) |
| | | .click(function(e) { |
| | | ref.learnWord(elm, id); |
| | | ref.ignoreError(elm, id); |
| | | }); |
| | | |
| | | item.appendChild(dummy); |
| | | row.appendChild(item); |
| | | list.appendChild(row); |
| | | } |
| | | /* |
| | | if (suggestions.length == 0) { |
| | | row = document.createElement('tr'), |
| | | item = document.createElement('td'), |
| | |
| | | row.appendChild(item); |
| | | list.appendChild(row); |
| | | } |
| | | |
| | | */ |
| | | for (var i=0, len=suggestions.length; i < len; i++) { |
| | | row = document.createElement('tr'), |
| | | item = document.createElement('td'), |
| | |
| | | |
| | | $(dummy).html(suggestions[i]); |
| | | |
| | | $(item).bind('mouseover', this.item_onmouseover) |
| | | .bind('mouseout', this.item_onmouseout) |
| | | .bind('click', function(e) { ref.correctError(id, elm, e.target.firstChild) }); |
| | | $(item).mouseover(this.item_onmouseover).mouseout(this.item_onmouseout) |
| | | .click(function(e) { ref.correctError(id, elm, e.target.firstChild) }); |
| | | |
| | | item.appendChild(dummy); |
| | | row.appendChild(item); |
| | | list.appendChild(row); |
| | | } |
| | | |
| | | //The element is changed, append the revert |
| | | // The element is changed, append the revert |
| | | if (elm.is_changed && elm.innerHTML != elm.old_value) { |
| | | var old_value = elm.old_value, |
| | | revert_row = document.createElement('tr'), |
| | |
| | | |
| | | $(rev_span).addClass('googie_list_revert').html(this.lang_revert + ' ' + old_value); |
| | | |
| | | $(revert).bind('mouseover', this.item_onmouseover) |
| | | .bind('mouseout', this.item_onmouseout) |
| | | .bind('click', function(e) { |
| | | $(revert).mouseover(this.item_onmouseover).mouseout(this.item_onmouseout) |
| | | .click(function(e) { |
| | | ref.updateOrginalText(offset, elm.innerHTML, old_value, id); |
| | | $(elm).attr('is_corrected', true).css('color', '#b91414').html(old_value); |
| | | $(elm).removeAttr('is_corrected').css('color', '#b91414').html(old_value); |
| | | ref.hideErrorWindow(); |
| | | }); |
| | | |
| | |
| | | $(ok_pic).attr('src', this.img_dir + 'ok.gif') |
| | | .width(32).height(16) |
| | | .css({'cursor': 'pointer', 'margin-left': '2px', 'margin-right': '2px'}) |
| | | .bind('click', onsub); |
| | | .click(onsub); |
| | | |
| | | $(edit_form).attr('googie_action_btn', '1') |
| | | .css({'margin': 0, 'padding': 0, 'cursor': 'default', 'white-space': 'nowrap'}) |
| | | .bind('submit', onsub); |
| | | .submit(onsub); |
| | | |
| | | edit_form.appendChild(edit_input); |
| | | edit_form.appendChild(ok_pic); |
| | |
| | | e_col = document.createElement('td'); |
| | | |
| | | $(e_col).html(e_elm[0]) |
| | | .bind('mouseover', ref.item_onmouseover) |
| | | .bind('mouseout', ref.item_onmouseout) |
| | | .bind('click', function() { return e_elm[1](elm, ref) }); |
| | | .mouseover(ref.item_onmouseover) |
| | | .mouseout(ref.item_onmouseout) |
| | | .click(function() { return e_elm[1](elm, ref) }); |
| | | |
| | | e_row.appendChild(e_col); |
| | | list.appendChild(e_row); |
| | |
| | | ////// |
| | | // Edit layer (the layer where the suggestions are stored) |
| | | ////// |
| | | this.createEditLayer = function(width, height) { |
| | | this.createEditLayer = function(width, height) |
| | | { |
| | | this.edit_layer = document.createElement('div'); |
| | | $(this.edit_layer).addClass('googie_edit_layer').attr('id', 'googie_edit_layer') |
| | | .width('auto').height(height); |
| | |
| | | } |
| | | }; |
| | | |
| | | this.resumeEditing = function() { |
| | | this.resumeEditing = function() |
| | | { |
| | | this.setStateChanged('ready'); |
| | | |
| | | if (this.edit_layer) |
| | |
| | | this.checkSpellingState(false); |
| | | }; |
| | | |
| | | this.createErrorLink = function(text, id) { |
| | | this.createErrorLink = function(text, id) |
| | | { |
| | | var elm = document.createElement('span'), |
| | | ref = this, |
| | | d = function (e) { |
| | |
| | | return false; |
| | | }; |
| | | |
| | | $(elm).html(text).addClass('googie_link').bind('click', d) |
| | | .attr({'googie_action_btn' : '1', 'g_id' : id, 'is_corrected' : false}); |
| | | $(elm).html(text).addClass('googie_link').click(d).removeAttr('is_corrected') |
| | | .attr({'googie_action_btn' : '1', 'g_id' : id}); |
| | | |
| | | return elm; |
| | | }; |
| | | |
| | | this.createPart = function(txt_part) { |
| | | this.createPart = function(txt_part) |
| | | { |
| | | if (txt_part == " ") |
| | | return document.createTextNode(" "); |
| | | |
| | |
| | | return span; |
| | | }; |
| | | |
| | | this.showErrorsInIframe = function() { |
| | | this.showErrorsInIframe = function() |
| | | { |
| | | var output = document.createElement('div'), |
| | | pointer = 0, |
| | | results = this.results; |
| | |
| | | ////// |
| | | // Choose language menu |
| | | ////// |
| | | this.createLangWindow = function() { |
| | | this.createLangWindow = function() |
| | | { |
| | | this.language_window = document.createElement('div'); |
| | | $(this.language_window).addClass('googie_window popupmenu') |
| | | .width(100).attr('googie_action_btn', '1'); |
| | |
| | | this.language_window.appendChild(table); |
| | | }; |
| | | |
| | | this.isLangWindowShown = function() { |
| | | this.isLangWindowShown = function() |
| | | { |
| | | return $(this.language_window).is(':visible'); |
| | | }; |
| | | |
| | | this.hideLangWindow = function() { |
| | | this.hideLangWindow = function() |
| | | { |
| | | $(this.language_window).hide(); |
| | | $(this.switch_lan_pic).removeClass().addClass('googie_lang_3d_on'); |
| | | }; |
| | | |
| | | this.showLangWindow = function(elm) { |
| | | this.showLangWindow = function(elm) |
| | | { |
| | | if (this.show_menu_observer) |
| | | this.show_menu_observer(this); |
| | | |
| | |
| | | this.highlightCurSel(); |
| | | }; |
| | | |
| | | this.deHighlightCurSel = function() { |
| | | this.deHighlightCurSel = function() |
| | | { |
| | | $(this.lang_cur_elm).removeClass().addClass('googie_list_onout'); |
| | | }; |
| | | |
| | | this.highlightCurSel = function() { |
| | | this.highlightCurSel = function() |
| | | { |
| | | if (GOOGIE_CUR_LANG == null) |
| | | GOOGIE_CUR_LANG = GOOGIE_DEFAULT_LANG; |
| | | for (var i=0; i < this.lang_elms.length; i++) { |
| | |
| | | } |
| | | }; |
| | | |
| | | this.createChangeLangPic = function() { |
| | | this.createChangeLangPic = function() |
| | | { |
| | | var img = $('<img>') |
| | | .attr({src: this.img_dir + 'change_lang.gif', 'alt': 'Change language', 'googie_action_btn': '1'}), |
| | | switch_lan = document.createElement('span'); |
| | |
| | | return switch_lan; |
| | | }; |
| | | |
| | | this.createSpellDiv = function() { |
| | | this.createSpellDiv = function() |
| | | { |
| | | var span = document.createElement('span'); |
| | | |
| | | $(span).addClass('googie_check_spelling_link').text(this.lang_chck_spell); |
| | |
| | | ////// |
| | | // State functions |
| | | ///// |
| | | this.flashNoSpellingErrorState = function(on_finish) { |
| | | this.flashNoSpellingErrorState = function(on_finish) |
| | | { |
| | | this.setStateChanged('no_error_found'); |
| | | |
| | | var ref = this; |
| | |
| | | } |
| | | }; |
| | | |
| | | this.resumeEditingState = function() { |
| | | this.resumeEditingState = function() |
| | | { |
| | | this.setStateChanged('resume_editing'); |
| | | |
| | | //Change link text to resume |
| | |
| | | catch (e) {}; |
| | | }; |
| | | |
| | | this.checkSpellingState = function(fire) { |
| | | this.checkSpellingState = function(fire) |
| | | { |
| | | if (fire) |
| | | this.setStateChanged('ready'); |
| | | |
| | |
| | | ////// |
| | | // Misc. functions |
| | | ///// |
| | | this.isDefined = function(o) { |
| | | this.isDefined = function(o) |
| | | { |
| | | return (o !== undefined && o !== null) |
| | | }; |
| | | |
| | | this.errorFixed = function() { |
| | | this.cnt_errors_fixed++; |
| | | this.errorFixed = function() |
| | | { |
| | | this.cnt_errors_fixed++; |
| | | if (this.all_errors_fixed_observer) |
| | | if (this.cnt_errors_fixed == this.cnt_errors) { |
| | | this.hideErrorWindow(); |
| | |
| | | } |
| | | }; |
| | | |
| | | this.errorFound = function() { |
| | | this.errorFound = function() |
| | | { |
| | | this.cnt_errors++; |
| | | }; |
| | | |
| | | this.createCloseButton = function(c_fn) { |
| | | this.createCloseButton = function(c_fn) |
| | | { |
| | | return this.createButton(this.lang_close, 'googie_list_close', c_fn); |
| | | }; |
| | | |
| | | this.createButton = function(name, css_class, c_fn) { |
| | | this.createButton = function(name, css_class, c_fn) |
| | | { |
| | | var btn_row = document.createElement('tr'), |
| | | btn = document.createElement('td'), |
| | | spn_btn; |
| | |
| | | return btn_row; |
| | | }; |
| | | |
| | | this.removeIndicator = function(elm) { |
| | | this.removeIndicator = function(elm) |
| | | { |
| | | //$(this.indicator).remove(); |
| | | // roundcube mod. |
| | | if (window.rcmail) |
| | | rcmail.set_busy(false, null, this.rc_msg_id); |
| | | }; |
| | | |
| | | this.appendIndicator = function(elm) { |
| | | this.appendIndicator = function(elm) |
| | | { |
| | | // modified by roundcube |
| | | if (window.rcmail) |
| | | this.rc_msg_id = rcmail.set_busy(true, 'checking'); |
| | |
| | | */ |
| | | } |
| | | |
| | | this.createFocusLink = function(name) { |
| | | this.createFocusLink = function(name) |
| | | { |
| | | var link = document.createElement('a'); |
| | | $(link).attr({'href': 'javascript:;', 'name': name}); |
| | | return link; |
| | | }; |
| | | |
| | | this.item_onmouseover = function(e) { |
| | | this.item_onmouseover = function(e) |
| | | { |
| | | if (this.className != 'googie_list_revert' && this.className != 'googie_list_close') |
| | | this.className = 'googie_list_onhover'; |
| | | else |
| | | this.parentNode.className = 'googie_list_onhover'; |
| | | }; |
| | | this.item_onmouseout = function(e) { |
| | | |
| | | this.item_onmouseout = function(e) |
| | | { |
| | | if (this.className != 'googie_list_revert' && this.className != 'googie_list_close') |
| | | this.className = 'googie_list_onout'; |
| | | else |
| | |
| | | $labels['replysamefolder'] = 'Place replies in the folder of the message being replied to'; |
| | | $labels['defaultaddressbook'] = 'Add new contacts to the selected addressbook'; |
| | | $labels['spellcheckbeforesend'] = 'Check spelling before sending a message'; |
| | | $labels['spellcheckoptions'] = 'Spellcheck Options'; |
| | | $labels['spellcheckignoresyms'] = 'Ignore words with symbols'; |
| | | $labels['spellcheckignorenums'] = 'Ignore words with numbers'; |
| | | $labels['spellcheckignorecaps'] = 'Ignore words with all letters capitalized'; |
| | | $labels['addtodict'] = 'Add to dictionary'; |
| | | |
| | | $labels['folder'] = 'Folder'; |
| | | $labels['folders'] = 'Folders'; |
| | |
| | | |
| | | // include GoogieSpell |
| | | if (!empty($CONFIG['enable_spellcheck'])) { |
| | | |
| | | $engine = $RCMAIL->config->get('spellcheck_engine','googie'); |
| | | $engine = $RCMAIL->config->get('spellcheck_engine','googie'); |
| | | $dictionary = (bool) $RCMAIL->config->get('spellcheck_dictionary'); |
| | | $spellcheck_langs = (array) $RCMAIL->config->get('spellcheck_languages', |
| | | array('da'=>'Dansk', 'de'=>'Deutsch', 'en' => 'English', 'es'=>'Español', |
| | | 'fr'=>'Français', 'it'=>'Italiano', 'nl'=>'Nederlands', 'pl'=>'Polski', |
| | |
| | | foreach ($spellcheck_langs as $key => $name) { |
| | | $editor_lang_set[] = ($key == $lang ? '+' : '') . JQ($name).'='.JQ($key); |
| | | } |
| | | |
| | | |
| | | $OUTPUT->include_script('googiespell.js'); |
| | | $OUTPUT->add_script(sprintf( |
| | | "var googie = new GoogieSpell('\$__skin_path/images/googiespell/','?_task=utils&_action=spell&lang=');\n". |
| | | "var googie = new GoogieSpell('\$__skin_path/images/googiespell/','?_task=utils&_action=spell&lang=', %s);\n". |
| | | "googie.lang_chck_spell = \"%s\";\n". |
| | | "googie.lang_rsm_edt = \"%s\";\n". |
| | | "googie.lang_close = \"%s\";\n". |
| | | "googie.lang_revert = \"%s\";\n". |
| | | "googie.lang_no_error_found = \"%s\";\n". |
| | | "googie.lang_learn_word = \"%s\";\n". |
| | | "googie.setLanguages(%s);\n". |
| | | "googie.setCurrentLanguage('%s');\n". |
| | | "googie.setSpellContainer('spellcheck-control');\n". |
| | | "googie.decorateTextarea('%s');\n". |
| | | "%s.set_env('spellcheck', googie);", |
| | | !empty($dictionary) ? 'true' : 'false', |
| | | JQ(Q(rcube_label('checkspelling'))), |
| | | JQ(Q(rcube_label('resumeediting'))), |
| | | JQ(Q(rcube_label('close'))), |
| | | JQ(Q(rcube_label('revertto'))), |
| | | JQ(Q(rcube_label('nospellerrors'))), |
| | | JQ(Q(rcube_label('addtodict'))), |
| | | json_serialize($spellcheck_langs), |
| | | $lang, |
| | | $attrib['id'], |
| | |
| | | case 'compose': |
| | | |
| | | $blocks = array( |
| | | 'main' => array('name' => Q(rcube_label('mainoptions'))), |
| | | 'sig' => array('name' => Q(rcube_label('signatureoptions'))), |
| | | 'main' => array('name' => Q(rcube_label('mainoptions'))), |
| | | 'spellcheck' => array('name' => Q(rcube_label('spellcheckoptions'))), |
| | | 'sig' => array('name' => Q(rcube_label('signatureoptions'))), |
| | | ); |
| | | |
| | | // Show checkbox for HTML Editor |
| | |
| | | $field_id = 'rcmfd_spellcheck_before_send'; |
| | | $input_spellcheck = new html_checkbox(array('name' => '_spellcheck_before_send', 'id' => $field_id, 'value' => 1)); |
| | | |
| | | $blocks['main']['options']['spellcheck_before_send'] = array( |
| | | $blocks['spellcheck']['options']['spellcheck_before_send'] = array( |
| | | 'title' => html::label($field_id, Q(rcube_label('spellcheckbeforesend'))), |
| | | 'content' => $input_spellcheck->show($config['spellcheck_before_send']?1:0), |
| | | ); |
| | | } |
| | | |
| | | if ($config['enable_spellcheck']) { |
| | | foreach (array('syms', 'nums', 'caps') as $key) { |
| | | $key = 'spellcheck_ignore_'.$key; |
| | | if (!isset($no_override[$key])) { |
| | | $input_spellcheck = new html_checkbox(array('name' => '_'.$key, 'id' => 'rcmfd_'.$key, 'value' => 1)); |
| | | |
| | | $blocks['spellcheck']['options'][$key] = array( |
| | | 'title' => html::label($field_id, Q(rcube_label(str_replace('_', '', $key)))), |
| | | 'content' => $input_spellcheck->show($config[$key]?1:0), |
| | | ); |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (!isset($no_override['show_sig'])) { |
| | | $field_id = 'rcmfd_show_sig'; |
| | | $select_show_sig = new html_select(array('name' => '_show_sig', 'id' => $field_id)); |
| | |
| | | 'dsn_default' => isset($_POST['_dsn_default']) ? TRUE : FALSE, |
| | | 'reply_same_folder' => isset($_POST['_reply_same_folder']) ? TRUE : FALSE, |
| | | 'spellcheck_before_send' => isset($_POST['_spellcheck_before_send']) ? TRUE : FALSE, |
| | | 'spellcheck_ignore_syms' => isset($_POST['_spellcheck_ignore_syms']) ? TRUE : FALSE, |
| | | 'spellcheck_ignore_nums' => isset($_POST['_spellcheck_ignore_nums']) ? TRUE : FALSE, |
| | | 'spellcheck_ignore_caps' => isset($_POST['_spellcheck_ignore_caps']) ? TRUE : FALSE, |
| | | 'show_sig' => isset($_POST['_show_sig']) ? intval($_POST['_show_sig']) : 1, |
| | | 'top_posting' => !empty($_POST['_top_posting']), |
| | | 'strip_existing_sig' => isset($_POST['_strip_existing_sig']), |
| | |
| | | $a_user_prefs['default_imap_folders'][] = $a_user_prefs[$p]; |
| | | } |
| | | } |
| | | |
| | | |
| | | break; |
| | | } |
| | | |
| | |
| | | $lang = get_input_value('lang', RCUBE_INPUT_GET); |
| | | $data = file_get_contents('php://input'); |
| | | |
| | | $learn_word = strpos($data, '<learnword>'); |
| | | |
| | | // Get data string |
| | | $left = strpos($data, '<text>'); |
| | | $right = strrpos($data, '</text>'); |
| | |
| | | $data = html_entity_decode($data, ENT_QUOTES, RCMAIL_CHARSET); |
| | | |
| | | $spellchecker = new rcube_spellchecker($lang); |
| | | $spellchecker->check($data); |
| | | $result = $spellchecker->get_xml(); |
| | | |
| | | if ($learn_word) { |
| | | $spellchecker->add_word($data); |
| | | $result = '<?xml version="1.0" encoding="'.RCMAIL_CHARSET.'"?><learnwordresult></learnwordresult>'; |
| | | } |
| | | else { |
| | | $spellchecker->check($data); |
| | | $result = $spellchecker->get_xml(); |
| | | } |
| | | |
| | | // set response length |
| | | header("Content-Length: " . strlen($result)); |
| | |
| | | else if ($request['method'] == 'getSuggestions') { |
| | | $result['result'] = $spellchecker->get_suggestions($data); |
| | | } |
| | | else if ($request['method'] == 'learnWord') { |
| | | $spellchecker->add_word($data); |
| | | $result['result'] = true; |
| | | } |
| | | |
| | | if ($error = $spellchecker->error()) { |
| | | echo '{"error":{"errstr":"' . addslashes($error) . '","errfile":"","errline":null,"errcontext":"","level":"FATAL"}}'; |