- 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) The Roundcube Dev Team
- 12 :
* Copyright (C) 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 = false; // roundcube mod.
- 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.use_ok_pic = false; // added by roundcube
- 78 :
this.show_spell_img = false; // roundcube mod.
- 79 :
this.decoration = true;
- 80 :
this.use_close_btn = false;
- 81 :
this.edit_layer_dbl_click = true;
- 82 :
this.report_ta_not_found = true;
- 83 :
- 84 :
// Extensions
- 85 :
this.custom_ajax_error = null;
- 86 :
this.custom_no_spelling_error = null;
- 87 :
this.extra_menu_items = [];
- 88 :
this.custom_spellcheck_starter = null;
- 89 :
this.main_controller = true;
- 90 :
this.has_dictionary = has_dict;
- 91 :
- 92 :
// Observers
- 93 :
this.lang_state_observer = null;
- 94 :
this.spelling_state_observer = null;
- 95 :
this.show_menu_observer = null;
- 96 :
this.all_errors_fixed_observer = null;
- 97 :
- 98 :
// Focus links - used to give the text box focus
- 99 :
this.use_focus = false;
- 100 :
this.focus_link_t = null;
- 101 :
this.focus_link_b = null;
- 102 :
- 103 :
// Counters
- 104 :
this.cnt_errors = 0;
- 105 :
this.cnt_errors_fixed = 0;
- 106 :
- 107 :
// Set document's onclick to hide the language and error menu
- 108 :
$(document).click(function(e) {
- 109 :
var target = $(e.target);
- 110 :
if (target.attr('googie_action_btn') != '1' && ref.isErrorWindowShown())
- 111 :
ref.hideErrorWindow();
- 112 :
});
- 113 :
- 114 :
- 115 :
this.decorateTextarea = function(id)
- 116 :
{
- 117 :
this.text_area = typeof id === 'string' ? document.getElementById(id) : id;
- 118 :
- 119 :
if (this.text_area) {
- 120 :
if (!this.spell_container && this.decoration) {
- 121 :
var table = document.createElement('table'),
- 122 :
tbody = document.createElement('tbody'),
- 123 :
tr = document.createElement('tr'),
- 124 :
spell_container = document.createElement('td'),
- 125 :
r_width = this.isDefined(this.force_width) ? this.force_width : this.text_area.offsetWidth,
- 126 :
r_height = this.isDefined(this.force_height) ? this.force_height : 16;
- 127 :
- 128 :
tr.appendChild(spell_container);
- 129 :
tbody.appendChild(tr);
- 130 :
$(table).append(tbody).insertBefore(this.text_area).width('100%').height(r_height);
- 131 :
$(spell_container).height(r_height).width(r_width).css('text-align', 'right');
- 132 :
- 133 :
this.spell_container = spell_container;
- 134 :
}
- 135 :
- 136 :
this.checkSpellingState();
- 137 :
}
- 138 :
else if (this.report_ta_not_found) {
- 139 :
rcmail.alert_dialog('Text area not found');
- 140 :
}
- 141 :
};
- 142 :
- 143 :
//////
- 144 :
// API Functions (the ones that you can call)
- 145 :
/////
- 146 :
this.setSpellContainer = function(id)
- 147 :
{
- 148 :
this.spell_container = typeof id === 'string' ? document.getElementById(id) : id;
- 149 :
};
- 150 :
- 151 :
this.setLanguages = function(lang_dict)
- 152 :
{
- 153 :
this.lang_to_word = lang_dict;
- 154 :
this.langlist_codes = this.array_keys(lang_dict);
- 155 :
};
- 156 :
- 157 :
this.setCurrentLanguage = function(lan_code)
- 158 :
{
- 159 :
GOOGIE_CUR_LANG = lan_code;
- 160 :
- 161 :
//Set cookie
- 162 :
rcmail.set_cookie('language', lan_code, false);
- 163 :
};
- 164 :
- 165 :
this.setForceWidthHeight = function(width, height)
- 166 :
{
- 167 :
// Set to null if you want to use one of them
- 168 :
this.force_width = width;
- 169 :
this.force_height = height;
- 170 :
};
- 171 :
- 172 :
this.setDecoration = function(bool)
- 173 :
{
- 174 :
this.decoration = bool;
- 175 :
};
- 176 :
- 177 :
this.dontUseCloseButtons = function()
- 178 :
{
- 179 :
this.use_close_btn = false;
- 180 :
};
- 181 :
- 182 :
this.appendNewMenuItem = function(name, call_back_fn, checker)
- 183 :
{
- 184 :
this.extra_menu_items.push([name, call_back_fn, checker]);
- 185 :
};
- 186 :
- 187 :
this.setFocus = function()
- 188 :
{
- 189 :
try {
- 190 :
this.focus_link_b.focus();
- 191 :
this.focus_link_t.focus();
- 192 :
return true;
- 193 :
}
- 194 :
catch(e) {
- 195 :
return false;
- 196 :
}
- 197 :
};
- 198 :
- 199 :
- 200 :
//////
- 201 :
// Set functions (internal)
- 202 :
/////
- 203 :
this.setStateChanged = function(current_state)
- 204 :
{
- 205 :
this.state = current_state;
- 206 :
if (this.spelling_state_observer != null && this.report_state_change)
- 207 :
this.spelling_state_observer(current_state, this);
- 208 :
};
- 209 :
- 210 :
this.setReportStateChange = function(bool)
- 211 :
{
- 212 :
this.report_state_change = bool;
- 213 :
};
- 214 :
- 215 :
- 216 :
//////
- 217 :
// Request functions
- 218 :
/////
- 219 :
this.getUrl = function()
- 220 :
{
- 221 :
return this.server_url + GOOGIE_CUR_LANG;
- 222 :
};
- 223 :
- 224 :
this.escapeSpecial = function(val)
- 225 :
{
- 226 :
return val ? val.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") : '';
- 227 :
};
- 228 :
- 229 :
this.createXMLReq = function (text)
- 230 :
{
- 231 :
return '<?xml version="1.0" encoding="utf-8" ?>'
- 232 :
+ '<spellrequest textalreadyclipped="0" ignoredups="0" ignoredigits="1" ignoreallcaps="1">'
- 233 :
+ '<text>' + text + '</text></spellrequest>';
- 234 :
};
- 235 :
- 236 :
this.spellCheck = function(ignore)
- 237 :
{
- 238 :
this.prepare(ignore);
- 239 :
- 240 :
var req_text = this.escapeSpecial(this.original_text),
- 241 :
ref = this;
- 242 :
- 243 :
$.ajax({ type: 'POST', url: this.getUrl(), data: this.createXMLReq(req_text), dataType: 'text',
- 244 :
error: function(o) {
- 245 :
if (ref.custom_ajax_error) {
- 246 :
ref.custom_ajax_error(ref);
- 247 :
}
- 248 :
else {
- 249 :
rcmail.alert_dialog('An error was encountered on the server. Please try again later.');
- 250 :
}
- 251 :
if (ref.main_controller) {
- 252 :
$(ref.spell_span).remove();
- 253 :
ref.removeIndicator();
- 254 :
}
- 255 :
ref.checkSpellingState();
- 256 :
},
- 257 :
success: function(data) {
- 258 :
ref.processData(data);
- 259 :
if (!ref.results.length) {
- 260 :
if (!ref.custom_no_spelling_error)
- 261 :
ref.flashNoSpellingErrorState();
- 262 :
else
- 263 :
ref.custom_no_spelling_error(ref);
- 264 :
}
- 265 :
ref.removeIndicator();
- 266 :
}
- 267 :
});
- 268 :
};
- 269 :
- 270 :
this.learnWord = function(word, id)
- 271 :
{
- 272 :
word = this.escapeSpecial(word.innerHTML);
- 273 :
- 274 :
var ref = this,
- 275 :
req_text = '<?xml version="1.0" encoding="utf-8" ?><learnword><text>' + word + '</text></learnword>';
- 276 :
- 277 :
$.ajax({ type: 'POST', url: this.getUrl(), data: req_text, dataType: 'text',
- 278 :
error: function(o) {
- 279 :
if (ref.custom_ajax_error) {
- 280 :
ref.custom_ajax_error(ref);
- 281 :
}
- 282 :
else {
- 283 :
rcmail.alert_dialog('An error was encountered on the server. Please try again later.');
- 284 :
}
- 285 :
},
- 286 :
success: function(data) {
- 287 :
}
- 288 :
});
- 289 :
};
- 290 :
- 291 :
- 292 :
//////
- 293 :
// Spell checking functions
- 294 :
/////
- 295 :
this.prepare = function(ignore, no_indicator)
- 296 :
{
- 297 :
this.cnt_errors_fixed = 0;
- 298 :
this.cnt_errors = 0;
- 299 :
this.setStateChanged('checking_spell');
- 300 :
this.original_text = '';
- 301 :
- 302 :
if (!no_indicator && this.main_controller)
- 303 :
this.appendIndicator(this.spell_span);
- 304 :
- 305 :
this.error_links = [];
- 306 :
this.ta_scroll_top = this.text_area.scrollTop;
- 307 :
this.ignore = ignore;
- 308 :
- 309 :
var area = $(this.text_area);
- 310 :
- 311 :
if (area.val() == '' || ignore) {
- 312 :
if (!this.custom_no_spelling_error)
- 313 :
this.flashNoSpellingErrorState();
- 314 :
else
- 315 :
this.custom_no_spelling_error(this);
- 316 :
this.removeIndicator();
- 317 :
return;
- 318 :
}
- 319 :
- 320 :
var height = $(area).css('box-sizing') == 'border-box' ? this.text_area.offsetHeight : $(area).height();
- 321 :
- 322 :
this.createEditLayer(area.width(), height);
- 323 :
this.createErrorWindow();
- 324 :
- 325 :
$('body').append(this.error_window);
- 326 :
- 327 :
if (this.main_controller)
- 328 :
$(this.spell_span).off('click');
- 329 :
- 330 :
this.original_text = area.val();
- 331 :
};
- 332 :
- 333 :
this.parseResult = function(r_text)
- 334 :
{
- 335 :
// Returns an array: result[item] -> ['attrs'], ['suggestions']
- 336 :
var re_split_attr_c = /\w+="(\d+|true)"/g,
- 337 :
re_split_text = /\t/g,
- 338 :
matched_c = r_text.match(/<c[^>]*>[^<]*<\/c>/g),
- 339 :
results = [];
- 340 :
- 341 :
if (matched_c == null)
- 342 :
return results;
- 343 :
- 344 :
for (var i=0, len=matched_c.length; i < len; i++) {
- 345 :
var item = [];
- 346 :
this.errorFound();
- 347 :
- 348 :
// Get attributes
- 349 :
item['attrs'] = [];
- 350 :
var c_attr, val,
- 351 :
split_c = matched_c[i].match(re_split_attr_c);
- 352 :
for (var j=0; j < split_c.length; j++) {
- 353 :
c_attr = split_c[j].split(/=/);
- 354 :
val = c_attr[1].replace(/"/g, '');
- 355 :
item['attrs'][c_attr[0]] = val != 'true' ? parseInt(val) : val;
- 356 :
}
- 357 :
- 358 :
// Get suggestions
- 359 :
item['suggestions'] = [];
- 360 :
var only_text = matched_c[i].replace(/<[^>]*>/g, ''),
- 361 :
split_t = only_text.split(re_split_text);
- 362 :
for (var k=0; k < split_t.length; k++) {
- 363 :
if(split_t[k] != '')
- 364 :
item['suggestions'].push(split_t[k]);
- 365 :
}
- 366 :
results.push(item);
- 367 :
}
- 368 :
- 369 :
return results;
- 370 :
};
- 371 :
- 372 :
this.processData = function(data)
- 373 :
{
- 374 :
this.results = this.parseResult(data);
- 375 :
if (this.results.length) {
- 376 :
this.showErrorsInIframe();
- 377 :
this.resumeEditingState();
- 378 :
}
- 379 :
};
- 380 :
- 381 :
//////
- 382 :
// Error menu functions
- 383 :
/////
- 384 :
this.createErrorWindow = function()
- 385 :
{
- 386 :
this.error_window = document.createElement('div');
- 387 :
$(this.error_window).addClass('googie_window popupmenu').attr('googie_action_btn', '1');
- 388 :
};
- 389 :
- 390 :
this.isErrorWindowShown = function()
- 391 :
{
- 392 :
return $(this.error_window).is(':visible');
- 393 :
};
- 394 :
- 395 :
this.hideErrorWindow = function()
- 396 :
{
- 397 :
$(this.error_window).hide();
- 398 :
$(this.error_window_iframe).hide();
- 399 :
};
- 400 :
- 401 :
this.updateOriginalText = function(offset, old_value, new_value, id)
- 402 :
{
- 403 :
var part_1 = this.original_text.substring(0, offset),
- 404 :
part_2 = this.original_text.substring(offset+old_value.length),
- 405 :
add_2_offset = new_value.length - old_value.length;
- 406 :
- 407 :
this.original_text = part_1 + new_value + part_2;
- 408 :
$(this.text_area).val(this.original_text);
- 409 :
for (var j=0, len=this.results.length; j<len; j++) {
- 410 :
// Don't edit the offset of the current item
- 411 :
if (j != id && j > id)
- 412 :
this.results[j]['attrs']['o'] += add_2_offset;
- 413 :
}
- 414 :
};
- 415 :
- 416 :
this.saveOldValue = function(elm, old_value) {
- 417 :
elm.is_changed = true;
- 418 :
elm.old_value = old_value;
- 419 :
};
- 420 :
- 421 :
this.createListSeparator = function()
- 422 :
{
- 423 :
return $('<li>').html(' ').attr('googie_action_btn', '1')
- 424 :
.css({'cursor': 'default', 'font-size': '3px', 'border-top': '1px solid #ccc', 'padding-top': '3px'})
- 425 :
.get(0);
- 426 :
};
- 427 :
- 428 :
this.correctError = function(id, elm, l_elm, rm_pre_space)
- 429 :
{
- 430 :
var old_value = elm.innerHTML,
- 431 :
new_value = l_elm.nodeType == 3 ? l_elm.nodeValue : l_elm.innerHTML,
- 432 :
offset = this.results[id]['attrs']['o'];
- 433 :
- 434 :
if (rm_pre_space) {
- 435 :
var pre_length = elm.previousSibling.innerHTML;
- 436 :
elm.previousSibling.innerHTML = pre_length.slice(0, pre_length.length-1);
- 437 :
old_value = " " + old_value;
- 438 :
offset--;
- 439 :
}
- 440 :
- 441 :
this.hideErrorWindow();
- 442 :
this.updateOriginalText(offset, old_value, new_value, id);
- 443 :
- 444 :
$(elm).html(new_value).css('color', 'green').attr('is_corrected', true);
- 445 :
- 446 :
this.results[id]['attrs']['l'] = new_value.length;
- 447 :
- 448 :
if (!this.isDefined(elm.old_value))
- 449 :
this.saveOldValue(elm, old_value);
- 450 :
- 451 :
this.errorFixed();
- 452 :
};
- 453 :
- 454 :
this.ignoreError = function(elm, id)
- 455 :
{
- 456 :
// @TODO: ignore all same words
- 457 :
$(elm).removeAttr('class').css('color', '').off();
- 458 :
this.hideErrorWindow();
- 459 :
};
- 460 :
- 461 :
this.showErrorWindow = function(elm, id)
- 462 :
{
- 463 :
if (this.show_menu_observer)
- 464 :
this.show_menu_observer(this);
- 465 :
- 466 :
var ref = this,
- 467 :
pos = $(elm).offset(),
- 468 :
list = document.createElement('ul');
- 469 :
- 470 :
$(this.error_window).html('');
- 471 :
$(list).addClass('googie_list toolbarmenu').attr('googie_action_btn', '1');
- 472 :
- 473 :
// Build up the result list
- 474 :
var suggestions = this.results[id]['suggestions'],
- 475 :
offset = this.results[id]['attrs']['o'],
- 476 :
len = this.results[id]['attrs']['l'],
- 477 :
item, dummy;
- 478 :
- 479 :
// [Add to dictionary] button
- 480 :
if (this.has_dictionary && !$(elm).attr('is_corrected')) {
- 481 :
dummy = $('<a>').text(this.lang_learn_word).addClass('googie_add_to_dict active');
- 482 :
- 483 :
$('<li>').attr('googie_action_btn', '1').css('cursor', 'default')
- 484 :
.mouseover(ref.item_onmouseover)
- 485 :
.mouseout(ref.item_onmouseout)
- 486 :
.click(function(e) {
- 487 :
ref.learnWord(elm, id);
- 488 :
ref.ignoreError(elm, id);
- 489 :
})
- 490 :
.append(dummy)
- 491 :
.appendTo(list);
- 492 :
}
- 493 :
- 494 :
for (var i=0, len=suggestions.length; i < len; i++) {
- 495 :
dummy = $('<a>').html(suggestions[i]).addClass('active');
- 496 :
- 497 :
$('<li>').mouseover(this.item_onmouseover).mouseout(this.item_onmouseout)
- 498 :
.click(function(e) { ref.correctError(id, elm, e.target.firstChild); })
- 499 :
.append(dummy)
- 500 :
.appendTo(list);
- 501 :
}
- 502 :
- 503 :
// The element is changed, append the revert
- 504 :
if (elm.is_changed && elm.innerHTML != elm.old_value) {
- 505 :
var old_value = elm.old_value;
- 506 :
- 507 :
dummy = $('<a>').addClass('googie_list_revert active').html(this.lang_revert + ' ' + old_value);
- 508 :
- 509 :
$('<li>').mouseover(this.item_onmouseover).mouseout(this.item_onmouseout)
- 510 :
.click(function(e) {
- 511 :
ref.updateOriginalText(offset, elm.innerHTML, old_value, id);
- 512 :
$(elm).removeAttr('is_corrected').css('color', '#b91414').html(old_value);
- 513 :
ref.hideErrorWindow();
- 514 :
})
- 515 :
.append(dummy)
- 516 :
.appendTo(list);
- 517 :
}
- 518 :
- 519 :
// Append the edit box
- 520 :
var edit_row = document.createElement('li'),
- 521 :
edit_input = document.createElement('input'),
- 522 :
ok_pic = document.createElement('button'),
- 523 :
edit_form = document.createElement('form');
- 524 :
- 525 :
var onsub = function () {
- 526 :
if (edit_input.value != '') {
- 527 :
if (!ref.isDefined(elm.old_value))
- 528 :
ref.saveOldValue(elm, elm.innerHTML);
- 529 :
- 530 :
ref.updateOriginalText(offset, elm.innerHTML, edit_input.value, id);
- 531 :
$(elm).attr('is_corrected', true).css('color', 'green').text(edit_input.value);
- 532 :
ref.hideErrorWindow();
- 533 :
}
- 534 :
return false;
- 535 :
};
- 536 :
- 537 :
$(edit_input).width(120).val($(elm).text()).attr('googie_action_btn', '1');
- 538 :
$(edit_row).css('cursor', 'default').attr('googie_action_btn', '1')
- 539 :
.on('click', function() { return false; });
- 540 :
- 541 :
// roundcube modified image use
- 542 :
if (this.use_ok_pic) {
- 543 :
$('<img>').attr('src', this.img_dir + 'ok.gif')
- 544 :
.width(32).height(16)
- 545 :
.css({cursor: 'pointer', 'margin-left': '2px', 'margin-right': '2px'})
- 546 :
.appendTo(ok_pic);
- 547 :
}
- 548 :
else {
- 549 :
$(ok_pic).text('OK');
- 550 :
}
- 551 :
- 552 :
$(ok_pic).addClass('mainaction save googie_ok_button btn-sm').click(onsub);
- 553 :
- 554 :
$(edit_form).attr('googie_action_btn', '1')
- 555 :
.css({'cursor': 'default', 'white-space': 'nowrap'})
- 556 :
.submit(onsub)
- 557 :
.append(edit_input)
- 558 :
.append(ok_pic)
- 559 :
.appendTo(edit_row);
- 560 :
- 561 :
list.appendChild(edit_row);
- 562 :
- 563 :
// Append extra menu items
- 564 :
if (this.extra_menu_items.length > 0)
- 565 :
list.appendChild(this.createListSeparator());
- 566 :
- 567 :
var loop = function(i) {
- 568 :
if (i < ref.extra_menu_items.length) {
- 569 :
var e_elm = ref.extra_menu_items[i];
- 570 :
- 571 :
if (!e_elm[2] || e_elm[2](elm, ref)) {
- 572 :
var e_row = document.createElement('tr'),
- 573 :
e_col = document.createElement('td');
- 574 :
- 575 :
$(e_col).html(e_elm[0])
- 576 :
.mouseover(ref.item_onmouseover)
- 577 :
.mouseout(ref.item_onmouseout)
- 578 :
.click(function() { return e_elm[1](elm, ref) });
- 579 :
- 580 :
e_row.appendChild(e_col);
- 581 :
list.appendChild(e_row);
- 582 :
}
- 583 :
loop(i+1);
- 584 :
}
- 585 :
};
- 586 :
- 587 :
loop(0);
- 588 :
loop = null;
- 589 :
- 590 :
//Close button
- 591 :
if (this.use_close_btn) {
- 592 :
list.appendChild(this.createCloseButton(this.hideErrorWindow));
- 593 :
}
- 594 :
- 595 :
this.error_window.appendChild(list);
- 596 :
- 597 :
// roundcube plugin api hook
- 598 :
rcmail.triggerEvent('googiespell_create', {obj: this.error_window});
- 599 :
- 600 :
// calculate and set position
- 601 :
var height = $(this.error_window).height(),
- 602 :
width = $(this.error_window).width(),
- 603 :
pageheight = $(document).height(),
- 604 :
pagewidth = $(document).width(),
- 605 :
top = pos.top + height + 20 < pageheight ? pos.top + 20 : pos.top - height,
- 606 :
left = pos.left + width < pagewidth ? pos.left : pos.left - width;
- 607 :
- 608 :
if (left < 0) left = 0;
- 609 :
if (top < 0) top = 0;
- 610 :
- 611 :
$(this.error_window).css({'top': top+'px', 'left': left+'px', position: 'absolute'}).show();
- 612 :
- 613 :
// Dummy for IE - dropdown bug fix
- 614 :
if (document.all && !window.opera) {
- 615 :
if (!this.error_window_iframe) {
- 616 :
var iframe = $('<iframe>').css({'position': 'absolute', 'z-index': -1});
- 617 :
$('body').append(iframe);
- 618 :
this.error_window_iframe = iframe;
- 619 :
}
- 620 :
- 621 :
$(this.error_window_iframe)
- 622 :
.css({'top': this.error_window.offsetTop, 'left': this.error_window.offsetLeft,
- 623 :
'width': this.error_window.offsetWidth, 'height': this.error_window.offsetHeight})
- 624 :
.show();
- 625 :
}
- 626 :
};
- 627 :
- 628 :
- 629 :
//////
- 630 :
// Edit layer (the layer where the suggestions are stored)
- 631 :
//////
- 632 :
this.createEditLayer = function(width, height)
- 633 :
{
- 634 :
this.edit_layer = document.createElement('div');
- 635 :
$(this.edit_layer).addClass('googie_edit_layer').attr('id', 'googie_edit_layer')
- 636 :
.width(width).height(height);
- 637 :
- 638 :
if (this.text_area.nodeName.toLowerCase() != 'input' || $(this.text_area).val() == '') {
- 639 :
$(this.edit_layer).css('overflow', 'auto');
- 640 :
} else {
- 641 :
$(this.edit_layer).css('overflow', 'hidden');
- 642 :
}
- 643 :
- 644 :
var ref = this;
- 645 :
- 646 :
if (this.edit_layer_dbl_click) {
- 647 :
$(this.edit_layer).dblclick(function(e) {
- 648 :
if (e.target.className != 'googie_link' && !ref.isErrorWindowShown()) {
- 649 :
ref.resumeEditing();
- 650 :
var fn1 = function() {
- 651 :
$(ref.text_area).focus();
- 652 :
fn1 = null;
- 653 :
};
- 654 :
setTimeout(fn1, 10);
- 655 :
}
- 656 :
return false;
- 657 :
});
- 658 :
}
- 659 :
};
- 660 :
- 661 :
this.resumeEditing = function()
- 662 :
{
- 663 :
this.setStateChanged('ready');
- 664 :
- 665 :
if (this.edit_layer)
- 666 :
this.el_scroll_top = this.edit_layer.scrollTop;
- 667 :
- 668 :
this.hideErrorWindow();
- 669 :
- 670 :
if (this.main_controller)
- 671 :
$(this.spell_span).removeClass().addClass('googie_no_style');
- 672 :
- 673 :
if (!this.ignore) {
- 674 :
if (this.use_focus) {
- 675 :
$(this.focus_link_t).remove();
- 676 :
$(this.focus_link_b).remove();
- 677 :
}
- 678 :
- 679 :
$(this.edit_layer).remove();
- 680 :
$(this.text_area).show();
- 681 :
- 682 :
if (this.el_scroll_top != undefined)
- 683 :
this.text_area.scrollTop = this.el_scroll_top;
- 684 :
}
- 685 :
this.checkSpellingState(false);
- 686 :
};
- 687 :
- 688 :
this.createErrorLink = function(text, id)
- 689 :
{
- 690 :
var elm = document.createElement('span'),
- 691 :
ref = this,
- 692 :
d = function (e) {
- 693 :
ref.showErrorWindow(elm, id);
- 694 :
d = null;
- 695 :
return false;
- 696 :
};
- 697 :
- 698 :
$(elm).html(text).addClass('googie_link').click(d).removeAttr('is_corrected')
- 699 :
.attr({'googie_action_btn' : '1', 'g_id' : id});
- 700 :
- 701 :
return elm;
- 702 :
};
- 703 :
- 704 :
this.createPart = function(txt_part)
- 705 :
{
- 706 :
if (txt_part == " ")
- 707 :
return document.createTextNode(" ");
- 708 :
- 709 :
txt_part = this.escapeSpecial(txt_part);
- 710 :
txt_part = txt_part.replace(/\n/g, "<br>");
- 711 :
txt_part = txt_part.replace(/ /g, " ");
- 712 :
txt_part = txt_part.replace(/^ /g, " ");
- 713 :
txt_part = txt_part.replace(/ $/g, " ");
- 714 :
- 715 :
var span = document.createElement('span');
- 716 :
$(span).html(txt_part);
- 717 :
return span;
- 718 :
};
- 719 :
- 720 :
this.showErrorsInIframe = function()
- 721 :
{
- 722 :
var output = document.createElement('div'),
- 723 :
pointer = 0,
- 724 :
results = this.results;
- 725 :
- 726 :
if (results.length > 0) {
- 727 :
for (var i=0, length=results.length; i < length; i++) {
- 728 :
var offset = results[i]['attrs']['o'],
- 729 :
len = results[i]['attrs']['l'],
- 730 :
part_1_text = this.original_text.substring(pointer, offset),
- 731 :
part_1 = this.createPart(part_1_text);
- 732 :
- 733 :
output.appendChild(part_1);
- 734 :
pointer += offset - pointer;
- 735 :
- 736 :
// If the last child was an error, then insert some space
- 737 :
var err_link = this.createErrorLink(this.original_text.substr(offset, len), i);
- 738 :
this.error_links.push(err_link);
- 739 :
output.appendChild(err_link);
- 740 :
pointer += len;
- 741 :
}
- 742 :
- 743 :
// Insert the rest of the original text
- 744 :
var part_2_text = this.original_text.substr(pointer, this.original_text.length),
- 745 :
part_2 = this.createPart(part_2_text);
- 746 :
- 747 :
output.appendChild(part_2);
- 748 :
}
- 749 :
else
- 750 :
output.innerHTML = this.original_text;
- 751 :
- 752 :
$(output).css('text-align', 'left');
- 753 :
- 754 :
var me = this;
- 755 :
if (this.custom_item_evaluator)
- 756 :
$.map(this.error_links, function(elm){me.custom_item_evaluator(me, elm)});
- 757 :
- 758 :
$(this.edit_layer).append(output);
- 759 :
- 760 :
// Hide text area and show edit layer
- 761 :
$(this.text_area).hide();
- 762 :
$(this.edit_layer).insertBefore(this.text_area);
- 763 :
- 764 :
if (this.use_focus) {
- 765 :
this.focus_link_t = this.createFocusLink('focus_t');
- 766 :
this.focus_link_b = this.createFocusLink('focus_b');
- 767 :
- 768 :
$(this.focus_link_t).insertBefore(this.edit_layer);
- 769 :
$(this.focus_link_b).insertAfter(this.edit_layer);
- 770 :
}
- 771 :
- 772 :
// this.edit_layer.scrollTop = this.ta_scroll_top;
- 773 :
};
- 774 :
- 775 :
this.deHighlightCurSel = function()
- 776 :
{
- 777 :
$(this.lang_cur_elm).removeClass().addClass('googie_list_onout');
- 778 :
};
- 779 :
- 780 :
this.highlightCurSel = function()
- 781 :
{
- 782 :
if (GOOGIE_CUR_LANG == null)
- 783 :
GOOGIE_CUR_LANG = GOOGIE_DEFAULT_LANG;
- 784 :
for (var i=0; i < this.lang_elms.length; i++) {
- 785 :
if ($(this.lang_elms[i]).attr('googieId') == GOOGIE_CUR_LANG) {
- 786 :
this.lang_elms[i].className = 'googie_list_selected';
- 787 :
this.lang_cur_elm = this.lang_elms[i];
- 788 :
}
- 789 :
else {
- 790 :
this.lang_elms[i].className = 'googie_list_onout';
- 791 :
}
- 792 :
}
- 793 :
};
- 794 :
- 795 :
this.createSpellDiv = function()
- 796 :
{
- 797 :
var span = document.createElement('span');
- 798 :
- 799 :
$(span).addClass('googie_check_spelling_link').text(this.lang_chck_spell);
- 800 :
- 801 :
if (this.show_spell_img) {
- 802 :
$(span).append(' ').append($('<img>').attr('src', this.img_dir + 'spellc.gif'));
- 803 :
}
- 804 :
return span;
- 805 :
};
- 806 :
- 807 :
- 808 :
//////
- 809 :
// State functions
- 810 :
/////
- 811 :
this.flashNoSpellingErrorState = function(on_finish)
- 812 :
{
- 813 :
this.setStateChanged('no_error_found');
- 814 :
- 815 :
var ref = this;
- 816 :
if (this.main_controller) {
- 817 :
var no_spell_errors;
- 818 :
if (on_finish) {
- 819 :
var fn = function() {
- 820 :
on_finish();
- 821 :
ref.checkSpellingState();
- 822 :
};
- 823 :
no_spell_errors = fn;
- 824 :
}
- 825 :
else
- 826 :
no_spell_errors = function () { ref.checkSpellingState() };
- 827 :
- 828 :
var rsm = $('<span>').text(this.lang_no_error_found);
- 829 :
- 830 :
$(this.switch_lan_pic).hide();
- 831 :
$(this.spell_span).empty().append(rsm)
- 832 :
.removeClass().addClass('googie_check_spelling_ok');
- 833 :
- 834 :
setTimeout(no_spell_errors, 1000);
- 835 :
}
- 836 :
};
- 837 :
- 838 :
this.resumeEditingState = function()
- 839 :
{
- 840 :
this.setStateChanged('resume_editing');
- 841 :
- 842 :
//Change link text to resume
- 843 :
if (this.main_controller) {
- 844 :
var rsm = $('<span>').text(this.lang_rsm_edt);
- 845 :
var ref = this;
- 846 :
- 847 :
$(this.switch_lan_pic).hide();
- 848 :
$(this.spell_span).empty().off().append(rsm)
- 849 :
.click(function() { ref.resumeEditing(); })
- 850 :
.removeClass().addClass('googie_resume_editing');
- 851 :
}
- 852 :
- 853 :
try { this.edit_layer.scrollTop = this.ta_scroll_top; }
- 854 :
catch (e) {};
- 855 :
};
- 856 :
- 857 :
this.checkSpellingState = function(fire)
- 858 :
{
- 859 :
if (fire)
- 860 :
this.setStateChanged('ready');
- 861 :
- 862 :
this.switch_lan_pic = document.createElement('span');
- 863 :
- 864 :
var span_chck = this.createSpellDiv(),
- 865 :
ref = this;
- 866 :
- 867 :
if (this.custom_spellcheck_starter)
- 868 :
$(span_chck).click(function(e) { ref.custom_spellcheck_starter(); });
- 869 :
else {
- 870 :
$(span_chck).click(function(e) { ref.spellCheck(); });
- 871 :
}
- 872 :
- 873 :
if (this.main_controller) {
- 874 :
if (this.change_lang_pic_placement == 'left') {
- 875 :
$(this.spell_container).empty().append(this.switch_lan_pic).append(' ').append(span_chck);
- 876 :
} else {
- 877 :
$(this.spell_container).empty().append(span_chck).append(' ').append(this.switch_lan_pic);
- 878 :
}
- 879 :
}
- 880 :
- 881 :
this.spell_span = span_chck;
- 882 :
};
- 883 :
- 884 :
- 885 :
//////
- 886 :
// Misc. functions
- 887 :
/////
- 888 :
this.isDefined = function(o)
- 889 :
{
- 890 :
return (o !== undefined && o !== null)
- 891 :
};
- 892 :
- 893 :
this.errorFixed = function()
- 894 :
{
- 895 :
this.cnt_errors_fixed++;
- 896 :
if (this.all_errors_fixed_observer)
- 897 :
if (this.cnt_errors_fixed == this.cnt_errors) {
- 898 :
this.hideErrorWindow();
- 899 :
this.all_errors_fixed_observer();
- 900 :
}
- 901 :
};
- 902 :
- 903 :
this.errorFound = function()
- 904 :
{
- 905 :
this.cnt_errors++;
- 906 :
};
- 907 :
- 908 :
this.createCloseButton = function(c_fn)
- 909 :
{
- 910 :
return this.createButton(this.lang_close, 'googie_list_close', c_fn);
- 911 :
};
- 912 :
- 913 :
this.createButton = function(name, css_class, c_fn)
- 914 :
{
- 915 :
var btn_row = document.createElement('tr'),
- 916 :
btn = document.createElement('td'),
- 917 :
spn_btn;
- 918 :
- 919 :
if (css_class) {
- 920 :
spn_btn = document.createElement('span');
- 921 :
$(spn_btn).addClass(css_class).html(name);
- 922 :
} else {
- 923 :
spn_btn = document.createTextNode(name);
- 924 :
}
- 925 :
- 926 :
$(btn).click(c_fn)
- 927 :
.mouseover(this.item_onmouseover)
- 928 :
.mouseout(this.item_onmouseout);
- 929 :
- 930 :
btn.appendChild(spn_btn);
- 931 :
btn_row.appendChild(btn);
- 932 :
- 933 :
return btn_row;
- 934 :
};
- 935 :
- 936 :
this.removeIndicator = function(elm)
- 937 :
{
- 938 :
//$(this.indicator).remove();
- 939 :
// roundcube mod.
- 940 :
if (window.rcmail)
- 941 :
rcmail.set_busy(false, null, this.rc_msg_id);
- 942 :
};
- 943 :
- 944 :
this.appendIndicator = function(elm)
- 945 :
{
- 946 :
// modified by roundcube
- 947 :
if (window.rcmail)
- 948 :
this.rc_msg_id = rcmail.set_busy(true, 'checking');
- 949 :
/*
- 950 :
this.indicator = document.createElement('img');
- 951 :
$(this.indicator).attr('src', this.img_dir + 'indicator.gif')
- 952 :
.css({'margin-right': '5px', 'text-decoration': 'none'}).width(16).height(16);
- 953 :
- 954 :
if (elm)
- 955 :
$(this.indicator).insertBefore(elm);
- 956 :
else
- 957 :
$('body').append(this.indicator);
- 958 :
*/
- 959 :
}
- 960 :
- 961 :
this.createFocusLink = function(name)
- 962 :
{
- 963 :
var link = document.createElement('a');
- 964 :
$(link).attr({'href': 'javascript:;', 'name': name});
- 965 :
return link;
- 966 :
};
- 967 :
- 968 :
this.item_onmouseover = function(e)
- 969 :
{
- 970 :
if (this.className != 'googie_list_revert' && this.className != 'googie_list_close')
- 971 :
this.className = 'googie_list_onhover';
- 972 :
else
- 973 :
this.parentNode.className = 'googie_list_onhover';
- 974 :
};
- 975 :
- 976 :
this.item_onmouseout = function(e)
- 977 :
{
- 978 :
if (this.className != 'googie_list_revert' && this.className != 'googie_list_close')
- 979 :
this.className = 'googie_list_onout';
- 980 :
else
- 981 :
this.parentNode.className = 'googie_list_onout';
- 982 :
};
- 983 :
- 984 :
- 985 :
};