0
|
1 /**
|
|
2 * Roundcube SpellCheck script
|
|
3 *
|
|
4 * jQuery'fied spell checker based on GoogieSpell 4.0
|
|
5 * (which was published under GPL "version 2 or any later version")
|
|
6 *
|
|
7 * @licstart The following is the entire license notice for the
|
|
8 * JavaScript code in this file.
|
|
9 *
|
|
10 * Copyright (C) 2006 Amir Salihefendic
|
|
11 * Copyright (C) 2009 The Roundcube Dev Team
|
|
12 * Copyright (C) 2011 Kolab Systems AG
|
|
13 *
|
|
14 * The JavaScript code in this page is free software: you can
|
|
15 * redistribute it and/or modify it under the terms of the GNU
|
|
16 * General Public License (GNU GPL) as published by the Free Software
|
|
17 * Foundation, either version 3 of the License, or (at your option)
|
|
18 * any later version. The code is distributed WITHOUT ANY WARRANTY;
|
|
19 * without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
20 * FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
|
|
21 *
|
|
22 * As additional permission under GNU GPL version 3 section 7, you
|
|
23 * may distribute non-source (e.g., minimized or compacted) forms of
|
|
24 * that code without the copy of the GNU GPL normally required by
|
|
25 * section 4, provided you include this license notice and a URL
|
|
26 * through which recipients can access the Corresponding Source.
|
|
27 *
|
|
28 * @licend The above is the entire license notice
|
|
29 * for the JavaScript code in this file.
|
|
30 *
|
|
31 * @author 4mir Salihefendic <amix@amix.dk>
|
|
32 * @author Aleksander Machniak - <alec [at] alec.pl>
|
|
33 */
|
|
34
|
|
35 var GOOGIE_CUR_LANG,
|
|
36 GOOGIE_DEFAULT_LANG = 'en';
|
|
37
|
|
38 function GoogieSpell(img_dir, server_url, has_dict)
|
|
39 {
|
|
40 var ref = this,
|
|
41 cookie_value = rcmail.get_cookie('language');
|
|
42
|
|
43 GOOGIE_CUR_LANG = cookie_value != null ? cookie_value : GOOGIE_DEFAULT_LANG;
|
|
44
|
|
45 this.array_keys = function(arr) {
|
|
46 var res = [];
|
|
47 for (var key in arr) { res.push([key]); }
|
|
48 return res;
|
|
49 }
|
|
50
|
|
51 this.img_dir = img_dir;
|
|
52 this.server_url = server_url;
|
|
53
|
|
54 this.org_lang_to_word = {
|
|
55 "da": "Dansk", "de": "Deutsch", "en": "English",
|
|
56 "es": "Español", "fr": "Français", "it": "Italiano",
|
|
57 "nl": "Nederlands", "pl": "Polski", "pt": "Português",
|
|
58 "ru": "Русский", "fi": "Suomi", "sv": "Svenska"
|
|
59 };
|
|
60 this.lang_to_word = this.org_lang_to_word;
|
|
61 this.langlist_codes = this.array_keys(this.lang_to_word);
|
|
62 this.show_change_lang_pic = true;
|
|
63 this.change_lang_pic_placement = 'right';
|
|
64 this.report_state_change = true;
|
|
65
|
|
66 this.ta_scroll_top = 0;
|
|
67 this.el_scroll_top = 0;
|
|
68
|
|
69 this.lang_chck_spell = "Check spelling";
|
|
70 this.lang_revert = "Revert to";
|
|
71 this.lang_close = "Close";
|
|
72 this.lang_rsm_edt = "Resume editing";
|
|
73 this.lang_no_error_found = "No spelling errors found";
|
|
74 this.lang_no_suggestions = "No suggestions";
|
|
75 this.lang_learn_word = "Add to dictionary";
|
|
76
|
|
77 this.show_spell_img = false; // roundcube mod.
|
|
78 this.decoration = true;
|
|
79 this.use_close_btn = false;
|
|
80 this.edit_layer_dbl_click = true;
|
|
81 this.report_ta_not_found = true;
|
|
82
|
|
83 // Extensions
|
|
84 this.custom_ajax_error = null;
|
|
85 this.custom_no_spelling_error = null;
|
|
86 this.custom_menu_builder = []; // Should take an eval function and a build menu function
|
|
87 this.custom_item_evaulator = null; // Should take an eval function and a build menu function
|
|
88 this.extra_menu_items = [];
|
|
89 this.custom_spellcheck_starter = null;
|
|
90 this.main_controller = true;
|
|
91 this.has_dictionary = has_dict;
|
|
92
|
|
93 // Observers
|
|
94 this.lang_state_observer = null;
|
|
95 this.spelling_state_observer = null;
|
|
96 this.show_menu_observer = null;
|
|
97 this.all_errors_fixed_observer = null;
|
|
98
|
|
99 // Focus links - used to give the text box focus
|
|
100 this.use_focus = false;
|
|
101 this.focus_link_t = null;
|
|
102 this.focus_link_b = null;
|
|
103
|
|
104 // Counters
|
|
105 this.cnt_errors = 0;
|
|
106 this.cnt_errors_fixed = 0;
|
|
107
|
|
108 // Set document's onclick to hide the language and error menu
|
|
109 $(document).click(function(e) {
|
|
110 var target = $(e.target);
|
|
111 if (target.attr('googie_action_btn') != '1' && ref.isLangWindowShown())
|
|
112 ref.hideLangWindow();
|
|
113 if (target.attr('googie_action_btn') != '1' && ref.isErrorWindowShown())
|
|
114 ref.hideErrorWindow();
|
|
115 });
|
|
116
|
|
117
|
|
118 this.decorateTextarea = function(id)
|
|
119 {
|
|
120 this.text_area = typeof id === 'string' ? document.getElementById(id) : id;
|
|
121
|
|
122 if (this.text_area) {
|
|
123 if (!this.spell_container && this.decoration) {
|
|
124 var table = document.createElement('table'),
|
|
125 tbody = document.createElement('tbody'),
|
|
126 tr = document.createElement('tr'),
|
|
127 spell_container = document.createElement('td'),
|
|
128 r_width = this.isDefined(this.force_width) ? this.force_width : this.text_area.offsetWidth,
|
|
129 r_height = this.isDefined(this.force_height) ? this.force_height : 16;
|
|
130
|
|
131 tr.appendChild(spell_container);
|
|
132 tbody.appendChild(tr);
|
|
133 $(table).append(tbody).insertBefore(this.text_area).width('100%').height(r_height);
|
|
134 $(spell_container).height(r_height).width(r_width).css('text-align', 'right');
|
|
135
|
|
136 this.spell_container = spell_container;
|
|
137 }
|
|
138
|
|
139 this.checkSpellingState();
|
|
140 }
|
|
141 else if (this.report_ta_not_found)
|
|
142 alert('Text area not found');
|
|
143 };
|
|
144
|
|
145 //////
|
|
146 // API Functions (the ones that you can call)
|
|
147 /////
|
|
148 this.setSpellContainer = function(id)
|
|
149 {
|
|
150 this.spell_container = typeof id === 'string' ? document.getElementById(id) : id;
|
|
151 };
|
|
152
|
|
153 this.setLanguages = function(lang_dict)
|
|
154 {
|
|
155 this.lang_to_word = lang_dict;
|
|
156 this.langlist_codes = this.array_keys(lang_dict);
|
|
157 };
|
|
158
|
|
159 this.setCurrentLanguage = function(lan_code)
|
|
160 {
|
|
161 GOOGIE_CUR_LANG = lan_code;
|
|
162
|
|
163 //Set cookie
|
|
164 var now = new Date();
|
|
165 now.setTime(now.getTime() + 365 * 24 * 60 * 60 * 1000);
|
|
166 rcmail.set_cookie('language', lan_code, now);
|
|
167 };
|
|
168
|
|
169 this.setForceWidthHeight = function(width, height)
|
|
170 {
|
|
171 // Set to null if you want to use one of them
|
|
172 this.force_width = width;
|
|
173 this.force_height = height;
|
|
174 };
|
|
175
|
|
176 this.setDecoration = function(bool)
|
|
177 {
|
|
178 this.decoration = bool;
|
|
179 };
|
|
180
|
|
181 this.dontUseCloseButtons = function()
|
|
182 {
|
|
183 this.use_close_btn = false;
|
|
184 };
|
|
185
|
|
186 this.appendNewMenuItem = function(name, call_back_fn, checker)
|
|
187 {
|
|
188 this.extra_menu_items.push([name, call_back_fn, checker]);
|
|
189 };
|
|
190
|
|
191 this.appendCustomMenuBuilder = function(eval_fn, builder)
|
|
192 {
|
|
193 this.custom_menu_builder.push([eval_fn, builder]);
|
|
194 };
|
|
195
|
|
196 this.setFocus = function()
|
|
197 {
|
|
198 try {
|
|
199 this.focus_link_b.focus();
|
|
200 this.focus_link_t.focus();
|
|
201 return true;
|
|
202 }
|
|
203 catch(e) {
|
|
204 return false;
|
|
205 }
|
|
206 };
|
|
207
|
|
208
|
|
209 //////
|
|
210 // Set functions (internal)
|
|
211 /////
|
|
212 this.setStateChanged = function(current_state)
|
|
213 {
|
|
214 this.state = current_state;
|
|
215 if (this.spelling_state_observer != null && this.report_state_change)
|
|
216 this.spelling_state_observer(current_state, this);
|
|
217 };
|
|
218
|
|
219 this.setReportStateChange = function(bool)
|
|
220 {
|
|
221 this.report_state_change = bool;
|
|
222 };
|
|
223
|
|
224
|
|
225 //////
|
|
226 // Request functions
|
|
227 /////
|
|
228 this.getUrl = function()
|
|
229 {
|
|
230 return this.server_url + GOOGIE_CUR_LANG;
|
|
231 };
|
|
232
|
|
233 this.escapeSpecial = function(val)
|
|
234 {
|
|
235 return val ? val.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") : '';
|
|
236 };
|
|
237
|
|
238 this.createXMLReq = function (text)
|
|
239 {
|
|
240 return '<?xml version="1.0" encoding="utf-8" ?>'
|
|
241 + '<spellrequest textalreadyclipped="0" ignoredups="0" ignoredigits="1" ignoreallcaps="1">'
|
|
242 + '<text>' + text + '</text></spellrequest>';
|
|
243 };
|
|
244
|
|
245 this.spellCheck = function(ignore)
|
|
246 {
|
|
247 this.prepare(ignore);
|
|
248
|
|
249 var req_text = this.escapeSpecial(this.orginal_text),
|
|
250 ref = this;
|
|
251
|
|
252 $.ajax({ type: 'POST', url: this.getUrl(), data: this.createXMLReq(req_text), dataType: 'text',
|
|
253 error: function(o) {
|
|
254 if (ref.custom_ajax_error)
|
|
255 ref.custom_ajax_error(ref);
|
|
256 else
|
|
257 alert('An error was encountered on the server. Please try again later.');
|
|
258 if (ref.main_controller) {
|
|
259 $(ref.spell_span).remove();
|
|
260 ref.removeIndicator();
|
|
261 }
|
|
262 ref.checkSpellingState();
|
|
263 },
|
|
264 success: function(data) {
|
|
265 ref.processData(data);
|
|
266 if (!ref.results.length) {
|
|
267 if (!ref.custom_no_spelling_error)
|
|
268 ref.flashNoSpellingErrorState();
|
|
269 else
|
|
270 ref.custom_no_spelling_error(ref);
|
|
271 }
|
|
272 ref.removeIndicator();
|
|
273 }
|
|
274 });
|
|
275 };
|
|
276
|
|
277 this.learnWord = function(word, id)
|
|
278 {
|
|
279 word = this.escapeSpecial(word.innerHTML);
|
|
280
|
|
281 var ref = this,
|
|
282 req_text = '<?xml version="1.0" encoding="utf-8" ?><learnword><text>' + word + '</text></learnword>';
|
|
283
|
|
284 $.ajax({ type: 'POST', url: this.getUrl(), data: req_text, dataType: 'text',
|
|
285 error: function(o) {
|
|
286 if (ref.custom_ajax_error)
|
|
287 ref.custom_ajax_error(ref);
|
|
288 else
|
|
289 alert('An error was encountered on the server. Please try again later.');
|
|
290 },
|
|
291 success: function(data) {
|
|
292 }
|
|
293 });
|
|
294 };
|
|
295
|
|
296
|
|
297 //////
|
|
298 // Spell checking functions
|
|
299 /////
|
|
300 this.prepare = function(ignore, no_indicator)
|
|
301 {
|
|
302 this.cnt_errors_fixed = 0;
|
|
303 this.cnt_errors = 0;
|
|
304 this.setStateChanged('checking_spell');
|
|
305 this.orginal_text = '';
|
|
306
|
|
307 if (!no_indicator && this.main_controller)
|
|
308 this.appendIndicator(this.spell_span);
|
|
309
|
|
310 this.error_links = [];
|
|
311 this.ta_scroll_top = this.text_area.scrollTop;
|
|
312 this.ignore = ignore;
|
|
313 this.hideLangWindow();
|
|
314
|
|
315 var area = $(this.text_area);
|
|
316
|
|
317 if (area.val() == '' || ignore) {
|
|
318 if (!this.custom_no_spelling_error)
|
|
319 this.flashNoSpellingErrorState();
|
|
320 else
|
|
321 this.custom_no_spelling_error(this);
|
|
322 this.removeIndicator();
|
|
323 return;
|
|
324 }
|
|
325
|
|
326 this.createEditLayer(area.width(), area.height());
|
|
327 this.createErrorWindow();
|
|
328 $('body').append(this.error_window);
|
|
329
|
|
330 try { netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead"); }
|
|
331 catch (e) { }
|
|
332
|
|
333 if (this.main_controller)
|
|
334 $(this.spell_span).off('click');
|
|
335
|
|
336 this.orginal_text = area.val();
|
|
337 };
|
|
338
|
|
339 this.parseResult = function(r_text)
|
|
340 {
|
|
341 // Returns an array: result[item] -> ['attrs'], ['suggestions']
|
|
342 var re_split_attr_c = /\w+="(\d+|true)"/g,
|
|
343 re_split_text = /\t/g,
|
|
344 matched_c = r_text.match(/<c[^>]*>[^<]*<\/c>/g),
|
|
345 results = [];
|
|
346
|
|
347 if (matched_c == null)
|
|
348 return results;
|
|
349
|
|
350 for (var i=0, len=matched_c.length; i < len; i++) {
|
|
351 var item = [];
|
|
352 this.errorFound();
|
|
353
|
|
354 // Get attributes
|
|
355 item['attrs'] = [];
|
|
356 var c_attr, val,
|
|
357 split_c = matched_c[i].match(re_split_attr_c);
|
|
358 for (var j=0; j < split_c.length; j++) {
|
|
359 c_attr = split_c[j].split(/=/);
|
|
360 val = c_attr[1].replace(/"/g, '');
|
|
361 item['attrs'][c_attr[0]] = val != 'true' ? parseInt(val) : val;
|
|
362 }
|
|
363
|
|
364 // Get suggestions
|
|
365 item['suggestions'] = [];
|
|
366 var only_text = matched_c[i].replace(/<[^>]*>/g, ''),
|
|
367 split_t = only_text.split(re_split_text);
|
|
368 for (var k=0; k < split_t.length; k++) {
|
|
369 if(split_t[k] != '')
|
|
370 item['suggestions'].push(split_t[k]);
|
|
371 }
|
|
372 results.push(item);
|
|
373 }
|
|
374
|
|
375 return results;
|
|
376 };
|
|
377
|
|
378 this.processData = function(data)
|
|
379 {
|
|
380 this.results = this.parseResult(data);
|
|
381 if (this.results.length) {
|
|
382 this.showErrorsInIframe();
|
|
383 this.resumeEditingState();
|
|
384 }
|
|
385 };
|
|
386
|
|
387 //////
|
|
388 // Error menu functions
|
|
389 /////
|
|
390 this.createErrorWindow = function()
|
|
391 {
|
|
392 this.error_window = document.createElement('div');
|
|
393 $(this.error_window).addClass('googie_window popupmenu').attr('googie_action_btn', '1');
|
|
394 };
|
|
395
|
|
396 this.isErrorWindowShown = function()
|
|
397 {
|
|
398 return $(this.error_window).is(':visible');
|
|
399 };
|
|
400
|
|
401 this.hideErrorWindow = function()
|
|
402 {
|
|
403 $(this.error_window).hide();
|
|
404 $(this.error_window_iframe).hide();
|
|
405 };
|
|
406
|
|
407 this.updateOrginalText = function(offset, old_value, new_value, id)
|
|
408 {
|
|
409 var part_1 = this.orginal_text.substring(0, offset),
|
|
410 part_2 = this.orginal_text.substring(offset+old_value.length),
|
|
411 add_2_offset = new_value.length - old_value.length;
|
|
412
|
|
413 this.orginal_text = part_1 + new_value + part_2;
|
|
414 $(this.text_area).val(this.orginal_text);
|
|
415 for (var j=0, len=this.results.length; j<len; j++) {
|
|
416 // Don't edit the offset of the current item
|
|
417 if (j != id && j > id)
|
|
418 this.results[j]['attrs']['o'] += add_2_offset;
|
|
419 }
|
|
420 };
|
|
421
|
|
422 this.saveOldValue = function(elm, old_value) {
|
|
423 elm.is_changed = true;
|
|
424 elm.old_value = old_value;
|
|
425 };
|
|
426
|
|
427 this.createListSeparator = function()
|
|
428 {
|
|
429 var td = document.createElement('td'),
|
|
430 tr = document.createElement('tr');
|
|
431
|
|
432 $(td).html(' ').attr('googie_action_btn', '1')
|
|
433 .css({'cursor': 'default', 'font-size': '3px', 'border-top': '1px solid #ccc', 'padding-top': '3px'});
|
|
434 tr.appendChild(td);
|
|
435
|
|
436 return tr;
|
|
437 };
|
|
438
|
|
439 this.correctError = function(id, elm, l_elm, rm_pre_space)
|
|
440 {
|
|
441 var old_value = elm.innerHTML,
|
|
442 new_value = l_elm.nodeType == 3 ? l_elm.nodeValue : l_elm.innerHTML,
|
|
443 offset = this.results[id]['attrs']['o'];
|
|
444
|
|
445 if (rm_pre_space) {
|
|
446 var pre_length = elm.previousSibling.innerHTML;
|
|
447 elm.previousSibling.innerHTML = pre_length.slice(0, pre_length.length-1);
|
|
448 old_value = " " + old_value;
|
|
449 offset--;
|
|
450 }
|
|
451
|
|
452 this.hideErrorWindow();
|
|
453 this.updateOrginalText(offset, old_value, new_value, id);
|
|
454
|
|
455 $(elm).html(new_value).css('color', 'green').attr('is_corrected', true);
|
|
456
|
|
457 this.results[id]['attrs']['l'] = new_value.length;
|
|
458
|
|
459 if (!this.isDefined(elm.old_value))
|
|
460 this.saveOldValue(elm, old_value);
|
|
461
|
|
462 this.errorFixed();
|
|
463 };
|
|
464
|
|
465 this.ignoreError = function(elm, id)
|
|
466 {
|
|
467 // @TODO: ignore all same words
|
|
468 $(elm).removeAttr('class').css('color', '').off();
|
|
469 this.hideErrorWindow();
|
|
470 };
|
|
471
|
|
472 this.showErrorWindow = function(elm, id)
|
|
473 {
|
|
474 if (this.show_menu_observer)
|
|
475 this.show_menu_observer(this);
|
|
476
|
|
477 var ref = this,
|
|
478 pos = $(elm).offset(),
|
|
479 table = document.createElement('table'),
|
|
480 list = document.createElement('tbody');
|
|
481
|
|
482 $(this.error_window).html('');
|
|
483 $(table).addClass('googie_list').attr('googie_action_btn', '1');
|
|
484
|
|
485 // Check if we should use custom menu builder, if not we use the default
|
|
486 var changed = false;
|
|
487 for (var k=0; k<this.custom_menu_builder.length; k++) {
|
|
488 var eb = this.custom_menu_builder[k];
|
|
489 if (eb[0](this.results[id])) {
|
|
490 changed = eb[1](this, list, elm);
|
|
491 break;
|
|
492 }
|
|
493 }
|
|
494
|
|
495 if (!changed) {
|
|
496 // Build up the result list
|
|
497 var suggestions = this.results[id]['suggestions'],
|
|
498 offset = this.results[id]['attrs']['o'],
|
|
499 len = this.results[id]['attrs']['l'],
|
|
500 row, item, dummy;
|
|
501
|
|
502 // [Add to dictionary] button
|
|
503 if (this.has_dictionary && !$(elm).attr('is_corrected')) {
|
|
504 row = document.createElement('tr'),
|
|
505 item = document.createElement('td'),
|
|
506 dummy = document.createElement('span');
|
|
507
|
|
508 $(dummy).text(this.lang_learn_word);
|
|
509 $(item).attr('googie_action_btn', '1').css('cursor', 'default')
|
|
510 .mouseover(ref.item_onmouseover)
|
|
511 .mouseout(ref.item_onmouseout)
|
|
512 .click(function(e) {
|
|
513 ref.learnWord(elm, id);
|
|
514 ref.ignoreError(elm, id);
|
|
515 });
|
|
516
|
|
517 item.appendChild(dummy);
|
|
518 row.appendChild(item);
|
|
519 list.appendChild(row);
|
|
520 }
|
|
521 /*
|
|
522 if (suggestions.length == 0) {
|
|
523 row = document.createElement('tr'),
|
|
524 item = document.createElement('td'),
|
|
525 dummy = document.createElement('span');
|
|
526
|
|
527 $(dummy).text(this.lang_no_suggestions);
|
|
528 $(item).attr('googie_action_btn', '1').css('cursor', 'default');
|
|
529
|
|
530 item.appendChild(dummy);
|
|
531 row.appendChild(item);
|
|
532 list.appendChild(row);
|
|
533 }
|
|
534 */
|
|
535 for (var i=0, len=suggestions.length; i < len; i++) {
|
|
536 row = document.createElement('tr'),
|
|
537 item = document.createElement('td'),
|
|
538 dummy = document.createElement('span');
|
|
539
|
|
540 $(dummy).html(suggestions[i]);
|
|
541
|
|
542 $(item).mouseover(this.item_onmouseover).mouseout(this.item_onmouseout)
|
|
543 .click(function(e) { ref.correctError(id, elm, e.target.firstChild) });
|
|
544
|
|
545 item.appendChild(dummy);
|
|
546 row.appendChild(item);
|
|
547 list.appendChild(row);
|
|
548 }
|
|
549
|
|
550 // The element is changed, append the revert
|
|
551 if (elm.is_changed && elm.innerHTML != elm.old_value) {
|
|
552 var old_value = elm.old_value,
|
|
553 revert_row = document.createElement('tr'),
|
|
554 revert = document.createElement('td'),
|
|
555 rev_span = document.createElement('span');
|
|
556
|
|
557 $(rev_span).addClass('googie_list_revert').html(this.lang_revert + ' ' + old_value);
|
|
558
|
|
559 $(revert).mouseover(this.item_onmouseover).mouseout(this.item_onmouseout)
|
|
560 .click(function(e) {
|
|
561 ref.updateOrginalText(offset, elm.innerHTML, old_value, id);
|
|
562 $(elm).removeAttr('is_corrected').css('color', '#b91414').html(old_value);
|
|
563 ref.hideErrorWindow();
|
|
564 });
|
|
565
|
|
566 revert.appendChild(rev_span);
|
|
567 revert_row.appendChild(revert);
|
|
568 list.appendChild(revert_row);
|
|
569 }
|
|
570
|
|
571 // Append the edit box
|
|
572 var edit_row = document.createElement('tr'),
|
|
573 edit = document.createElement('td'),
|
|
574 edit_input = document.createElement('input'),
|
|
575 ok_pic = document.createElement('img'),
|
|
576 edit_form = document.createElement('form');
|
|
577
|
|
578 var onsub = function () {
|
|
579 if (edit_input.value != '') {
|
|
580 if (!ref.isDefined(elm.old_value))
|
|
581 ref.saveOldValue(elm, elm.innerHTML);
|
|
582
|
|
583 ref.updateOrginalText(offset, elm.innerHTML, edit_input.value, id);
|
|
584 $(elm).attr('is_corrected', true).css('color', 'green').text(edit_input.value);
|
|
585 ref.hideErrorWindow();
|
|
586 }
|
|
587 return false;
|
|
588 };
|
|
589
|
|
590 $(edit_input).width(120)
|
|
591 .css({'margin': 0, 'padding': 0})
|
|
592 .val($(elm).text()).attr('googie_action_btn', '1');
|
|
593 $(edit).css('cursor', 'default').attr('googie_action_btn', '1');
|
|
594
|
|
595 $(ok_pic).attr('src', this.img_dir + 'ok.gif')
|
|
596 .width(32).height(16)
|
|
597 .css({'cursor': 'pointer', 'margin-left': '2px', 'margin-right': '2px'})
|
|
598 .click(onsub);
|
|
599
|
|
600 $(edit_form).attr('googie_action_btn', '1')
|
|
601 .css({'margin': 0, 'padding': 0, 'cursor': 'default', 'white-space': 'nowrap'})
|
|
602 .submit(onsub);
|
|
603
|
|
604 edit_form.appendChild(edit_input);
|
|
605 edit_form.appendChild(ok_pic);
|
|
606 edit.appendChild(edit_form);
|
|
607 edit_row.appendChild(edit);
|
|
608 list.appendChild(edit_row);
|
|
609
|
|
610 // Append extra menu items
|
|
611 if (this.extra_menu_items.length > 0)
|
|
612 list.appendChild(this.createListSeparator());
|
|
613
|
|
614 var loop = function(i) {
|
|
615 if (i < ref.extra_menu_items.length) {
|
|
616 var e_elm = ref.extra_menu_items[i];
|
|
617
|
|
618 if (!e_elm[2] || e_elm[2](elm, ref)) {
|
|
619 var e_row = document.createElement('tr'),
|
|
620 e_col = document.createElement('td');
|
|
621
|
|
622 $(e_col).html(e_elm[0])
|
|
623 .mouseover(ref.item_onmouseover)
|
|
624 .mouseout(ref.item_onmouseout)
|
|
625 .click(function() { return e_elm[1](elm, ref) });
|
|
626
|
|
627 e_row.appendChild(e_col);
|
|
628 list.appendChild(e_row);
|
|
629 }
|
|
630 loop(i+1);
|
|
631 }
|
|
632 };
|
|
633
|
|
634 loop(0);
|
|
635 loop = null;
|
|
636
|
|
637 //Close button
|
|
638 if (this.use_close_btn) {
|
|
639 list.appendChild(this.createCloseButton(this.hideErrorWindow));
|
|
640 }
|
|
641 }
|
|
642
|
|
643 table.appendChild(list);
|
|
644 this.error_window.appendChild(table);
|
|
645
|
|
646 // calculate and set position
|
|
647 var height = $(this.error_window).height(),
|
|
648 width = $(this.error_window).width(),
|
|
649 pageheight = $(document).height(),
|
|
650 pagewidth = $(document).width(),
|
|
651 top = pos.top + height + 20 < pageheight ? pos.top + 20 : pos.top - height,
|
|
652 left = pos.left + width < pagewidth ? pos.left : pos.left - width;
|
|
653
|
|
654 $(this.error_window).css({'top': top+'px', 'left': left+'px'}).show();
|
|
655
|
|
656 // Dummy for IE - dropdown bug fix
|
|
657 if (document.all && !window.opera) {
|
|
658 if (!this.error_window_iframe) {
|
|
659 var iframe = $('<iframe>').css({'position': 'absolute', 'z-index': -1});
|
|
660 $('body').append(iframe);
|
|
661 this.error_window_iframe = iframe;
|
|
662 }
|
|
663
|
|
664 $(this.error_window_iframe)
|
|
665 .css({'top': this.error_window.offsetTop, 'left': this.error_window.offsetLeft,
|
|
666 'width': this.error_window.offsetWidth, 'height': this.error_window.offsetHeight})
|
|
667 .show();
|
|
668 }
|
|
669 };
|
|
670
|
|
671
|
|
672 //////
|
|
673 // Edit layer (the layer where the suggestions are stored)
|
|
674 //////
|
|
675 this.createEditLayer = function(width, height)
|
|
676 {
|
|
677 this.edit_layer = document.createElement('div');
|
|
678 $(this.edit_layer).addClass('googie_edit_layer').attr('id', 'googie_edit_layer')
|
|
679 .width(width).height(height);
|
|
680
|
|
681 if (this.text_area.nodeName.toLowerCase() != 'input' || $(this.text_area).val() == '') {
|
|
682 $(this.edit_layer).css('overflow', 'auto');
|
|
683 } else {
|
|
684 $(this.edit_layer).css('overflow', 'hidden');
|
|
685 }
|
|
686
|
|
687 var ref = this;
|
|
688
|
|
689 if (this.edit_layer_dbl_click) {
|
|
690 $(this.edit_layer).dblclick(function(e) {
|
|
691 if (e.target.className != 'googie_link' && !ref.isErrorWindowShown()) {
|
|
692 ref.resumeEditing();
|
|
693 var fn1 = function() {
|
|
694 $(ref.text_area).focus();
|
|
695 fn1 = null;
|
|
696 };
|
|
697 window.setTimeout(fn1, 10);
|
|
698 }
|
|
699 return false;
|
|
700 });
|
|
701 }
|
|
702 };
|
|
703
|
|
704 this.resumeEditing = function()
|
|
705 {
|
|
706 this.setStateChanged('ready');
|
|
707
|
|
708 if (this.edit_layer)
|
|
709 this.el_scroll_top = this.edit_layer.scrollTop;
|
|
710
|
|
711 this.hideErrorWindow();
|
|
712
|
|
713 if (this.main_controller)
|
|
714 $(this.spell_span).removeClass().addClass('googie_no_style');
|
|
715
|
|
716 if (!this.ignore) {
|
|
717 if (this.use_focus) {
|
|
718 $(this.focus_link_t).remove();
|
|
719 $(this.focus_link_b).remove();
|
|
720 }
|
|
721
|
|
722 $(this.edit_layer).remove();
|
|
723 $(this.text_area).show();
|
|
724
|
|
725 if (this.el_scroll_top != undefined)
|
|
726 this.text_area.scrollTop = this.el_scroll_top;
|
|
727 }
|
|
728 this.checkSpellingState(false);
|
|
729 };
|
|
730
|
|
731 this.createErrorLink = function(text, id)
|
|
732 {
|
|
733 var elm = document.createElement('span'),
|
|
734 ref = this,
|
|
735 d = function (e) {
|
|
736 ref.showErrorWindow(elm, id);
|
|
737 d = null;
|
|
738 return false;
|
|
739 };
|
|
740
|
|
741 $(elm).html(text).addClass('googie_link').click(d).removeAttr('is_corrected')
|
|
742 .attr({'googie_action_btn' : '1', 'g_id' : id});
|
|
743
|
|
744 return elm;
|
|
745 };
|
|
746
|
|
747 this.createPart = function(txt_part)
|
|
748 {
|
|
749 if (txt_part == " ")
|
|
750 return document.createTextNode(" ");
|
|
751
|
|
752 txt_part = this.escapeSpecial(txt_part);
|
|
753 txt_part = txt_part.replace(/\n/g, "<br>");
|
|
754 txt_part = txt_part.replace(/ /g, " ");
|
|
755 txt_part = txt_part.replace(/^ /g, " ");
|
|
756 txt_part = txt_part.replace(/ $/g, " ");
|
|
757
|
|
758 var span = document.createElement('span');
|
|
759 $(span).html(txt_part);
|
|
760 return span;
|
|
761 };
|
|
762
|
|
763 this.showErrorsInIframe = function()
|
|
764 {
|
|
765 var output = document.createElement('div'),
|
|
766 pointer = 0,
|
|
767 results = this.results;
|
|
768
|
|
769 if (results.length > 0) {
|
|
770 for (var i=0, length=results.length; i < length; i++) {
|
|
771 var offset = results[i]['attrs']['o'],
|
|
772 len = results[i]['attrs']['l'],
|
|
773 part_1_text = this.orginal_text.substring(pointer, offset),
|
|
774 part_1 = this.createPart(part_1_text);
|
|
775
|
|
776 output.appendChild(part_1);
|
|
777 pointer += offset - pointer;
|
|
778
|
|
779 // If the last child was an error, then insert some space
|
|
780 var err_link = this.createErrorLink(this.orginal_text.substr(offset, len), i);
|
|
781 this.error_links.push(err_link);
|
|
782 output.appendChild(err_link);
|
|
783 pointer += len;
|
|
784 }
|
|
785
|
|
786 // Insert the rest of the orginal text
|
|
787 var part_2_text = this.orginal_text.substr(pointer, this.orginal_text.length),
|
|
788 part_2 = this.createPart(part_2_text);
|
|
789
|
|
790 output.appendChild(part_2);
|
|
791 }
|
|
792 else
|
|
793 output.innerHTML = this.orginal_text;
|
|
794
|
|
795 $(output).css('text-align', 'left');
|
|
796
|
|
797 var me = this;
|
|
798 if (this.custom_item_evaulator)
|
|
799 $.map(this.error_links, function(elm){me.custom_item_evaulator(me, elm)});
|
|
800
|
|
801 $(this.edit_layer).append(output);
|
|
802
|
|
803 // Hide text area and show edit layer
|
|
804 $(this.text_area).hide();
|
|
805 $(this.edit_layer).insertBefore(this.text_area);
|
|
806
|
|
807 if (this.use_focus) {
|
|
808 this.focus_link_t = this.createFocusLink('focus_t');
|
|
809 this.focus_link_b = this.createFocusLink('focus_b');
|
|
810
|
|
811 $(this.focus_link_t).insertBefore(this.edit_layer);
|
|
812 $(this.focus_link_b).insertAfter(this.edit_layer);
|
|
813 }
|
|
814
|
|
815 // this.edit_layer.scrollTop = this.ta_scroll_top;
|
|
816 };
|
|
817
|
|
818
|
|
819 //////
|
|
820 // Choose language menu
|
|
821 //////
|
|
822 this.createLangWindow = function()
|
|
823 {
|
|
824 this.language_window = document.createElement('div');
|
|
825 $(this.language_window).addClass('googie_window popupmenu')
|
|
826 .width(100).attr('googie_action_btn', '1');
|
|
827
|
|
828 // Build up the result list
|
|
829 var table = document.createElement('table'),
|
|
830 list = document.createElement('tbody'),
|
|
831 ref = this,
|
|
832 row, item, span;
|
|
833
|
|
834 $(table).addClass('googie_list').width('100%');
|
|
835 this.lang_elms = [];
|
|
836
|
|
837 for (i=0; i < this.langlist_codes.length; i++) {
|
|
838 row = document.createElement('tr');
|
|
839 item = document.createElement('td');
|
|
840 span = document.createElement('span');
|
|
841
|
|
842 $(span).text(this.lang_to_word[this.langlist_codes[i]]);
|
|
843 this.lang_elms.push(item);
|
|
844
|
|
845 $(item).attr('googieId', this.langlist_codes[i])
|
|
846 .click(function(e) {
|
|
847 ref.deHighlightCurSel();
|
|
848 ref.setCurrentLanguage($(this).attr('googieId'));
|
|
849
|
|
850 if (ref.lang_state_observer != null) {
|
|
851 ref.lang_state_observer();
|
|
852 }
|
|
853
|
|
854 ref.highlightCurSel();
|
|
855 ref.hideLangWindow();
|
|
856 })
|
|
857 .mouseover(function(e) {
|
|
858 if (this.className != "googie_list_selected")
|
|
859 this.className = "googie_list_onhover";
|
|
860 })
|
|
861 .mouseout(function(e) {
|
|
862 if (this.className != "googie_list_selected")
|
|
863 this.className = "googie_list_onout";
|
|
864 });
|
|
865
|
|
866 item.appendChild(span);
|
|
867 row.appendChild(item);
|
|
868 list.appendChild(row);
|
|
869 }
|
|
870
|
|
871 // Close button
|
|
872 if (this.use_close_btn) {
|
|
873 list.appendChild(this.createCloseButton(function () { ref.hideLangWindow.apply(ref) }));
|
|
874 }
|
|
875
|
|
876 this.highlightCurSel();
|
|
877
|
|
878 table.appendChild(list);
|
|
879 this.language_window.appendChild(table);
|
|
880 };
|
|
881
|
|
882 this.isLangWindowShown = function()
|
|
883 {
|
|
884 return $(this.language_window).is(':visible');
|
|
885 };
|
|
886
|
|
887 this.hideLangWindow = function()
|
|
888 {
|
|
889 $(this.language_window).hide();
|
|
890 $(this.switch_lan_pic).removeClass().addClass('googie_lang_3d_on');
|
|
891 };
|
|
892
|
|
893 this.showLangWindow = function(elm)
|
|
894 {
|
|
895 if (this.show_menu_observer)
|
|
896 this.show_menu_observer(this);
|
|
897
|
|
898 this.createLangWindow();
|
|
899 $('body').append(this.language_window);
|
|
900
|
|
901 var pos = $(elm).offset(),
|
|
902 height = $(elm).height(),
|
|
903 width = $(elm).width(),
|
|
904 h = $(this.language_window).height(),
|
|
905 pageheight = $(document).height(),
|
|
906 left = this.change_lang_pic_placement == 'right' ?
|
|
907 pos.left - 100 + width : pos.left + width,
|
|
908 top = pos.top + h < pageheight ? pos.top + height : pos.top - h - 4;
|
|
909
|
|
910 $(this.language_window).css({'top' : top+'px','left' : left+'px'}).show();
|
|
911
|
|
912 this.highlightCurSel();
|
|
913 };
|
|
914
|
|
915 this.deHighlightCurSel = function()
|
|
916 {
|
|
917 $(this.lang_cur_elm).removeClass().addClass('googie_list_onout');
|
|
918 };
|
|
919
|
|
920 this.highlightCurSel = function()
|
|
921 {
|
|
922 if (GOOGIE_CUR_LANG == null)
|
|
923 GOOGIE_CUR_LANG = GOOGIE_DEFAULT_LANG;
|
|
924 for (var i=0; i < this.lang_elms.length; i++) {
|
|
925 if ($(this.lang_elms[i]).attr('googieId') == GOOGIE_CUR_LANG) {
|
|
926 this.lang_elms[i].className = 'googie_list_selected';
|
|
927 this.lang_cur_elm = this.lang_elms[i];
|
|
928 }
|
|
929 else {
|
|
930 this.lang_elms[i].className = 'googie_list_onout';
|
|
931 }
|
|
932 }
|
|
933 };
|
|
934
|
|
935 this.createChangeLangPic = function()
|
|
936 {
|
|
937 var img = $('<img>')
|
|
938 .attr({src: this.img_dir + 'change_lang.gif', 'alt': 'Change language', 'googie_action_btn': '1'}),
|
|
939 switch_lan = document.createElement('span');
|
|
940 ref = this;
|
|
941
|
|
942 $(switch_lan).addClass('googie_lang_3d_on')
|
|
943 .append(img)
|
|
944 .click(function(e) {
|
|
945 var elm = this.tagName.toLowerCase() == 'img' ? this.parentNode : this;
|
|
946 if($(elm).hasClass('googie_lang_3d_click')) {
|
|
947 elm.className = 'googie_lang_3d_on';
|
|
948 ref.hideLangWindow();
|
|
949 }
|
|
950 else {
|
|
951 elm.className = 'googie_lang_3d_click';
|
|
952 ref.showLangWindow(elm);
|
|
953 }
|
|
954 });
|
|
955
|
|
956 return switch_lan;
|
|
957 };
|
|
958
|
|
959 this.createSpellDiv = function()
|
|
960 {
|
|
961 var span = document.createElement('span');
|
|
962
|
|
963 $(span).addClass('googie_check_spelling_link').text(this.lang_chck_spell);
|
|
964
|
|
965 if (this.show_spell_img) {
|
|
966 $(span).append(' ').append($('<img>').attr('src', this.img_dir + 'spellc.gif'));
|
|
967 }
|
|
968 return span;
|
|
969 };
|
|
970
|
|
971
|
|
972 //////
|
|
973 // State functions
|
|
974 /////
|
|
975 this.flashNoSpellingErrorState = function(on_finish)
|
|
976 {
|
|
977 this.setStateChanged('no_error_found');
|
|
978
|
|
979 var ref = this;
|
|
980 if (this.main_controller) {
|
|
981 var no_spell_errors;
|
|
982 if (on_finish) {
|
|
983 var fn = function() {
|
|
984 on_finish();
|
|
985 ref.checkSpellingState();
|
|
986 };
|
|
987 no_spell_errors = fn;
|
|
988 }
|
|
989 else
|
|
990 no_spell_errors = function () { ref.checkSpellingState() };
|
|
991
|
|
992 var rsm = $('<span>').text(this.lang_no_error_found);
|
|
993
|
|
994 $(this.switch_lan_pic).hide();
|
|
995 $(this.spell_span).empty().append(rsm)
|
|
996 .removeClass().addClass('googie_check_spelling_ok');
|
|
997
|
|
998 window.setTimeout(no_spell_errors, 1000);
|
|
999 }
|
|
1000 };
|
|
1001
|
|
1002 this.resumeEditingState = function()
|
|
1003 {
|
|
1004 this.setStateChanged('resume_editing');
|
|
1005
|
|
1006 //Change link text to resume
|
|
1007 if (this.main_controller) {
|
|
1008 var rsm = $('<span>').text(this.lang_rsm_edt);
|
|
1009 var ref = this;
|
|
1010
|
|
1011 $(this.switch_lan_pic).hide();
|
|
1012 $(this.spell_span).empty().off().append(rsm)
|
|
1013 .click(function() { ref.resumeEditing(); })
|
|
1014 .removeClass().addClass('googie_resume_editing');
|
|
1015 }
|
|
1016
|
|
1017 try { this.edit_layer.scrollTop = this.ta_scroll_top; }
|
|
1018 catch (e) {};
|
|
1019 };
|
|
1020
|
|
1021 this.checkSpellingState = function(fire)
|
|
1022 {
|
|
1023 if (fire)
|
|
1024 this.setStateChanged('ready');
|
|
1025
|
|
1026 if (this.show_change_lang_pic)
|
|
1027 this.switch_lan_pic = this.createChangeLangPic();
|
|
1028 else
|
|
1029 this.switch_lan_pic = document.createElement('span');
|
|
1030
|
|
1031 var span_chck = this.createSpellDiv(),
|
|
1032 ref = this;
|
|
1033
|
|
1034 if (this.custom_spellcheck_starter)
|
|
1035 $(span_chck).click(function(e) { ref.custom_spellcheck_starter(); });
|
|
1036 else {
|
|
1037 $(span_chck).click(function(e) { ref.spellCheck(); });
|
|
1038 }
|
|
1039
|
|
1040 if (this.main_controller) {
|
|
1041 if (this.change_lang_pic_placement == 'left') {
|
|
1042 $(this.spell_container).empty().append(this.switch_lan_pic).append(' ').append(span_chck);
|
|
1043 } else {
|
|
1044 $(this.spell_container).empty().append(span_chck).append(' ').append(this.switch_lan_pic);
|
|
1045 }
|
|
1046 }
|
|
1047
|
|
1048 this.spell_span = span_chck;
|
|
1049 };
|
|
1050
|
|
1051
|
|
1052 //////
|
|
1053 // Misc. functions
|
|
1054 /////
|
|
1055 this.isDefined = function(o)
|
|
1056 {
|
|
1057 return (o !== undefined && o !== null)
|
|
1058 };
|
|
1059
|
|
1060 this.errorFixed = function()
|
|
1061 {
|
|
1062 this.cnt_errors_fixed++;
|
|
1063 if (this.all_errors_fixed_observer)
|
|
1064 if (this.cnt_errors_fixed == this.cnt_errors) {
|
|
1065 this.hideErrorWindow();
|
|
1066 this.all_errors_fixed_observer();
|
|
1067 }
|
|
1068 };
|
|
1069
|
|
1070 this.errorFound = function()
|
|
1071 {
|
|
1072 this.cnt_errors++;
|
|
1073 };
|
|
1074
|
|
1075 this.createCloseButton = function(c_fn)
|
|
1076 {
|
|
1077 return this.createButton(this.lang_close, 'googie_list_close', c_fn);
|
|
1078 };
|
|
1079
|
|
1080 this.createButton = function(name, css_class, c_fn)
|
|
1081 {
|
|
1082 var btn_row = document.createElement('tr'),
|
|
1083 btn = document.createElement('td'),
|
|
1084 spn_btn;
|
|
1085
|
|
1086 if (css_class) {
|
|
1087 spn_btn = document.createElement('span');
|
|
1088 $(spn_btn).addClass(css_class).html(name);
|
|
1089 } else {
|
|
1090 spn_btn = document.createTextNode(name);
|
|
1091 }
|
|
1092
|
|
1093 $(btn).click(c_fn)
|
|
1094 .mouseover(this.item_onmouseover)
|
|
1095 .mouseout(this.item_onmouseout);
|
|
1096
|
|
1097 btn.appendChild(spn_btn);
|
|
1098 btn_row.appendChild(btn);
|
|
1099
|
|
1100 return btn_row;
|
|
1101 };
|
|
1102
|
|
1103 this.removeIndicator = function(elm)
|
|
1104 {
|
|
1105 //$(this.indicator).remove();
|
|
1106 // roundcube mod.
|
|
1107 if (window.rcmail)
|
|
1108 rcmail.set_busy(false, null, this.rc_msg_id);
|
|
1109 };
|
|
1110
|
|
1111 this.appendIndicator = function(elm)
|
|
1112 {
|
|
1113 // modified by roundcube
|
|
1114 if (window.rcmail)
|
|
1115 this.rc_msg_id = rcmail.set_busy(true, 'checking');
|
|
1116 /*
|
|
1117 this.indicator = document.createElement('img');
|
|
1118 $(this.indicator).attr('src', this.img_dir + 'indicator.gif')
|
|
1119 .css({'margin-right': '5px', 'text-decoration': 'none'}).width(16).height(16);
|
|
1120
|
|
1121 if (elm)
|
|
1122 $(this.indicator).insertBefore(elm);
|
|
1123 else
|
|
1124 $('body').append(this.indicator);
|
|
1125 */
|
|
1126 }
|
|
1127
|
|
1128 this.createFocusLink = function(name)
|
|
1129 {
|
|
1130 var link = document.createElement('a');
|
|
1131 $(link).attr({'href': 'javascript:;', 'name': name});
|
|
1132 return link;
|
|
1133 };
|
|
1134
|
|
1135 this.item_onmouseover = function(e)
|
|
1136 {
|
|
1137 if (this.className != 'googie_list_revert' && this.className != 'googie_list_close')
|
|
1138 this.className = 'googie_list_onhover';
|
|
1139 else
|
|
1140 this.parentNode.className = 'googie_list_onhover';
|
|
1141 };
|
|
1142
|
|
1143 this.item_onmouseout = function(e)
|
|
1144 {
|
|
1145 if (this.className != 'googie_list_revert' && this.className != 'googie_list_close')
|
|
1146 this.className = 'googie_list_onout';
|
|
1147 else
|
|
1148 this.parentNode.className = 'googie_list_onout';
|
|
1149 };
|
|
1150
|
|
1151
|
|
1152 };
|