Changeset 66df084 in github
- Timestamp:
- Sep 6, 2011 9:39:45 AM (21 months ago)
- Branches:
- master, HEAD, courier-fix, dev-browser-capabilities, pdo, release-0.7, release-0.8
- Children:
- f8e48df
- Parents:
- eb2365c
- Files:
-
- 20 edited
-
CHANGELOG (modified) (1 diff)
-
SQL/mssql.initial.sql (modified) (2 diffs)
-
SQL/mssql.upgrade.sql (modified) (1 diff)
-
SQL/mysql.initial.sql (modified) (1 diff)
-
SQL/mysql.update.sql (modified) (1 diff)
-
SQL/postgres.initial.sql (modified) (1 diff)
-
SQL/postgres.update.sql (modified) (1 diff)
-
SQL/sqlite.initial.sql (modified) (1 diff)
-
SQL/sqlite.update.sql (modified) (1 diff)
-
config/main.inc.php.dist (modified) (2 diffs)
-
program/include/main.inc (modified) (2 diffs)
-
program/include/rcube_spellchecker.php (modified) (15 diffs)
-
program/js/editor.js (modified) (4 diffs)
-
program/js/googiespell.js (modified) (47 diffs)
-
program/localization/en_US/labels.inc (modified) (1 diff)
-
program/steps/mail/compose.inc (modified) (5 diffs)
-
program/steps/settings/func.inc (modified) (2 diffs)
-
program/steps/settings/save_prefs.inc (modified) (2 diffs)
-
program/steps/utils/spell.inc (modified) (2 diffs)
-
program/steps/utils/spell_html.inc (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
CHANGELOG
reb2365c r66df084 1 1 CHANGELOG Roundcube Webmail 2 2 =========================== 3 4 - Added spellchecker exceptions dictionary (shared or per-user) 5 - Added possibility to ignore words containing caps, numbers, symbols (spellcheck_ignore_* options) 3 6 4 7 RELEASE 0.6-rc -
SQL/mssql.initial.sql
r3a5476d1 r66df084 94 94 GO 95 95 96 CREATE TABLE [dbo].[dictionary] ( 97 [user_id] [int] , 98 [language] [varchar] (5) COLLATE Latin1_General_CI_AI NOT NULL , 99 [data] [text] COLLATE Latin1_General_CI_AI NOT NULL 100 ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] 101 GO 102 96 103 ALTER TABLE [dbo].[cache] WITH NOCHECK ADD 97 104 PRIMARY KEY CLUSTERED … … 265 272 GO 266 273 274 CREATE UNIQUE INDEX [IX_dictionary_user_language] ON [dbo].[dictionary]([user_id],[language]) ON [PRIMARY] 275 GO 276 267 277 ALTER TABLE [dbo].[identities] ADD CONSTRAINT [FK_identities_user_id] 268 278 FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id]) -
SQL/mssql.upgrade.sql
rfaf10e8 r66df084 111 111 DELETE FROM [dbo].[cache] 112 112 GO 113 114 -- Updates from version 0.6-stable 115 116 CREATE TABLE [dbo].[dictionary] ( 117 [user_id] [int] , 118 [language] [varchar] (5) COLLATE Latin1_General_CI_AI NOT NULL , 119 [data] [text] COLLATE Latin1_General_CI_AI NOT NULL 120 ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] 121 GO 122 CREATE UNIQUE INDEX [IX_dictionary_user_language] ON [dbo].[dictionary]([user_id],[language]) ON [PRIMARY] 123 GO -
SQL/mysql.initial.sql
r09b0e36 r66df084 145 145 146 146 147 -- Table structure for table `dictionary` 148 149 CREATE TABLE `dictionary` ( 150 `user_id` int(10) UNSIGNED DEFAULT NULL, 151 `language` varchar(5) NOT NULL, 152 `data` longtext NOT NULL, 153 CONSTRAINT `user_id_fk_dictionary` FOREIGN KEY (`user_id`) 154 REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE, 155 UNIQUE `uniqueness` (`user_id`, `language`) 156 ) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; 157 147 158 /*!40014 SET FOREIGN_KEY_CHECKS=1 */; -
SQL/mysql.update.sql
rfaf10e8 r66df084 145 145 TRUNCATE TABLE `messages`; 146 146 TRUNCATE TABLE `cache`; 147 148 -- Updates from version 0.6-stable 149 150 CREATE TABLE `dictionary` ( 151 `user_id` int(10) UNSIGNED DEFAULT NULL, 152 `language` varchar(5) NOT NULL, 153 `data` longtext NOT NULL, 154 CONSTRAINT `user_id_fk_dictionary` FOREIGN KEY (`user_id`) 155 REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE, 156 UNIQUE `uniqueness` (`user_id`, `language`) 157 ) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; -
SQL/postgres.initial.sql
r8381ec1 r66df084 226 226 CREATE INDEX messages_index_idx ON messages (user_id, cache_key, idx); 227 227 CREATE INDEX messages_created_idx ON messages (created); 228 229 -- 230 -- Table "dictionary" 231 -- Name: dictionary; Type: TABLE; Schema: public; Owner: postgres 232 -- 233 234 CREATE TABLE dictionary ( 235 user_id integer DEFAULT NULL 236 REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE, 237 "language" varchar(5) NOT NULL, 238 data text NOT NULL, 239 CONSTRAINT dictionary_user_id_language_key UNIQUE (user_id, "language") 240 ); -
SQL/postgres.update.sql
rfaf10e8 r66df084 101 101 TRUNCATE messages; 102 102 TRUNCATE cache; 103 104 -- Updates from version 0.6-stable 105 106 CREATE TABLE dictionary ( 107 user_id integer DEFAULT NULL 108 REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE, 109 "language" varchar(5) NOT NULL, 110 data text NOT NULL, 111 CONSTRAINT dictionary_user_id_language_key UNIQUE (user_id, "language") 112 ); -
SQL/sqlite.initial.sql
r3a5476d1 r66df084 147 147 CREATE INDEX ix_messages_index ON messages (user_id,cache_key,idx); 148 148 CREATE INDEX ix_messages_created ON messages (created); 149 150 -- -------------------------------------------------------- 151 152 -- 153 -- Table structure for table dictionary 154 -- 155 156 CREATE TABLE dictionary ( 157 user_id integer DEFAULT NULL, 158 "language" varchar(5) NOT NULL, 159 data text NOT NULL 160 ); 161 162 CREATE UNIQUE INDEX ix_dictionary_user_language ON dictionary (user_id, "language"); 163 -
SQL/sqlite.update.sql
rfaf10e8 r66df084 227 227 DELETE FROM cache; 228 228 CREATE INDEX ix_contactgroupmembers_contact_id ON contactgroupmembers (contact_id); 229 230 -- Updates from version 0.6-stable 231 232 CREATE TABLE dictionary ( 233 user_id integer DEFAULT NULL, 234 "language" varchar(5) NOT NULL, 235 data text NOT NULL 236 ); 237 238 CREATE UNIQUE INDEX ix_dictionary_user_language ON dictionary (user_id, "language"); -
config/main.inc.php.dist
r67eecde r66df084 428 428 $rcmail_config['enable_spellcheck'] = true; 429 429 430 // Enables spellchecker exceptions dictionary. 431 // Setting it to 'shared' will make the dictionary shared by all users. 432 $rcmail_config['spellcheck_dictionary'] = false; 433 430 434 // Set the spell checking engine. 'googie' is the default. 'pspell' is also available, 431 435 // but requires the Pspell extensions. When using Nox Spell Server, also set 'googie' here. … … 442 446 // Leave empty for default set of available language. 443 447 $rcmail_config['spellcheck_languages'] = NULL; 448 449 // Makes that words with all letters capitalized will be ignored (e.g. GOOGLE) 450 $rcmail_config['spellcheck_ignore_caps'] = false; 451 452 // Makes that words with numbers will be ignored (e.g. g00gle) 453 $rcmail_config['spellcheck_ignore_nums'] = false; 454 455 // Makes that words with symbols will be ignored (e.g. g@@gle) 456 $rcmail_config['spellcheck_ignore_syms'] = false; 444 457 445 458 // don't let users set pagesize to more than this value if set -
program/include/main.inc
r427e3a4 r66df084 1596 1596 1597 1597 if ($hook['abort']) 1598 return; 1598 return; 1599 1599 1600 1600 $lang = strtolower($_SESSION['language']); … … 1608 1608 $RCMAIL->output->include_script('tiny_mce/tiny_mce.js'); 1609 1609 $RCMAIL->output->include_script('editor.js'); 1610 $RCMAIL->output->add_script(sprintf("rcmail_editor_init('\$__skin_path', '%s', %d, '%s');", 1611 JQ($lang), intval($CONFIG['enable_spellcheck']), $mode), 1612 'foot'); 1610 $RCMAIL->output->add_script(sprintf("rcmail_editor_init(%s)", 1611 json_encode(array( 1612 'mode' => $mode, 1613 'skin_path' => '$__skin_path', 1614 'lang' => $lang, 1615 'spellcheck' => intval($CONFIG['enable_spellcheck']), 1616 'spelldict' => intval($CONFIG['spellcheck_dictionary']), 1617 ))), 'foot'); 1613 1618 } 1614 1619 -
program/include/rcube_spellchecker.php
r644e3ad9 r66df084 35 35 private $rc; 36 36 private $error; 37 private $separator = '/[ !"#$%&()*+\\,\/\n:;<=>?@\[\]^_{|}-]+|\.[^\w]/'; 38 37 private $separator = '/[\s\r\n\t\(\)\/\[\]{}<>\\"]+|[:;?!,\.]([^\w]|$)/'; 38 private $options = array(); 39 private $dict; 40 private $have_dict; 41 39 42 40 43 // default settings … … 51 54 function __construct($lang = 'en') 52 55 { 53 $this->rc = rcmail::get_instance();56 $this->rc = rcmail::get_instance(); 54 57 $this->engine = $this->rc->config->get('spellcheck_engine', 'googie'); 55 $this->lang = $lang ? $lang : 'en';58 $this->lang = $lang ? $lang : 'en'; 56 59 57 60 if ($this->engine == 'pspell' && !extension_loaded('pspell')) { … … 61 64 'message' => "Pspell extension not available"), true, true); 62 65 } 66 67 $this->options = array( 68 'ignore_syms' => $this->rc->config->get('spellcheck_ignore_syms'), 69 'ignore_nums' => $this->rc->config->get('spellcheck_ignore_nums'), 70 'ignore_caps' => $this->rc->config->get('spellcheck_ignore_caps'), 71 'dictionary' => $this->rc->config->get('spellcheck_dictionary'), 72 ); 63 73 } 64 74 … … 72 82 * @return bool True when no mispelling found, otherwise false 73 83 */ 74 function check($text, $is_html =false)84 function check($text, $is_html = false) 75 85 { 76 86 // convert to plain text … … 117 127 } 118 128 119 return $this->_googie_suggestions($word); 120 } 121 129 return $this->_googie_suggestions($word); 130 } 131 122 132 123 133 /** … … 180 190 } 181 191 182 return $ out;192 return $result; 183 193 } 184 194 … … 212 222 $text = preg_split($this->separator, $text, NULL, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_OFFSET_CAPTURE); 213 223 214 $diff = 0;215 $matches = array();224 $diff = 0; 225 $matches = array(); 216 226 217 227 foreach ($text as $w) { … … 220 230 $len = mb_strlen($word); 221 231 222 if ($word && preg_match('/[^0-9\.]/', $word) && !pspell_check($this->plink, $word)) { 232 // skip exceptions 233 if ($this->is_exception($word)) { 234 } 235 else if (!pspell_check($this->plink, $word)) { 223 236 $suggestions = pspell_suggest($this->plink, $word); 224 237 … … 241 254 private function _pspell_words($text = null, $is_html=false) 242 255 { 256 $result = array(); 257 243 258 if ($text) { 244 259 // init spellchecker … … 258 273 foreach ($text as $w) { 259 274 $word = trim($w[0]); 260 if ($word && preg_match('/[^0-9\.]/', $word) && !pspell_check($this->plink, $word)) { 275 276 // skip exceptions 277 if ($this->is_exception($word)) { 278 continue; 279 } 280 281 if (!pspell_check($this->plink, $word)) { 261 282 $result[] = $word; 262 283 } … … 265 286 return $result; 266 287 } 267 268 $result = array();269 288 270 289 foreach ($this->matches as $m) { … … 331 350 332 351 // Google has some problem with spaces, use \n instead 333 $ text = str_replace(' ', "\n", $text);334 335 $ text = '<?xml version="1.0" encoding="utf-8" ?>'352 $gtext = str_replace(' ', "\n", $text); 353 354 $gtext = '<?xml version="1.0" encoding="utf-8" ?>' 336 355 .'<spellrequest textalreadyclipped="0" ignoredups="0" ignoredigits="1" ignoreallcaps="1">' 337 .'<text>' . $ text . '</text>'356 .'<text>' . $gtext . '</text>' 338 357 .'</spellrequest>'; 339 358 … … 342 361 $out = "POST $path HTTP/1.0\r\n"; 343 362 $out .= "Host: " . str_replace('ssl://', '', $host) . "\r\n"; 344 $out .= "Content-Length: " . strlen($ text) . "\r\n";363 $out .= "Content-Length: " . strlen($gtext) . "\r\n"; 345 364 $out .= "Content-Type: application/x-www-form-urlencoded\r\n"; 346 365 $out .= "Connection: Close\r\n\r\n"; 347 $out .= $ text;366 $out .= $gtext; 348 367 fwrite($fp, $out); 349 368 … … 358 377 359 378 preg_match_all('/<c o="([^"]*)" l="([^"]*)" s="([^"]*)">([^<]*)<\/c>/', $store, $matches, PREG_SET_ORDER); 379 380 // skip exceptions (if appropriate options are enabled) 381 if (!empty($this->options['ignore_syms']) || !empty($this->options['ignore_nums']) 382 || !empty($this->options['ignore_caps']) || !empty($this->options['dictionary']) 383 ) { 384 foreach ($matches as $idx => $m) { 385 $word = mb_substr($text, $m[1], $m[2], RCMAIL_CHARSET); 386 // skip exceptions 387 if ($this->is_exception($word)) { 388 unset($matches[$idx]); 389 } 390 } 391 } 360 392 361 393 return $matches; … … 414 446 return $h2t->get_text(); 415 447 } 448 449 450 /** 451 * Check if the specified word is an exception accoring to 452 * spellcheck options. 453 * 454 * @param string $word The word 455 * 456 * @return bool True if the word is an exception, False otherwise 457 */ 458 public function is_exception($word) 459 { 460 // Contain only symbols (e.g. "+9,0", "2:2") 461 if (!$word || preg_match('/^[0-9@#$%^&_+~*=:;?!,.-]+$/', $word)) 462 return true; 463 464 // Contain symbols (e.g. "g@@gle"), all symbols excluding separators 465 if (!empty($this->options['ignore_syms']) && preg_match('/[@#$%^&_+~*=-]/', $word)) 466 return true; 467 468 // Contain numbers (e.g. "g00g13") 469 if (!empty($this->options['ignore_nums']) && preg_match('/[0-9]/', $word)) 470 return true; 471 472 // Blocked caps (e.g. "GOOGLE") 473 if (!empty($this->options['ignore_caps']) && $word == mb_strtoupper($word)) 474 return true; 475 476 // Use exceptions from dictionary 477 if (!empty($this->options['dictionary'])) { 478 $this->load_dict(); 479 480 // @TODO: should dictionary be case-insensitive? 481 if (!empty($this->dict) && in_array($word, $this->dict)) 482 return true; 483 } 484 485 return false; 486 } 487 488 489 /** 490 * Add a word to dictionary 491 * 492 * @param string $word The word to add 493 */ 494 public function add_word($word) 495 { 496 $this->load_dict(); 497 498 foreach (explode(' ', $word) as $word) { 499 // sanity check 500 if (strlen($word) < 512) { 501 $this->dict[] = $word; 502 $valid = true; 503 } 504 } 505 506 if ($valid) { 507 $this->dict = array_unique($this->dict); 508 $this->update_dict(); 509 } 510 } 511 512 513 /** 514 * Remove a word from dictionary 515 * 516 * @param string $word The word to remove 517 */ 518 public function remove_word($word) 519 { 520 $this->load_dict(); 521 522 if (($key = array_search($word, $this->dict)) !== false) { 523 unset($this->dict[$key]); 524 $this->update_dict(); 525 } 526 } 527 528 529 /** 530 * Update dictionary row in DB 531 */ 532 private function update_dict() 533 { 534 if (strcasecmp($this->options['dictionary'], 'shared') != 0) { 535 $userid = (int) $this->rc->user->ID; 536 } 537 538 $plugin = $this->rc->plugins->exec_hook('spell_dictionary_save', array( 539 'userid' => $userid, 'language' => $this->lang, 'dictionary' => $this->dict)); 540 541 if (!empty($plugin['abort'])) { 542 return; 543 } 544 545 if ($this->have_dict) { 546 if (!empty($this->dict)) { 547 $this->rc->db->query( 548 "UPDATE ".get_table_name('dictionary') 549 ." SET data = ?" 550 ." WHERE user_id " . ($plugin['userid'] ? "= ".$plugin['userid'] : "IS NULL") 551 ." AND " . $this->rc->db->quoteIdentifier('language') . " = ?", 552 implode(' ', $plugin['dictionary']), $plugin['language']); 553 } 554 // don't store empty dict 555 else { 556 $this->rc->db->query( 557 "DELETE FROM " . get_table_name('dictionary') 558 ." WHERE user_id " . ($plugin['userid'] ? "= ".$plugin['userid'] : "IS NULL") 559 ." AND " . $this->rc->db->quoteIdentifier('language') . " = ?", 560 $plugin['language']); 561 } 562 } 563 else if (!empty($this->dict)) { 564 $this->rc->db->query( 565 "INSERT INTO " .get_table_name('dictionary') 566 ." (user_id, " . $this->rc->db->quoteIdentifier('language') . ", data) VALUES (?, ?, ?)", 567 $plugin['userid'], $plugin['language'], implode(' ', $plugin['dictionary'])); 568 } 569 } 570 571 572 /** 573 * Get dictionary from DB 574 */ 575 private function load_dict() 576 { 577 if (is_array($this->dict)) { 578 return $this->dict; 579 } 580 581 if (strcasecmp($this->options['dictionary'], 'shared') != 0) { 582 $userid = (int) $this->rc->user->ID; 583 } 584 585 $plugin = $this->rc->plugins->exec_hook('spell_dictionary_get', array( 586 'userid' => $userid, 'language' => $this->lang, 'dictionary' => array())); 587 588 if (empty($plugin['abort'])) { 589 $dict = array(); 590 $this->rc->db->query( 591 "SELECT data FROM ".get_table_name('dictionary') 592 ." WHERE user_id ". ($plugin['userid'] ? "= ".$plugin['userid'] : "IS NULL") 593 ." AND " . $this->rc->db->quoteIdentifier('language') . " = ?", 594 $plugin['language']); 595 596 if ($sql_arr = $this->rc->db->fetch_assoc($sql_result)) { 597 $this->have_dict = true; 598 if (!empty($sql_arr['data'])) { 599 $dict = explode(' ', $sql_arr['data']); 600 } 601 } 602 603 $plugin['dictionary'] = array_merge((array)$plugin['dictionary'], $dict); 604 } 605 606 if (!empty($plugin['dictionary']) && is_array($plugin['dictionary'])) { 607 $this->dict = $plugin['dictionary']; 608 } 609 else { 610 $this->dict = array(); 611 } 612 613 return $this->dict; 614 } 615 416 616 } -
program/js/editor.js
rb4edf78 r66df084 15 15 16 16 // Initialize HTML editor 17 function rcmail_editor_init( skin_path, editor_lang, spellcheck, mode)17 function rcmail_editor_init(config) 18 18 { 19 19 var ret, conf = { … … 22 22 apply_source_formatting: true, 23 23 theme: 'advanced', 24 language: editor_lang,25 content_css: skin_path + '/editor_content.css',24 language: config.lang, 25 content_css: config.skin_path + '/editor_content.css', 26 26 theme_advanced_toolbar_location: 'top', 27 27 theme_advanced_toolbar_align: 'left', … … 36 36 }; 37 37 38 if ( mode == 'identity')38 if (config.mode == 'identity') 39 39 $.extend(conf, { 40 40 plugins: 'paste,tabfocus', … … 44 44 else // mail compose 45 45 $.extend(conf, { 46 plugins: 'paste,emotions,media,nonbreaking,table,searchreplace,visualchars,directionality,tabfocus' + ( spellcheck ? ',spellchecker' : ''),46 plugins: 'paste,emotions,media,nonbreaking,table,searchreplace,visualchars,directionality,tabfocus' + (config.spellcheck ? ',spellchecker' : ''), 47 47 theme_advanced_buttons1: 'bold,italic,underline,|,justifyleft,justifycenter,justifyright,justifyfull,|,bullist,numlist,outdent,indent,ltr,rtl,blockquote,|,forecolor,backcolor,fontselect,fontsizeselect', 48 theme_advanced_buttons2: 'link,unlink,table,|,emotions,charmap,image,media,|,code,search' + ( spellcheck ? ',spellchecker' : '') + ',undo,redo',48 theme_advanced_buttons2: 'link,unlink,table,|,emotions,charmap,image,media,|,code,search' + (config.spellcheck ? ',spellchecker' : '') + ',undo,redo', 49 49 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'), 50 50 spellchecker_rpc_url: '?_task=utils&_action=spell_html', 51 spellchecker_enable_learn_rpc: config.spelldict, 51 52 accessibility_focus: false, 52 53 oninit: 'rcmail_editor_callback' -
program/js/googiespell.js
r340546c r66df084 2 2 SpellCheck 3 3 jQuery'fied spell checker based on GoogieSpell 4.0 4 Copyright Amir Salihefendic 2006 5 Copyright Aleksander Machniak 2009 4 Copyright (C) 2006 Amir Salihefendic 5 Copyright (C) 2009 Aleksander Machniak 6 Copyright (C) 2011 Kolab Systems AG 6 7 LICENSE 7 8 GPL … … 14 15 GOOGIE_DEFAULT_LANG = 'en'; 15 16 16 function GoogieSpell(img_dir, server_url) { 17 function GoogieSpell(img_dir, server_url, has_dict) 18 { 17 19 var ref = this, 18 20 cookie_value = getCookie('language'); … … 50 52 this.lang_no_error_found = "No spelling errors found"; 51 53 this.lang_no_suggestions = "No suggestions"; 54 this.lang_learn_word = "Add to dictionary"; 52 55 53 56 this.show_spell_img = false; // roundcube mod. … … 65 68 this.custom_spellcheck_starter = null; 66 69 this.main_controller = true; 70 this.has_dictionary = has_dict; 67 71 68 72 // Observers … … 91 95 92 96 93 this.decorateTextarea = function(id) { 97 this.decorateTextarea = function(id) 98 { 94 99 this.text_area = typeof id === 'string' ? document.getElementById(id) : id; 95 100 … … 120 125 // API Functions (the ones that you can call) 121 126 ///// 122 this.setSpellContainer = function(id) { 127 this.setSpellContainer = function(id) 128 { 123 129 this.spell_container = typeof id === 'string' ? document.getElementById(id) : id; 124 130 }; 125 131 126 this.setLanguages = function(lang_dict) { 132 this.setLanguages = function(lang_dict) 133 { 127 134 this.lang_to_word = lang_dict; 128 135 this.langlist_codes = this.array_keys(lang_dict); 129 136 }; 130 137 131 this.setCurrentLanguage = function(lan_code) { 138 this.setCurrentLanguage = function(lan_code) 139 { 132 140 GOOGIE_CUR_LANG = lan_code; 133 141 … … 138 146 }; 139 147 140 this.setForceWidthHeight = function(width, height) { 148 this.setForceWidthHeight = function(width, height) 149 { 141 150 // Set to null if you want to use one of them 142 151 this.force_width = width; … … 144 153 }; 145 154 146 this.setDecoration = function(bool) { 155 this.setDecoration = function(bool) 156 { 147 157 this.decoration = bool; 148 158 }; 149 159 150 this.dontUseCloseButtons = function() { 160 this.dontUseCloseButtons = function() 161 { 151 162 this.use_close_btn = false; 152 163 }; 153 164 154 this.appendNewMenuItem = function(name, call_back_fn, checker) { 165 this.appendNewMenuItem = function(name, call_back_fn, checker) 166 { 155 167 this.extra_menu_items.push([name, call_back_fn, checker]); 156 168 }; 157 169 158 this.appendCustomMenuBuilder = function(eval, builder) { 170 this.appendCustomMenuBuilder = function(eval, builder) 171 { 159 172 this.custom_menu_builder.push([eval, builder]); 160 173 }; 161 174 162 this.setFocus = function() { 175 this.setFocus = function() 176 { 163 177 try { 164 178 this.focus_link_b.focus(); … … 175 189 // Set functions (internal) 176 190 ///// 177 this.setStateChanged = function(current_state) { 191 this.setStateChanged = function(current_state) 192 { 178 193 this.state = current_state; 179 194 if (this.spelling_state_observer != null && this.report_state_change) … … 181 196 }; 182 197 183 this.setReportStateChange = function(bool) { 198 this.setReportStateChange = function(bool) 199 { 184 200 this.report_state_change = bool; 185 201 }; … … 189 205 // Request functions 190 206 ///// 191 this.getUrl = function() { 207 this.getUrl = function() 208 { 192 209 return this.server_url + GOOGIE_CUR_LANG; 193 210 }; 194 211 195 this.escapeSpecial = function(val) { 212 this.escapeSpecial = function(val) 213 { 196 214 return val.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">"); 197 215 }; 198 216 199 this.createXMLReq = function (text) { 217 this.createXMLReq = function (text) 218 { 200 219 return '<?xml version="1.0" encoding="utf-8" ?>' 201 220 + '<spellrequest textalreadyclipped="0" ignoredups="0" ignoredigits="1" ignoreallcaps="1">' … … 203 222 }; 204 223 205 this.spellCheck = function(ignore) { 224 this.spellCheck = function(ignore) 225 { 206 226 this.prepare(ignore); 207 227 … … 209 229 ref = this; 210 230 211 $.ajax({ type: 'POST', url: this.getUrl(), 212 data: this.createXMLReq(req_text), dataType: 'text', 231 $.ajax({ type: 'POST', url: this.getUrl(), data: this.createXMLReq(req_text), dataType: 'text', 213 232 error: function(o) { 214 233 if (ref.custom_ajax_error) … … 235 254 }; 236 255 256 this.learnWord = function(word, id) 257 { 258 word = this.escapeSpecial(word.innerHTML); 259 260 var ref = this, 261 req_text = '<?xml version="1.0" encoding="utf-8" ?><learnword><text>' + word + '</text></learnword>'; 262 263 $.ajax({ type: 'POST', url: this.getUrl(), data: req_text, dataType: 'text', 264 error: function(o) { 265 if (ref.custom_ajax_error) 266 ref.custom_ajax_error(ref); 267 else 268 alert('An error was encountered on the server. Please try again later.'); 269 }, 270 success: function(data) { 271 } 272 }); 273 }; 274 237 275 238 276 ////// … … 275 313 }; 276 314 277 this.parseResult = function(r_text) { 315 this.parseResult = function(r_text) 316 { 278 317 // Returns an array: result[item] -> ['attrs'], ['suggestions'] 279 318 var re_split_attr_c = /\w+="(\d+|true)"/g, … … 325 364 // Error menu functions 326 365 ///// 327 this.createErrorWindow = function() { 366 this.createErrorWindow = function() 367 { 328 368 this.error_window = document.createElement('div'); 329 369 $(this.error_window).addClass('googie_window popupmenu').attr('googie_action_btn', '1'); 330 370 }; 331 371 332 this.isErrorWindowShown = function() { 372 this.isErrorWindowShown = function() 373 { 333 374 return $(this.error_window).is(':visible'); 334 375 }; 335 376 336 this.hideErrorWindow = function() { 377 this.hideErrorWindow = function() 378 { 337 379 $(this.error_window).hide(); 338 380 $(this.error_window_iframe).hide(); 339 381 }; 340 382 341 this.updateOrginalText = function(offset, old_value, new_value, id) { 383 this.updateOrginalText = function(offset, old_value, new_value, id) 384 { 342 385 var part_1 = this.orginal_text.substring(0, offset), 343 386 part_2 = this.orginal_text.substring(offset+old_value.length), … … 358 401 }; 359 402 360 this.createListSeparator = function() { 403 this.createListSeparator = function() 404 { 361 405 var td = document.createElement('td'), 362 406 tr = document.createElement('tr'); 363 407 364 408 $(td).html(' ').attr('googie_action_btn', '1') 365 .css({'cursor': 'default', 'font-size': '3px', 'border-top': '1px solid #ccc', 'padding-top': '3px'});409 .css({'cursor': 'default', 'font-size': '3px', 'border-top': '1px solid #ccc', 'padding-top': '3px'}); 366 410 tr.appendChild(td); 367 411 … … 369 413 }; 370 414 371 this.correctError = function(id, elm, l_elm, rm_pre_space) { 415 this.correctError = function(id, elm, l_elm, rm_pre_space) 416 { 372 417 var old_value = elm.innerHTML, 373 418 new_value = l_elm.nodeType == 3 ? l_elm.nodeValue : l_elm.innerHTML, … … 394 439 }; 395 440 396 this.showErrorWindow = function(elm, id) { 441 this.ignoreError = function(elm, id) 442 { 443 // @TODO: ignore all same words 444 $(elm).removeAttr('class').css('color', '').unbind(); 445 this.hideErrorWindow(); 446 }; 447 448 this.showErrorWindow = function(elm, id) 449 { 397 450 if (this.show_menu_observer) 398 451 this.show_menu_observer(this); … … 415 468 } 416 469 } 470 417 471 if (!changed) { 418 472 // Build up the result list … … 422 476 row, item, dummy; 423 477 478 // [Add to dictionary] button 479 if (this.has_dictionary && !$(elm).attr('is_corrected')) { 480 row = document.createElement('tr'), 481 item = document.createElement('td'), 482 dummy = document.createElement('span'); 483 484 $(dummy).text(this.lang_learn_word); 485 $(item).attr('googie_action_btn', '1').css('cursor', 'default') 486 .mouseover(ref.item_onmouseover) 487 .mouseout(ref.item_onmouseout) 488 .click(function(e) { 489 ref.learnWord(elm, id); 490 ref.ignoreError(elm, id); 491 }); 492 493 item.appendChild(dummy); 494 row.appendChild(item); 495 list.appendChild(row); 496 } 497 /* 424 498 if (suggestions.length == 0) { 425 499 row = document.createElement('tr'), … … 434 508 list.appendChild(row); 435 509 } 436 510 */ 437 511 for (var i=0, len=suggestions.length; i < len; i++) { 438 512 row = document.createElement('tr'), … … 442 516 $(dummy).html(suggestions[i]); 443 517 444 $(item).bind('mouseover', this.item_onmouseover) 445 .bind('mouseout', this.item_onmouseout) 446 .bind('click', function(e) { ref.correctError(id, elm, e.target.firstChild) }); 518 $(item).mouseover(this.item_onmouseover).mouseout(this.item_onmouseout) 519 .click(function(e) { ref.correctError(id, elm, e.target.firstChild) }); 447 520 448 521 item.appendChild(dummy); … … 451 524 } 452 525 453 // The element is changed, append the revert526 // The element is changed, append the revert 454 527 if (elm.is_changed && elm.innerHTML != elm.old_value) { 455 528 var old_value = elm.old_value, … … 460 533 $(rev_span).addClass('googie_list_revert').html(this.lang_revert + ' ' + old_value); 461 534 462 $(revert).bind('mouseover', this.item_onmouseover) 463 .bind('mouseout', this.item_onmouseout) 464 .bind('click', function(e) { 535 $(revert).mouseover(this.item_onmouseover).mouseout(this.item_onmouseout) 536 .click(function(e) { 465 537 ref.updateOrginalText(offset, elm.innerHTML, old_value, id); 466 $(elm). attr('is_corrected', true).css('color', '#b91414').html(old_value);538 $(elm).removeAttr('is_corrected').css('color', '#b91414').html(old_value); 467 539 ref.hideErrorWindow(); 468 540 }); … … 499 571 .width(32).height(16) 500 572 .css({'cursor': 'pointer', 'margin-left': '2px', 'margin-right': '2px'}) 501 . bind('click',onsub);573 .click(onsub); 502 574 503 575 $(edit_form).attr('googie_action_btn', '1') 504 576 .css({'margin': 0, 'padding': 0, 'cursor': 'default', 'white-space': 'nowrap'}) 505 . bind('submit',onsub);577 .submit(onsub); 506 578 507 579 edit_form.appendChild(edit_input); … … 524 596 525 597 $(e_col).html(e_elm[0]) 526 . bind('mouseover',ref.item_onmouseover)527 . bind('mouseout',ref.item_onmouseout)528 . bind('click',function() { return e_elm[1](elm, ref) });598 .mouseover(ref.item_onmouseover) 599 .mouseout(ref.item_onmouseout) 600 .click(function() { return e_elm[1](elm, ref) }); 529 601 530 602 e_row.appendChild(e_col); … … 576 648 // Edit layer (the layer where the suggestions are stored) 577 649 ////// 578 this.createEditLayer = function(width, height) { 650 this.createEditLayer = function(width, height) 651 { 579 652 this.edit_layer = document.createElement('div'); 580 653 $(this.edit_layer).addClass('googie_edit_layer').attr('id', 'googie_edit_layer') … … 604 677 }; 605 678 606 this.resumeEditing = function() { 679 this.resumeEditing = function() 680 { 607 681 this.setStateChanged('ready'); 608 682 … … 630 704 }; 631 705 632 this.createErrorLink = function(text, id) { 706 this.createErrorLink = function(text, id) 707 { 633 708 var elm = document.createElement('span'), 634 709 ref = this, … … 639 714 }; 640 715 641 $(elm).html(text).addClass('googie_link'). bind('click', d)642 .attr({'googie_action_btn' : '1', 'g_id' : id , 'is_corrected' : false});716 $(elm).html(text).addClass('googie_link').click(d).removeAttr('is_corrected') 717 .attr({'googie_action_btn' : '1', 'g_id' : id}); 643 718 644 719 return elm; 645 720 }; 646 721 647 this.createPart = function(txt_part) { 722 this.createPart = function(txt_part) 723 { 648 724 if (txt_part == " ") 649 725 return document.createTextNode(" "); … … 660 736 }; 661 737 662 this.showErrorsInIframe = function() { 738 this.showErrorsInIframe = function() 739 { 663 740 var output = document.createElement('div'), 664 741 pointer = 0, … … 718 795 // Choose language menu 719 796 ////// 720 this.createLangWindow = function() { 797 this.createLangWindow = function() 798 { 721 799 this.language_window = document.createElement('div'); 722 800 $(this.language_window).addClass('googie_window popupmenu') … … 777 855 }; 778 856 779 this.isLangWindowShown = function() { 857 this.isLangWindowShown = function() 858 { 780 859 return $(this.language_window).is(':visible'); 781 860 }; 782 861 783 this.hideLangWindow = function() { 862 this.hideLangWindow = function() 863 { 784 864 $(this.language_window).hide(); 785 865 $(this.switch_lan_pic).removeClass().addClass('googie_lang_3d_on'); 786 866 }; 787 867 788 this.showLangWindow = function(elm) { 868 this.showLangWindow = function(elm) 869 { 789 870 if (this.show_menu_observer) 790 871 this.show_menu_observer(this); … … 807 888 }; 808 889 809 this.deHighlightCurSel = function() { 890 this.deHighlightCurSel = function() 891 { 810 892 $(this.lang_cur_elm).removeClass().addClass('googie_list_onout'); 811 893 }; 812 894 813 this.highlightCurSel = function() { 895 this.highlightCurSel = function() 896 { 814 897 if (GOOGIE_CUR_LANG == null) 815 898 GOOGIE_CUR_LANG = GOOGIE_DEFAULT_LANG; … … 825 908 }; 826 909 827 this.createChangeLangPic = function() { 910 this.createChangeLangPic = function() 911 { 828 912 var img = $('<img>') 829 913 .attr({src: this.img_dir + 'change_lang.gif', 'alt': 'Change language', 'googie_action_btn': '1'}), … … 848 932 }; 849 933 850 this.createSpellDiv = function() { 934 this.createSpellDiv = function() 935 { 851 936 var span = document.createElement('span'); 852 937 … … 863 948 // State functions 864 949 ///// 865 this.flashNoSpellingErrorState = function(on_finish) { 950 this.flashNoSpellingErrorState = function(on_finish) 951 { 866 952 this.setStateChanged('no_error_found'); 867 953 … … 889 975 }; 890 976 891 this.resumeEditingState = function() { 977 this.resumeEditingState = function() 978 { 892 979 this.setStateChanged('resume_editing'); 893 980 … … 907 994 }; 908 995 909 this.checkSpellingState = function(fire) { 996 this.checkSpellingState = function(fire) 997 { 910 998 if (fire) 911 999 this.setStateChanged('ready'); … … 940 1028 // Misc. functions 941 1029 ///// 942 this.isDefined = function(o) { 1030 this.isDefined = function(o) 1031 { 943 1032 return (o !== undefined && o !== null) 944 1033 }; 945 1034 946 this.errorFixed = function() { 947 this.cnt_errors_fixed++; 1035 this.errorFixed = function() 1036 { 1037 this.cnt_errors_fixed++; 948 1038 if (this.all_errors_fixed_observer) 949 1039 if (this.cnt_errors_fixed == this.cnt_errors) { … … 953 1043 }; 954 1044 955 this.errorFound = function() { 1045 this.errorFound = function() 1046 { 956 1047 this.cnt_errors++; 957 1048 }; 958 1049 959 this.createCloseButton = function(c_fn) { 1050 this.createCloseButton = function(c_fn) 1051 { 960 1052 return this.createButton(this.lang_close, 'googie_list_close', c_fn); 961 1053 }; 962 1054 963 this.createButton = function(name, css_class, c_fn) { 1055 this.createButton = function(name, css_class, c_fn) 1056 { 964 1057 var btn_row = document.createElement('tr'), 965 1058 btn = document.createElement('td'), … … 983 1076 }; 984 1077 985 this.removeIndicator = function(elm) { 1078 this.removeIndicator = function(elm) 1079 { 986 1080 //$(this.indicator).remove(); 987 1081 // roundcube mod. … … 990 1084 }; 991 1085 992 this.appendIndicator = function(elm) { 1086 this.appendIndicator = function(elm) 1087 { 993 1088 // modified by roundcube 994 1089 if (window.rcmail) … … 1006 1101 } 1007 1102 1008 this.createFocusLink = function(name) { 1103 this.createFocusLink = function(name) 1104 { 1009 1105 var link = document.createElement('a'); 1010 1106 $(link).attr({'href': 'javascript:;', 'name': name}); … … 1012 1108 }; 1013 1109 1014 this.item_onmouseover = function(e) { 1110 this.item_onmouseover = function(e) 1111 { 1015 1112 if (this.className != 'googie_list_revert' && this.className != 'googie_list_close') 1016 1113 this.className = 'googie_list_onhover'; … … 1018 1115 this.parentNode.className = 'googie_list_onhover'; 1019 1116 }; 1020 this.item_onmouseout = function(e) { 1117 1118 this.item_onmouseout = function(e) 1119 { 1021 1120 if (this.className != 'googie_list_revert' && this.className != 'googie_list_close') 1022 1121 this.className = 'googie_list_onout'; -
program/localization/en_US/labels.inc
rdbb0c2a r66df084 428 428 $labels['defaultaddressbook'] = 'Add new contacts to the selected addressbook'; 429 429 $labels['spellcheckbeforesend'] = 'Check spelling before sending a message'; 430 $labels['spellcheckoptions'] = 'Spellcheck Options'; 431 $labels['spellcheckignoresyms'] = 'Ignore words with symbols'; 432 $labels['spellcheckignorenums'] = 'Ignore words with numbers'; 433 $labels['spellcheckignorecaps'] = 'Ignore words with all letters capitalized'; 434 $labels['addtodict'] = 'Add to dictionary'; 430 435 431 436 $labels['folder'] = 'Folder'; -
program/steps/mail/compose.inc
r13d45df r66df084 698 698 // include GoogieSpell 699 699 if (!empty($CONFIG['enable_spellcheck'])) { 700 701 $ engine = $RCMAIL->config->get('spellcheck_engine','googie');700 $engine = $RCMAIL->config->get('spellcheck_engine','googie'); 701 $dictionary = (bool) $RCMAIL->config->get('spellcheck_dictionary'); 702 702 $spellcheck_langs = (array) $RCMAIL->config->get('spellcheck_languages', 703 703 array('da'=>'Dansk', 'de'=>'Deutsch', 'en' => 'English', 'es'=>'Español', … … 729 729 $editor_lang_set[] = ($key == $lang ? '+' : '') . JQ($name).'='.JQ($key); 730 730 } 731 731 732 732 $OUTPUT->include_script('googiespell.js'); 733 733 $OUTPUT->add_script(sprintf( 734 "var googie = new GoogieSpell('\$__skin_path/images/googiespell/','?_task=utils&_action=spell&lang=' );\n".734 "var googie = new GoogieSpell('\$__skin_path/images/googiespell/','?_task=utils&_action=spell&lang=', %s);\n". 735 735 "googie.lang_chck_spell = \"%s\";\n". 736 736 "googie.lang_rsm_edt = \"%s\";\n". … … 738 738 "googie.lang_revert = \"%s\";\n". 739 739 "googie.lang_no_error_found = \"%s\";\n". 740 "googie.lang_learn_word = \"%s\";\n". 740 741 "googie.setLanguages(%s);\n". 741 742 "googie.setCurrentLanguage('%s');\n". … … 743 744 "googie.decorateTextarea('%s');\n". 744 745 "%s.set_env('spellcheck', googie);", 746 !empty($dictionary) ? 'true' : 'false', 745 747 JQ(Q(rcube_label('checkspelling'))), 746 748 JQ(Q(rcube_label('resumeediting'))), … … 748 750 JQ(Q(rcube_label('revertto'))), 749 751 JQ(Q(rcube_label('nospellerrors'))), 752 JQ(Q(rcube_label('addtodict'))), 750 753 json_serialize($spellcheck_langs), 751 754 $lang, -
program/steps/settings/func.inc
r25e6a0f r66df084 449 449 450 450 $blocks = array( 451 'main' => array('name' => Q(rcube_label('mainoptions'))), 452 'sig' => array('name' => Q(rcube_label('signatureoptions'))), 451 'main' => array('name' => Q(rcube_label('mainoptions'))), 452 'spellcheck' => array('name' => Q(rcube_label('spellcheckoptions'))), 453 'sig' => array('name' => Q(rcube_label('signatureoptions'))), 453 454 ); 454 455 … … 550 551 $input_spellcheck = new html_checkbox(array('name' => '_spellcheck_before_send', 'id' => $field_id, 'value' => 1)); 551 552 552 $blocks[' main']['options']['spellcheck_before_send'] = array(553 $blocks['spellcheck']['options']['spellcheck_before_send'] = array( 553 554 'title' => html::label($field_id, Q(rcube_label('spellcheckbeforesend'))), 554 555 'content' => $input_spellcheck->show($config['spellcheck_before_send']?1:0), 555 556 ); 557 } 558 559 if ($config['enable_spellcheck']) { 560 foreach (array('syms', 'nums', 'caps') as $key) { 561 $key = 'spellcheck_ignore_'.$key; 562 if (!isset($no_override[$key])) { 563 $input_spellcheck = new html_checkbox(array('name' => '_'.$key, 'id' => 'rcmfd_'.$key, 'value' => 1)); 564 565 $blocks['spellcheck']['options'][$key] = array( 566 'title' => html::label($field_id, Q(rcube_label(str_replace('_', '', $key)))), 567 'content' => $input_spellcheck->show($config[$key]?1:0), 568 ); 569 } 570 } 556 571 } 557 572 -
program/steps/settings/save_prefs.inc
rda71783 r66df084 72 72 'reply_same_folder' => isset($_POST['_reply_same_folder']) ? TRUE : FALSE, 73 73 'spellcheck_before_send' => isset($_POST['_spellcheck_before_send']) ? TRUE : FALSE, 74 'spellcheck_ignore_syms' => isset($_POST['_spellcheck_ignore_syms']) ? TRUE : FALSE, 75 'spellcheck_ignore_nums' => isset($_POST['_spellcheck_ignore_nums']) ? TRUE : FALSE, 76 'spellcheck_ignore_caps' => isset($_POST['_spellcheck_ignore_caps']) ? TRUE : FALSE, 74 77 'show_sig' => isset($_POST['_show_sig']) ? intval($_POST['_show_sig']) : 1, 75 78 'top_posting' => !empty($_POST['_top_posting']), … … 168 171 } 169 172 } 170 173 171 174 break; 172 175 } -
program/steps/utils/spell.inc
rb4edf78 r66df084 24 24 $data = file_get_contents('php://input'); 25 25 26 $learn_word = strpos($data, '<learnword>'); 27 26 28 // Get data string 27 29 $left = strpos($data, '<text>'); … … 31 33 32 34 $spellchecker = new rcube_spellchecker($lang); 33 $spellchecker->check($data); 34 $result = $spellchecker->get_xml(); 35 36 if ($learn_word) { 37 $spellchecker->add_word($data); 38 $result = '<?xml version="1.0" encoding="'.RCMAIL_CHARSET.'"?><learnwordresult></learnwordresult>'; 39 } 40 else { 41 $spellchecker->check($data); 42 $result = $spellchecker->get_xml(); 43 } 35 44 36 45 // set response length -
program/steps/utils/spell_html.inc
rb4edf78 r66df084 41 41 $result['result'] = $spellchecker->get_suggestions($data); 42 42 } 43 else if ($request['method'] == 'learnWord') { 44 $spellchecker->add_word($data); 45 $result['result'] = true; 46 } 43 47 44 48 if ($error = $spellchecker->error()) {
Note: See TracChangeset
for help on using the changeset viewer.
