alecpl
2009-05-01 309d2f40a037a158db596068b7fb6799f94018c2
- jQuery'fied GoogieSpell 4.0 + some changes in compose


8 files modified
2701 ■■■■ changed files
program/js/app.js 10 ●●●● patch | view | raw | blame | history
program/js/common.js 17 ●●●●● patch | view | raw | blame | history
program/js/googiespell.js 2577 ●●●● patch | view | raw | blame | history
program/steps/mail/compose.inc 24 ●●●●● patch | view | raw | blame | history
skins/default/common.css 5 ●●●●● patch | view | raw | blame | history
skins/default/googiespell.css 31 ●●●● patch | view | raw | blame | history
skins/default/mail.css 21 ●●●● patch | view | raw | blame | history
skins/default/templates/compose.html 16 ●●●●● patch | view | raw | blame | history
program/js/app.js
@@ -903,6 +903,7 @@
        }
        else if (this.env.spellcheck && this.env.spellcheck.spellCheck && this.spellcheck_ready) {
          this.env.spellcheck.spellCheck();
          this.set_spellcheck_state('checking');
        }
        break;
@@ -2112,7 +2113,7 @@
  this.stop_spellchecking = function()
    {
    if (this.env.spellcheck && !this.spellcheck_ready) {
      exec_event(this.env.spellcheck.check_link, 'click');
      $(this.env.spellcheck.spell_span).trigger('click');
      this.set_spellcheck_state('ready');
      }
    };
@@ -2122,16 +2123,15 @@
    if (this.env.spellcheck) {
      // stop spellchecking process
      if (!vis)
        this.stop_spellchecking();
    this.stop_spellchecking();
      this.env.spellcheck.check_link.style.visibility = vis ? 'visible' : 'hidden';
      this.env.spellcheck.switch_lan_pic.style.visibility = vis ? 'visible' : 'hidden';
      $(this.env.spellcheck.spell_container).css('visibility', vis ? 'visible' : 'hidden');
      }
    };
  this.set_spellcheck_state = function(s)
    {
    this.spellcheck_ready = (s=='check_spelling' || s=='spell_check' || s=='ready');
    this.spellcheck_ready = (s == 'ready' || s == 'no_error_found');
    this.enable_command('spellcheck', this.spellcheck_ready);
    };
program/js/common.js
@@ -634,20 +634,3 @@
    return o;
    }
  }
// Fire event on specified element
function exec_event(element,event)
{
  if (document.createEventObject) {
    // dispatch for IE
    var evt = document.createEventObject();
    return element.fireEvent('on'+event,evt)
  }
  else {
    // dispatch for firefox + others
    var evt = document.createEvent("HTMLEvents");
    evt.initEvent(event, true, true); // event type,bubbling,cancelable
    return !element.dispatchEvent(evt);
   }
}
program/js/googiespell.js
@@ -1,1468 +1,43 @@
/*
Last Modified: 29/04/07 18:44:48
AJS JavaScript library
    A very small library with a lot of functionality
AUTHOR
    4mir Salihefendic (http://amix.dk) - amix@amix.dk
LICENSE
    Copyright (c) 2006 Amir Salihefendic. All rights reserved.
    Copyright (c) 2005 Bob Ippolito. All rights reserved.
    http://www.opensource.org/licenses/mit-license.php
VERSION
    4.0
SITE
    http://orangoo.com/AmiNation/AJS
**/
if(!AJS) {
var AJS = {
    BASE_URL: "",
    drag_obj: null,
    drag_elm: null,
    _drop_zones: [],
    _drag_zones: [],
    _cur_pos: null,
    ajaxErrorHandler: null,
////
// General accessor functions
////
    getQueryArgument: function(var_name) {
        var query = window.location.search.substring(1);
        var vars = query.split("&");
        for (var i=0;i<vars.length;i++) {
            var pair = vars[i].split("=");
            if (pair[0] == var_name) {
                return pair[1];
            }
        }
        return null;
    },
    isIe: function() {
        return (navigator.userAgent.toLowerCase().indexOf("msie") != -1 && navigator.userAgent.toLowerCase().indexOf("opera") == -1);
    },
    isNetscape7: function() {
        return (navigator.userAgent.toLowerCase().indexOf("netscape") != -1 && navigator.userAgent.toLowerCase().indexOf("7.") != -1);
    },
    isSafari: function() {
        return (navigator.userAgent.toLowerCase().indexOf("khtml") != -1);
    },
    isOpera: function() {
        return (navigator.userAgent.toLowerCase().indexOf("opera") != -1);
    },
    isMozilla: function() {
        return (navigator.userAgent.toLowerCase().indexOf("gecko") != -1 && navigator.productSub >= 20030210);
    },
    isMac: function() {
        return (navigator.userAgent.toLowerCase().indexOf('macintosh') != -1);
    },
////
// Array functions
////
    //Shortcut: AJS.$A
    createArray: function(v) {
        if(AJS.isArray(v) && !AJS.isString(v))
            return v;
        else if(!v)
            return [];
        else
            return [v];
    },
    forceArray: function(args) {
        var r = [];
        AJS.map(args, function(elm) {
            r.push(elm);
        });
        return r;
    },
    join: function(delim, list) {
        try {
            return list.join(delim);
        }
        catch(e) {
            var r = list[0] || '';
            AJS.map(list, function(elm) {
                r += delim + elm;
            }, 1);
            return r + '';
        }
    },
    isIn: function(elm, list) {
        var i = AJS.getIndex(elm, list);
        if(i != -1)
            return true;
        else
            return false;
    },
    getIndex: function(elm, list/*optional*/, eval_fn) {
        for(var i=0; i < list.length; i++)
            if(eval_fn && eval_fn(list[i]) || elm == list[i])
                return i;
        return -1;
    },
    getFirst: function(list) {
        if(list.length > 0)
            return list[0];
        else
            return null;
    },
    getLast: function(list) {
        if(list.length > 0)
            return list[list.length-1];
        else
            return null;
    },
    update: function(l1, l2) {
        for(var i in l2)
            l1[i] = l2[i];
        return l1;
    },
    flattenList: function(list) {
        var r = [];
        var _flatten = function(r, l) {
            AJS.map(l, function(o) {
                if(o == null) {}
                else if (AJS.isArray(o))
                    _flatten(r, o);
                else
                    r.push(o);
            });
        }
        _flatten(r, list);
        return r;
    },
////
// Functional programming
////
    map: function(list, fn,/*optional*/ start_index, end_index) {
        var i = 0, l = list.length;
        if(start_index)
             i = start_index;
        if(end_index)
             l = end_index;
        for(i; i < l; i++) {
            var val = fn.apply(null, [list[i], i]);
            if(val != undefined)
                return val;
        }
    },
    rmap: function(list, fn) {
        var i = list.length-1, l = 0;
        for(i; i >= l; i--) {
            var val = fn.apply(null, [list[i], i]);
            if(val != undefined)
                return val;
        }
    },
    filter: function(list, fn, /*optional*/ start_index, end_index) {
        var r = [];
        AJS.map(list, function(elm) {
            if(fn(elm))
                r.push(elm);
        }, start_index, end_index);
        return r;
    },
    partial: function(fn) {
        var args = AJS.$FA(arguments);
        args.shift();
        return function() {
            args = args.concat(AJS.$FA(arguments));
            return fn.apply(window, args);
        }
    },
////
// DOM functions
////
    //Shortcut: AJS.$
    getElement: function(id) {
        if(AJS.isString(id) || AJS.isNumber(id))
            return document.getElementById(id);
        else
            return id;
    },
    //Shortcut: AJS.$$
    getElements: function(/*id1, id2, id3*/) {
        var args = AJS.forceArray(arguments);
        var elements = new Array();
            for (var i = 0; i < args.length; i++) {
                var element = AJS.getElement(args[i]);
                elements.push(element);
            }
            return elements;
    },
    //Shortcut: AJS.$bytc
    getElementsByTagAndClassName: function(tag_name, class_name, /*optional*/ parent) {
        var class_elements = [];
        if(!AJS.isDefined(parent))
            parent = document;
        if(!AJS.isDefined(tag_name))
            tag_name = '*';
        var els = parent.getElementsByTagName(tag_name);
        var els_len = els.length;
        var pattern = new RegExp("(^|\\s)" + class_name + "(\\s|$)");
        for (i = 0, j = 0; i < els_len; i++) {
            if ( pattern.test(els[i].className) || class_name == null ) {
                class_elements[j] = els[i];
                j++;
            }
        }
        return class_elements;
    },
    _nodeWalk: function(elm, tag_name, class_name, fn_next_elm) {
        var p = fn_next_elm(elm);
        var checkFn;
        if(tag_name && class_name) {
            checkFn = function(p) {
                return AJS.nodeName(p) == tag_name && AJS.hasClass(p, class_name);
            }
        }
        else if(tag_name) {
            checkFn = function(p) { return AJS.nodeName(p) == tag_name; }
        }
        else {
            checkFn = function(p) { return AJS.hasClass(p, class_name); }
        }
        while(p) {
            if(checkFn(p))
                return p;
            p = fn_next_elm(p);
        }
        return null;
    },
    getParentBytc: function(elm, tag_name, class_name) {
        return AJS._nodeWalk(elm, tag_name, class_name, function(m) { return m.parentNode; });
    },
    getPreviousSiblingBytc: function(elm, tag_name, class_name) {
        return AJS._nodeWalk(elm, tag_name, class_name, function(m) { return m.previousSibling; });
    },
    getNextSiblingBytc: function(elm, tag_name, class_name) {
        return AJS._nodeWalk(elm, tag_name, class_name, function(m) { return m.nextSibling; });
    },
    //Shortcut: AJS.$f
    getFormElement: function(form, name) {
        form = AJS.$(form);
        var r = null;
        AJS.map(form.elements, function(elm) {
            if(elm.name && elm.name == name)
                r = elm;
        });
        return r;
    },
    formContents: function(form) {
        var form = AJS.$(form);
        var r = {};
        var fn = function(elms) {
            AJS.map(elms, function(e) {
                if(e.name)
                    r[e.name] = e.value || '';
            });
        }
        fn(AJS.$bytc('input', null, form));
        fn(AJS.$bytc('textarea', null, form));
        return r;
    },
    getBody: function() {
        return AJS.$bytc('body')[0]
    },
    nodeName: function(elm) {
        return elm.nodeName.toLowerCase();
    },
    hasParent: function(elm, parent_to_consider, max_look_up) {
        if(elm == parent_to_consider)
            return true;
        if(max_look_up == 0)
            return false;
        return AJS.hasParent(elm.parentNode, parent_to_consider, max_look_up-1);
    },
    isElementHidden: function(elm) {
        return ((elm.style.display == "none") || (elm.style.visibility == "hidden"));
    },
    //Shortcut: AJS.DI
    documentInsert: function(elm) {
        if(typeof(elm) == 'string')
            elm = AJS.HTML2DOM(elm);
        document.write('<span id="dummy_holder"></span>');
        AJS.swapDOM(AJS.$('dummy_holder'), elm);
    },
    cloner: function(element) {
        return function() {
            return element.cloneNode(true);
        }
    },
    appendToTop: function(elm/*, elms...*/) {
        var args = AJS.forceArray(arguments).slice(1);
        if(args.length >= 1) {
            var first_child = elm.firstChild;
            if(first_child) {
                while(true) {
                    var t_elm = args.shift();
                    if(t_elm)
                        AJS.insertBefore(t_elm, first_child);
                    else
                        break;
                }
            }
            else {
                AJS.ACN.apply(null, arguments);
            }
        }
        return elm;
    },
    //Shortcut: AJS.ACN
    appendChildNodes: function(elm/*, elms...*/) {
        if(arguments.length >= 2) {
            AJS.map(arguments, function(n) {
                if(AJS.isString(n))
                    n = AJS.TN(n);
                if(AJS.isDefined(n))
                    elm.appendChild(n);
            }, 1);
        }
        return elm;
    },
    //Shortcut: AJS.RCN
    replaceChildNodes: function(elm/*, elms...*/) {
        var child;
        while ((child = elm.firstChild))
            elm.removeChild(child);
        if (arguments.length < 2)
            return elm;
        else
            return AJS.appendChildNodes.apply(null, arguments);
        return elm;
    },
    insertAfter: function(elm, reference_elm) {
        reference_elm.parentNode.insertBefore(elm, reference_elm.nextSibling);
        return elm;
    },
    insertBefore: function(elm, reference_elm) {
        reference_elm.parentNode.insertBefore(elm, reference_elm);
        return elm;
    },
    showElement: function(/*elms...*/) {
        var args = AJS.forceArray(arguments);
        AJS.map(args, function(elm) { elm.style.display = ''});
    },
    hideElement: function(elm) {
        var args = AJS.forceArray(arguments);
        AJS.map(args, function(elm) { elm.style.display = 'none'});
    },
    swapDOM: function(dest, src) {
        dest = AJS.getElement(dest);
        var parent = dest.parentNode;
        if (src) {
            src = AJS.getElement(src);
            parent.replaceChild(src, dest);
        } else {
            parent.removeChild(dest);
        }
        return src;
    },
    removeElement: function(/*elm1, elm2...*/) {
        var args = AJS.forceArray(arguments);
        AJS.map(args, function(elm) { AJS.swapDOM(elm, null); });
    },
    createDOM: function(name, attrs) {
        var i=0, attr;
        elm = document.createElement(name);
        if(AJS.isDict(attrs[i])) {
            for(k in attrs[0]) {
                attr = attrs[0][k];
                if(k == "style")
                    elm.style.cssText = attr;
                else if(k == "class" || k == 'className')
                    elm.className = attr;
                else {
                    elm.setAttribute(k, attr);
                }
            }
            i++;
        }
        if(attrs[0] == null)
            i = 1;
        AJS.map(attrs, function(n) {
            if(n) {
                if(AJS.isString(n) || AJS.isNumber(n))
                    n = AJS.TN(n);
                elm.appendChild(n);
            }
        }, i);
        return elm;
    },
    _createDomShortcuts: function() {
        var elms = [
                "ul", "li", "td", "tr", "th",
                "tbody", "table", "input", "span", "b",
                "a", "div", "img", "button", "h1",
                "h2", "h3", "br", "textarea", "form",
                "p", "select", "option", "optgroup", "iframe", "script",
                "center", "dl", "dt", "dd", "small",
                "pre"
        ];
        var extends_ajs = function(elm) {
            AJS[elm.toUpperCase()] = function() {
                return AJS.createDOM.apply(null, [elm, arguments]);
            };
        }
        AJS.map(elms, extends_ajs);
        AJS.TN = function(text) { return document.createTextNode(text) };
    },
    getCssDim: function(dim) {
        if(AJS.isString(dim))
            return dim;
        else
            return dim + "px";
    },
    getCssProperty: function(elm, prop) {
        elm = AJS.$(elm);
        var y;
        if(elm.currentStyle)
            y = elm.currentStyle[prop];
    else if (window.getComputedStyle)
            y = document.defaultView.getComputedStyle(elm,null).getPropertyValue(prop);
    return y;
    },
    setStyle: function(/*elm1, elm2..., property, new_value*/) {
        var args = AJS.forceArray(arguments);
        var new_val = args.pop();
        var property = args.pop();
        AJS.map(args, function(elm) {
            elm.style[property] = AJS.getCssDim(new_val);
        });
    },
    setWidth: function(/*elm1, elm2..., width*/) {
        var args = AJS.forceArray(arguments);
        args.splice(args.length-1, 0, 'width');
        AJS.setStyle.apply(null, args);
    },
    setHeight: function(/*elm1, elm2..., height*/) {
        var args = AJS.forceArray(arguments);
        args.splice(args.length-1, 0, 'height');
        AJS.setStyle.apply(null, args);
    },
    setLeft: function(/*elm1, elm2..., left*/) {
        var args = AJS.forceArray(arguments);
        args.splice(args.length-1, 0, 'left');
        AJS.setStyle.apply(null, args);
    },
    setTop: function(/*elm1, elm2..., top*/) {
        var args = AJS.forceArray(arguments);
        args.splice(args.length-1, 0, 'top');
        AJS.setStyle.apply(null, args);
    },
    setClass: function(/*elm1, elm2..., className*/) {
        var args = AJS.forceArray(arguments);
        var c = args.pop();
        AJS.map(args, function(elm) { elm.className = c});
    },
    addClass: function(/*elm1, elm2..., className*/) {
        var args = AJS.forceArray(arguments);
        var cls = args.pop();
        var add_class = function(o) {
            if(!new RegExp("(^|\\s)" + cls + "(\\s|$)").test(o.className))
                o.className += (o.className ? " " : "") + cls;
        };
        AJS.map(args, function(elm) { add_class(elm); });
    },
    hasClass: function(elm, cls) {
        if(!elm.className)
            return false;
        return elm.className == cls ||
               elm.className.search(new RegExp(" " + cls + "|^" + cls)) != -1
    },
    removeClass: function(/*elm1, elm2..., className*/) {
        var args = AJS.forceArray(arguments);
        var cls = args.pop();
        var rm_class = function(o) {
            o.className = o.className.replace(new RegExp("\\s?" + cls, 'g'), "");
        };
        AJS.map(args, function(elm) { rm_class(elm); });
    },
    setHTML: function(elm, html) {
        elm.innerHTML = html;
        return elm;
    },
    RND: function(tmpl, ns, scope) {
        scope = scope || window;
        var fn = function(w, g) {
            g = g.split("|");
            var cnt = ns[g[0]];
            for(var i=1; i < g.length; i++)
                cnt = scope[g[i]](cnt);
            if(cnt == '')
                return '';
            if(cnt == 0 || cnt == -1)
                cnt += '';
            return cnt || w;
        };
        return tmpl.replace(/%\(([A-Za-z0-9_|.]*)\)/g, fn);
    },
    HTML2DOM: function(html,/*optional*/ first_child) {
        var d = AJS.DIV();
        d.innerHTML = html;
        if(first_child)
            return d.childNodes[0];
        else
            return d;
    },
    preloadImages: function(/*img_src1, ..., img_srcN*/) {
        AJS.AEV(window, 'load', AJS.$p(function(args) {
            AJS.map(args, function(src) {
                var pic = new Image();
                pic.src = src;
            });
        }, arguments));
    },
////
// Effects
////
    setOpacity: function(elm, p) {
        elm.style.opacity = p;
        elm.style.filter = "alpha(opacity="+ p*100 +")";
    },
    resetOpacity: function(elm) {
        elm.style.opacity = 1;
        elm.style.filter = "";
    },
////
// Ajax functions
////
    getXMLHttpRequest: function() {
        var try_these = [
            function () { return new XMLHttpRequest(); },
            function () { return new ActiveXObject('Msxml2.XMLHTTP'); },
            function () { return new ActiveXObject('Microsoft.XMLHTTP'); },
            function () { return new ActiveXObject('Msxml2.XMLHTTP.4.0'); },
            function () { throw "Browser does not support XMLHttpRequest"; }
        ];
        for (var i = 0; i < try_these.length; i++) {
            var func = try_these[i];
            try {
                return func();
            } catch (e) {
            }
        }
    },
    getRequest: function(url, data, type) {
        if(!type)
            type = "POST";
        var req = AJS.getXMLHttpRequest();
        if(url.indexOf("http://") == -1) {
            if(AJS.BASE_URL != '') {
                if(AJS.BASE_URL.lastIndexOf('/') != AJS.BASE_URL.length-1)
                    AJS.BASE_URL += '/';
                url = AJS.BASE_URL + url;
            }
        }
        req.open(type, url, true);
        if(type == "POST")
            req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        return AJS._sendXMLHttpRequest(req);
    },
    _sendXMLHttpRequest: function(req, data) {
        var d = new AJSDeferred(req);
        var onreadystatechange = function () {
            if (req.readyState == 4) {
                var status = '';
                try {
                    status = req.status;
                }
                catch(e) {};
                if(status == 200 || status == 304 || req.responseText == null) {
                    d.callback();
                }
                else {
                    if(d.errbacks.length == 0) {
                        if(AJS.ajaxErrorHandler)
                            AJS.ajaxErrorHandler(req.responseText, req);
                    }
                    else
                        d.errback();
                }
            }
        }
        req.onreadystatechange = onreadystatechange;
        return d;
    },
    _reprString: function(o) {
        return ('"' + o.replace(/(["\\])/g, '\\$1') + '"'
        ).replace(/[\f]/g, "\\f"
        ).replace(/[\b]/g, "\\b"
        ).replace(/[\n]/g, "\\n"
        ).replace(/[\t]/g, "\\t"
        ).replace(/[\r]/g, "\\r");
    },
    _reprDate: function(db) {
        var year = db.getFullYear();
        var dd = db.getDate();
        var mm = db.getMonth()+1;
        var hh = db.getHours();
        var mins = db.getMinutes();
        function leadingZero(nr) {
            if (nr < 10) nr = "0" + nr;
            return nr;
        }
        if(hh == 24) hh = '00';
        var time = leadingZero(hh) + ':' + leadingZero(mins);
        return '"' + year + '-' + mm + '-' + dd + 'T' + time + '"';
    },
    serializeJSON: function(o) {
        var objtype = typeof(o);
        if (objtype == "undefined") {
            return "undefined";
        } else if (objtype == "number" || objtype == "boolean") {
            return o + "";
        } else if (o === null) {
            return "null";
        }
        if (objtype == "string") {
            return AJS._reprString(o);
        }
        if(objtype == 'object' && o.getFullYear) {
            return AJS._reprDate(o);
        }
        var me = arguments.callee;
        if (objtype != "function" && typeof(o.length) == "number") {
            var res = [];
            for (var i = 0; i < o.length; i++) {
                var val = me(o[i]);
                if (typeof(val) != "string") {
                    val = "undefined";
                }
                res.push(val);
            }
            return "[" + res.join(",") + "]";
        }
        // it's a function with no adapter, bad
        if (objtype == "function")
            return null;
        res = [];
        for (var k in o) {
            var useKey;
            if (typeof(k) == "number") {
                useKey = '"' + k + '"';
            } else if (typeof(k) == "string") {
                useKey = AJS._reprString(k);
            } else {
                // skip non-string or number keys
                continue;
            }
            val = me(o[k]);
            if (typeof(val) != "string") {
                // skip non-serializable values
                continue;
            }
            res.push(useKey + ":" + val);
        }
        return "{" + res.join(",") + "}";
    },
    loadJSONDoc: function(url) {
        var d = AJS.getRequest(url);
        var eval_req = function(data, req) {
            var text = req.responseText;
            if(text == "Error")
                d.errback(req);
            else
                return AJS.evalTxt(text);
        };
        d.addCallback(eval_req);
        return d;
    },
    evalTxt: function(txt) {
        try {
            return eval('('+ txt + ')');
        }
        catch(e) {
            return eval(txt);
        }
    },
    evalScriptTags: function(html) {
        var script_data = html.match(/<script.*?>((\n|\r|.)*?)<\/script>/g);
        if(script_data != null) {
            for(var i=0; i < script_data.length; i++) {
                var script_only = script_data[i].replace(/<script.*?>/g, "");
                script_only = script_only.replace(/<\/script>/g, "");
                eval(script_only);
            }
        }
    },
    queryArguments: function(data) {
        var post_data = [];
        for(k in data)
            post_data.push(k + "=" + AJS.urlencode(data[k]));
        return post_data.join("&");
    },
////
// Position and size
////
    getMousePos: function(e) {
        var posx = 0;
        var posy = 0;
        if (!e) var e = window.event;
        if (e.pageX || e.pageY) {
            posx = e.pageX;
            posy = e.pageY;
        }
        else if (e.clientX || e.clientY) {
            posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
            posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
        }
        return {x: posx, y: posy};
    },
    getScrollTop: function() {
        //From: http://www.quirksmode.org/js/doctypes.html
        var t;
        if (document.documentElement && document.documentElement.scrollTop)
                t = document.documentElement.scrollTop;
        else if (document.body)
                t = document.body.scrollTop;
        return t;
    },
    absolutePosition: function(elm) {
        var posObj = {'x': elm.offsetLeft, 'y': elm.offsetTop};
        if(elm.offsetParent) {
            var next = elm.offsetParent;
            while(next) {
                posObj.x += next.offsetLeft;
                posObj.y += next.offsetTop;
                next = next.offsetParent;
            }
        }
        // safari bug
        if (AJS.isSafari() && elm.style.position == 'absolute' ) {
            posObj.x -= document.body.offsetLeft;
            posObj.y -= document.body.offsetTop;
        }
        return posObj;
    },
    getWindowSize: function(doc) {
        doc = doc || document;
        var win_w, win_h;
        if (self.innerHeight) {
            win_w = self.innerWidth;
            win_h = self.innerHeight;
        } else if (doc.documentElement && doc.documentElement.clientHeight) {
            win_w = doc.documentElement.clientWidth;
            win_h = doc.documentElement.clientHeight;
        } else if (doc.body) {
            win_w = doc.body.clientWidth;
            win_h = doc.body.clientHeight;
        }
        return {'w': win_w, 'h': win_h};
    },
    isOverlapping: function(elm1, elm2) {
        var pos_elm1 = AJS.absolutePosition(elm1);
        var pos_elm2 = AJS.absolutePosition(elm2);
        var top1 = pos_elm1.y;
        var left1 = pos_elm1.x;
        var right1 = left1 + elm1.offsetWidth;
        var bottom1 = top1 + elm1.offsetHeight;
        var top2 = pos_elm2.y;
        var left2 = pos_elm2.x;
        var right2 = left2 + elm2.offsetWidth;
        var bottom2 = top2 + elm2.offsetHeight;
        var getSign = function(v) {
            if(v > 0) return "+";
            else if(v < 0) return "-";
            else return 0;
        }
        if ((getSign(top1 - bottom2) != getSign(bottom1 - top2)) &&
                (getSign(left1 - right2) != getSign(right1 - left2)))
            return true;
        return false;
    },
////
// Events
////
    getEventElm: function(e) {
        if(e && !e.type && !e.keyCode)
            return e
        var targ;
        if (!e) var e = window.event;
        if (e.target) targ = e.target;
        else if (e.srcElement) targ = e.srcElement;
        if (targ.nodeType == 3) // defeat Safari bug
            targ = targ.parentNode;
        return targ;
    },
    _getRealScope: function(fn, /*optional*/ extra_args) {
        extra_args = AJS.$A(extra_args);
        var scope = fn._cscope || window;
        return function() {
            var args = AJS.$FA(arguments).concat(extra_args);
            return fn.apply(scope, args);
        };
    },
    _unloadListeners: function() {
        if(AJS.listeners)
            AJS.map(AJS.listeners, function(elm, type, fn) { AJS.REV(elm, type, fn) });
        AJS.listeners = [];
    },
    setEventKey: function(e) {
        e.key = e.keyCode ? e.keyCode : e.charCode;
        if(window.event) {
            e.ctrl = window.event.ctrlKey;
            e.shift = window.event.shiftKey;
        }
        else {
            e.ctrl = e.ctrlKey;
            e.shift = e.shiftKey;
        }
        switch(e.key) {
            case 63232:
                e.key = 38;
                break;
            case 63233:
                e.key = 40;
                break;
            case 63235:
                e.key = 39;
                break;
            case 63234:
                e.key = 37;
                break;
        }
    },
    //Shortcut: AJS.AEV
    addEventListener: function(elm, type, fn, /*optional*/listen_once, cancle_bubble) {
        if(!cancle_bubble)
            cancle_bubble = false;
        var elms = AJS.$A(elm);
        AJS.map(elms, function(elmz) {
            if(listen_once)
                fn = AJS._listenOnce(elmz, type, fn);
            //Hack since it does not work in all browsers
            if(AJS.isIn(type, ['submit', 'load', 'scroll', 'resize'])) {
                var old = elm['on' + type];
                elm['on' + type] = function() {
                    if(old) {
                        fn(arguments);
                        return old(arguments);
                    }
                    else
                        return fn(arguments);
                };
                return;
            }
            //Fix keyCode
            if(AJS.isIn(type, ['keypress', 'keydown', 'keyup', 'click'])) {
                var old_fn = fn;
                fn = function(e) {
                    AJS.setEventKey(e);
                    return old_fn.apply(null, arguments);
                }
            }
            if (elmz.attachEvent) {
                //FIXME: We ignore cancle_bubble for IE... could be a problem?
                elmz.attachEvent("on" + type, fn);
            }
            else if(elmz.addEventListener)
                elmz.addEventListener(type, fn, cancle_bubble);
            AJS.listeners = AJS.$A(AJS.listeners);
            AJS.listeners.push([elmz, type, fn]);
        });
    },
    //Shortcut: AJS.REV
    removeEventListener: function(elm, type, fn, /*optional*/cancle_bubble) {
        if(!cancle_bubble)
            cancle_bubble = false;
        if(elm.removeEventListener) {
            elm.removeEventListener(type, fn, cancle_bubble);
            if(AJS.isOpera())
                elm.removeEventListener(type, fn, !cancle_bubble);
        }
        else if(elm.detachEvent)
            elm.detachEvent("on" + type, fn);
    },
    //Shortcut: AJS.$b
    bind: function(fn, scope, /*optional*/ extra_args) {
        fn._cscope = scope;
        return AJS._getRealScope(fn, extra_args);
    },
    bindMethods: function(self) {
        for (var k in self) {
            var func = self[k];
            if (typeof(func) == 'function') {
                self[k] = AJS.$b(func, self);
            }
        }
    },
    _listenOnce: function(elm, type, fn) {
        var r_fn = function() {
            AJS.removeEventListener(elm, type, r_fn);
            fn(arguments);
        }
        return r_fn;
    },
    callLater: function(fn, interval) {
        var fn_no_send = function() {
            fn();
        };
        window.setTimeout(fn_no_send, interval);
    },
    preventDefault: function(e) {
        if(AJS.isIe())
            window.event.returnValue = false;
        else
            e.preventDefault();
    },
////
// Drag and drop
////
    dragAble: function(elm, /*optional*/ handler, args) {
        if(!args)
            args = {};
        if(!AJS.isDefined(args['move_x']))
            args['move_x'] = true;
        if(!AJS.isDefined(args['move_y']))
            args['move_y'] = true;
        if(!AJS.isDefined(args['moveable']))
            args['moveable'] = false;
        if(!AJS.isDefined(args['hide_on_move']))
            args['hide_on_move'] = true;
        if(!AJS.isDefined(args['on_mouse_up']))
            args['on_mouse_up'] = null;
        if(!AJS.isDefined(args['cursor']))
            args['cursor'] = 'move';
        if(!AJS.isDefined(args['max_move']))
            args['max_move'] = {'top': null, 'left': null};
        elm = AJS.$(elm);
        if(!handler)
            handler = elm;
        handler = AJS.$(handler);
        var old_cursor = handler.style.cursor;
        handler.style.cursor = args['cursor'];
        elm.style.position = 'relative';
        AJS.addClass(handler, '_ajs_handler');
        handler._args = args;
        handler._elm = elm;
        AJS.AEV(handler, 'mousedown', AJS._dragStart);
    },
    _dragStart: function(e) {
        var handler = AJS.getEventElm(e);
        if(!AJS.hasClass(handler, '_ajs_handler')) {
            handler = AJS.getParentBytc(handler, null, '_ajs_handler');
        }
        if(handler)
            AJS._dragInit(e, handler._elm, handler._args);
    },
    dropZone: function(elm, args) {
        elm = AJS.$(elm);
        var item = {elm: elm};
        AJS.update(item, args);
        AJS._drop_zones.push(item);
    },
    removeDragAble: function(elm) {
        AJS.REV(elm, 'mousedown', AJS._dragStart);
        elm.style.cursor = '';
    },
    removeDropZone: function(elm) {
        var i = AJS.getIndex(elm, AJS._drop_zones, function(item) {
            if(item.elm == elm) return true;
        });
        if(i != -1) {
            AJS._drop_zones.splice(i, 1);
        }
    },
    _dragInit: function(e, click_elm, args) {
        AJS.drag_obj = new Object();
        AJS.drag_obj.args = args;
        AJS.drag_obj.click_elm = click_elm;
        AJS.drag_obj.mouse_pos = AJS.getMousePos(e);
        AJS.drag_obj.click_elm_pos = AJS.absolutePosition(click_elm);
        AJS.AEV(document, 'mousemove', AJS._dragMove, false, true);
        AJS.AEV(document, 'mouseup', AJS._dragStop, false, true);
        if (AJS.isIe())
            window.event.cancelBubble = true;
        AJS.preventDefault(e);
    },
    _initDragElm: function(elm) {
        if(AJS.drag_elm && AJS.drag_elm.style.display == 'none')
            AJS.removeElement(AJS.drag_elm);
        if(!AJS.drag_elm) {
            AJS.drag_elm = AJS.DIV();
            var d = AJS.drag_elm;
            AJS.insertBefore(d, AJS.getBody().firstChild);
            AJS.setHTML(d, elm.innerHTML);
            d.className = elm.className;
            d.style.cssText = elm.style.cssText;
            d.style.position = 'absolute';
            d.style.zIndex = 10000;
            var t = AJS.absolutePosition(elm);
            AJS.setTop(d, t.y);
            AJS.setLeft(d, t.x);
            if(AJS.drag_obj.args.on_init) {
                AJS.drag_obj.args.on_init(elm);
            }
        }
    },
    _dragMove: function(e) {
        var drag_obj = AJS.drag_obj;
        var click_elm = drag_obj.click_elm;
        AJS._initDragElm(click_elm);
        var drag_elm = AJS.drag_elm;
        if(drag_obj.args['hide_on_move'])
            click_elm.style.visibility = 'hidden';
        var cur_pos = AJS.getMousePos(e);
        var mouse_pos = drag_obj.mouse_pos;
        var click_elm_pos = drag_obj.click_elm_pos;
        var p_x, p_y;
        p_x = cur_pos.x - (mouse_pos.x - click_elm_pos.x);
        p_y = cur_pos.y - (mouse_pos.y - click_elm_pos.y);
        AJS.map(AJS._drop_zones, function(d_z) {
            if(AJS.isOverlapping(d_z['elm'], drag_elm)) {
                if(d_z['elm'] != drag_elm) {
                    var on_hover = d_z['on_hover'];
                    if(on_hover)
                        on_hover(d_z['elm'], click_elm, drag_elm);
                }
            }
        });
        if(drag_obj.args['on_drag'])
            drag_obj.args['on_drag'](click_elm, e);
        var max_move_top = drag_obj.args['max_move']['top'];
        var max_move_left = drag_obj.args['max_move']['left'];
        if(drag_obj.args['move_x']) {
            if(max_move_left == null || max_move_left <= p)
                AJS.setLeft(elm, p_x);
        }
        if(drag_obj.args['move_y']) {
            if(max_move_top == null || max_move_top <= p_y)
                AJS.setTop(elm, p_y);
        }
        if(AJS.isIe()) {
            window.event.cancelBubble = true;
            window.event.returnValue = false;
        }
        else
            e.preventDefault();
        //Moving scroll to the top, should move the scroll up
        var sc_top = AJS.getScrollTop();
        var sc_bottom = sc_top + AJS.getWindowSize().h;
        var d_e_top = AJS.absolutePosition(drag_elm).y;
        var d_e_bottom = drag_elm.offsetTop + drag_elm.offsetHeight;
        if(d_e_top <= sc_top + 20) {
            window.scrollBy(0, -15);
        }
        else if(d_e_bottom >= sc_bottom - 20) {
            window.scrollBy(0, 15);
        }
    },
    _dragStop: function(e) {
        var drag_obj = AJS.drag_obj;
        var drag_elm = AJS.drag_elm;
        var click_elm = drag_obj.click_elm;
        AJS.REV(document, "mousemove", AJS._dragMove, true);
        AJS.REV(document, "mouseup", AJS._dragStop, true);
        var dropped = false;
        AJS.map(AJS._drop_zones, function(d_z) {
            if(AJS.isOverlapping(d_z['elm'], click_elm)) {
                if(d_z['elm'] != click_elm) {
                    var on_drop = d_z['on_drop'];
                    if(on_drop) {
                        dropped = true;
                        on_drop(d_z['elm'], click_elm);
                    }
                }
            }
        });
        if(drag_obj.args['moveable']) {
            var t = parseInt(click_elm.style.top) || 0;
            var l = parseInt(click_elm.style.left) || 0;
            var drag_elm_xy = AJS.absolutePosition(drag_elm);
            var click_elm_xy = AJS.absolutePosition(click_elm);
            AJS.setTop(click_elm, t + drag_elm_xy.y - click_elm_xy.y);
            AJS.setLeft(click_elm, l + drag_elm_xy.x - click_elm_xy.x);
        }
        if(!dropped && drag_obj.args['on_mouse_up'])
            drag_obj.args['on_mouse_up'](click_elm, e);
        if(drag_obj.args['hide_on_move'])
            drag_obj.click_elm.style.visibility = 'visible';
        if(drag_obj.args.on_end) {
            drag_obj.args.on_end(click_elm);
        }
        AJS._dragObj = null;
        if(drag_elm)
            AJS.hideElement(drag_elm);
        AJS.drag_elm = null;
    },
////
// Misc.
////
    keys: function(obj) {
        var rval = [];
        for (var prop in obj) {
            rval.push(prop);
        }
        return rval;
    },
    values: function(obj) {
        var rval = [];
        for (var prop in obj) {
            rval.push(obj[prop]);
        }
        return rval;
    },
    urlencode: function(str) {
        return encodeURIComponent(str.toString());
    },
    isDefined: function(o) {
        return (o != "undefined" && o != null)
    },
    isArray: function(obj) {
        return obj instanceof Array;
    },
    isString: function(obj) {
        return (typeof obj == 'string');
    },
    isNumber: function(obj) {
        return (typeof obj == 'number');
    },
    isObject: function(obj) {
        return (typeof obj == 'object');
    },
    isFunction: function(obj) {
        return (typeof obj == 'function');
    },
    isDict: function(o) {
        var str_repr = String(o);
        return str_repr.indexOf(" Object") != -1;
    },
    exportToGlobalScope: function() {
        for(e in AJS)
            window[e] = AJS[e];
    },
    log: function(o) {
        if(window.console)
            console.log(o);
        else {
            var div = AJS.$('ajs_logger');
            if(!div) {
                div = AJS.DIV({id: 'ajs_logger', 'style': 'color: green; position: absolute; left: 0'});
                div.style.top = AJS.getScrollTop() + 'px';
                AJS.ACN(AJS.getBody(), div);
            }
            AJS.setHTML(div, ''+o);
        }
    }
}
AJS.Class = function(members) {
    var fn = function() {
        if(arguments[0] != 'no_init') {
            return this.init.apply(this, arguments);
        }
    }
    fn.prototype = members;
    AJS.update(fn, AJS.Class.prototype);
    return fn;
}
AJS.Class.prototype = {
    extend: function(members) {
        var parent = new this('no_init');
        for(k in members) {
            var prev = parent[k];
            var cur = members[k];
            if (prev && prev != cur && typeof cur == 'function') {
                cur = this._parentize(cur, prev);
            }
            parent[k] = cur;
        }
        return new AJS.Class(parent);
    },
    implement: function(members) {
        AJS.update(this.prototype, members);
    },
    _parentize: function(cur, prev) {
        return function(){
            this.parent = prev;
            return cur.apply(this, arguments);
        }
    }
};
//Shortcuts
AJS.$ = AJS.getElement;
AJS.$$ = AJS.getElements;
AJS.$f = AJS.getFormElement;
AJS.$b = AJS.bind;
AJS.$p = AJS.partial;
AJS.$FA = AJS.forceArray;
AJS.$A = AJS.createArray;
AJS.DI = AJS.documentInsert;
AJS.ACN = AJS.appendChildNodes;
AJS.RCN = AJS.replaceChildNodes;
AJS.AEV = AJS.addEventListener;
AJS.REV = AJS.removeEventListener;
AJS.$bytc = AJS.getElementsByTagAndClassName;
AJSDeferred = function(req) {
    this.callbacks = [];
    this.errbacks = [];
    this.req = req;
}
AJSDeferred.prototype = {
    excCallbackSeq: function(req, list) {
        var data = req.responseText;
        while (list.length > 0) {
            var fn = list.pop();
            var new_data = fn(data, req);
            if(new_data)
                data = new_data;
        }
    },
    callback: function () {
        this.excCallbackSeq(this.req, this.callbacks);
    },
    errback: function() {
        if(this.errbacks.length == 0)
            alert("Error encountered:\n" + this.req.responseText);
        this.excCallbackSeq(this.req, this.errbacks);
    },
    addErrback: function(fn) {
        this.errbacks.unshift(fn);
    },
    addCallback: function(fn) {
        this.callbacks.unshift(fn);
    },
    abort: function() {
        this.req.abort();
    },
    addCallbacks: function(fn1, fn2) {
        this.addCallback(fn1);
        this.addErrback(fn2);
    },
    sendReq: function(data) {
        if(AJS.isObject(data)) {
            this.req.send(AJS.queryArguments(data));
        }
        else if(AJS.isDefined(data))
            this.req.send(data);
        else {
            this.req.send("");
        }
    }
};
//Prevent memory-leaks
AJS.addEventListener(window, 'unload', AJS._unloadListeners);
AJS._createDomShortcuts()
}
script_loaded = true;
/****
Last Modified: 13/05/07 00:25:28
 GoogieSpell
     Google spell checker for your own web-apps :)
 SpellCheck
    jQuery'fied spell checker based on GoogieSpell 4.0
 Copyright Amir Salihefendic 2006
 Copyright Aleksander Machniak 2009
     LICENSE
     GPL (see gpl.txt for more information)
     This basically means that you can't use this script with/in proprietary software!
     There is another license that permits you to use this script with proprietary software. Check out:... for more info.
     AUTHOR
         GPL
     AUTHORS
         4mir Salihefendic (http://amix.dk) - amix@amix.dk
 VERSION
     4.0
****/
var GOOGIE_CUR_LANG = null;
var GOOGIE_DEFAULT_LANG = "en";
     Aleksander Machniak - alec [at] alec.pl
*/
var SPELL_CUR_LANG = null;
var GOOGIE_DEFAULT_LANG = 'en';
function GoogieSpell(img_dir, server_url) {
    var cookie_value;
    var lang;
    cookie_value = getCookie('language');
    var ref = this;
    if(cookie_value != null)
        GOOGIE_CUR_LANG = cookie_value;
    else
        GOOGIE_CUR_LANG = GOOGIE_DEFAULT_LANG;
    this.array_keys = function(arr) {
    var res = [];
    for (var key in arr) { res.push([key]); }
    return res;
    }
    var cookie_value = getCookie('language');
    GOOGIE_CUR_LANG = cookie_value != null ? cookie_value : GOOGIE_DEFAULT_LANG;
    this.img_dir = img_dir;
    this.server_url = server_url;
    this.org_lang_to_word = {"da": "Dansk", "de": "Deutsch", "en": "English",
                                             "es": "Espa&#241;ol", "fr": "Fran&#231;ais", "it": "Italiano",
                                             "nl": "Nederlands", "pl": "Polski", "pt": "Portugu&#234;s",
                                             "fi": "Suomi", "sv": "Svenska"};
    this.org_lang_to_word = {
    "da": "Dansk", "de": "Deutsch", "en": "English",
        "es": "Espa&#241;ol", "fr": "Fran&#231;ais", "it": "Italiano",
        "nl": "Nederlands", "pl": "Polski", "pt": "Portugu&#234;s",
        "fi": "Suomi", "sv": "Svenska"
    };
    this.lang_to_word = this.org_lang_to_word;
    this.langlist_codes = AJS.keys(this.lang_to_word);
    this.langlist_codes = this.array_keys(this.lang_to_word);
    this.show_change_lang_pic = true;
    this.change_lang_pic_placement = "left";
    this.change_lang_pic_placement = 'right';
    this.report_state_change = true;
    this.ta_scroll_top = 0;
@@ -1475,7 +50,7 @@
    this.lang_no_error_found = "No spelling errors found";
    this.lang_no_suggestions = "No suggestions";
    
    this.show_spell_img = false;  // modified by roundcube
    this.show_spell_img = false; // roundcube mod.
    this.decoration = true;
    this.use_close_btn = true;
    this.edit_layer_dbl_click = true;
@@ -1504,102 +79,89 @@
    //Counters
    this.cnt_errors = 0;
    this.cnt_errors_fixed = 0;
    //Set document on click to hide the language and error menu
    var fn = function(e) {
        var elm = AJS.getEventElm(e);
        if(elm.googie_action_btn != "1" && this.isLangWindowShown())
            this.hideLangWindow();
        if(elm.googie_action_btn != "1" && this.isErrorWindowShown())
            this.hideErrorWindow();
    };
    AJS.AEV(document, "click", AJS.$b(fn, this));
}
    $(document).bind('click', function(e) {
        if($(e.target).attr('googie_action_btn') != '1' && ref.isLangWindowShown())
        ref.hideLangWindow();
    if($(e.target).attr('googie_action_btn') != '1' && ref.isErrorWindowShown())
            ref.hideErrorWindow();
    });
GoogieSpell.prototype.decorateTextarea = function(id) {
    if(typeof(id) == "string")
        this.text_area = AJS.$(id);
    else
        this.text_area = id;
    var r_width, r_height;
this.decorateTextarea = function(id) {
    this.text_area = typeof(id) == 'string' ? document.getElementById(id) : id;
    if(this.text_area != null) {
        if(!AJS.isDefined(this.spell_container) && this.decoration) {
            var table = AJS.TABLE();
            var tbody = AJS.TBODY();
            var tr = AJS.TR();
            if(AJS.isDefined(this.force_width))
                r_width = this.force_width;
            else
                r_width = this.text_area.offsetWidth + "px";
    if (this.text_area) {
        if (!this.spell_container && this.decoration) {
            var table = document.createElement('TABLE');
            var tbody = document.createElement('TBODY');
            var tr = document.createElement('TR');
            var spell_container = document.createElement('TD');
            if(AJS.isDefined(this.force_height))
                r_height = this.force_height;
            else
                r_height = "";
            var spell_container = AJS.TD();
            this.spell_container = spell_container;
            var r_width = this.isDefined(this.force_width) ? this.force_width : this.text_area.offsetWidth;
            var r_height = this.isDefined(this.force_height) ? this.force_height : 16;
            tr.appendChild(spell_container);
            tbody.appendChild(tr);
            table.appendChild(tbody);
            $(table).append(tbody).insertBefore(this.text_area).width('100%').height(r_height);
            $(spell_container).height(r_height).width(r_width).css('text-align', 'right');
            AJS.insertBefore(table, this.text_area);
            //Set width
            AJS.setHeight(table, spell_container, r_height);
            AJS.setWidth(table, spell_container, '100%');  // modified by roundcube (old: r_width)
            spell_container.style.textAlign = "right";
            this.spell_container = spell_container;
        }
        this.checkSpellingState();
    }
    else 
        if(this.report_ta_not_found)
            alert("Text area not found");
        if (this.report_ta_not_found)
            alert('Text area not found');
}
//////
// API Functions (the ones that you can call)
/////
GoogieSpell.prototype.setSpellContainer = function(elm) {
    this.spell_container = AJS.$(elm);
this.setSpellContainer = function(id) {
    this.spell_container = typeof(id) == 'string' ? document.getElementById(id) : id;
}
GoogieSpell.prototype.setLanguages = function(lang_dict) {
this.setLanguages = function(lang_dict) {
    this.lang_to_word = lang_dict;
    this.langlist_codes = AJS.keys(lang_dict);
    this.langlist_codes = this.array_keys(lang_dict);
}
GoogieSpell.prototype.setForceWidthHeight = function(width, height) {
    /***
        Set to null if you want to use one of them
    ***/
this.setCurrentLanguage = function(lan_code) {
    GOOGIE_CUR_LANG = lan_code;
    //Set cookie
    var now = new Date();
    now.setTime(now.getTime() + 365 * 24 * 60 * 60 * 1000);
    setCookie('language', lan_code, now);
}
this.setForceWidthHeight = function(width, height) {
    // Set to null if you want to use one of them
    this.force_width = width;
    this.force_height = height;
}
GoogieSpell.prototype.setDecoration = function(bool) {
this.setDecoration = function(bool) {
    this.decoration = bool;
}
GoogieSpell.prototype.dontUseCloseButtons = function() {
this.dontUseCloseButtons = function() {
    this.use_close_btn = false;
}
GoogieSpell.prototype.appendNewMenuItem = function(name, call_back_fn, checker) {
this.appendNewMenuItem = function(name, call_back_fn, checker) {
    this.extra_menu_items.push([name, call_back_fn, checker]);
}
GoogieSpell.prototype.appendCustomMenuBuilder = function(eval, builder) {
this.appendCustomMenuBuilder = function(eval, builder) {
    this.custom_menu_builder.push([eval, builder]);
}
GoogieSpell.prototype.setFocus = function() {
this.setFocus = function() {
    try {
        this.focus_link_b.focus();
        this.focus_link_t.focus();
@@ -1610,25 +172,17 @@
    }
}
GoogieSpell.prototype.getValue = function(ta) {
    return ta.value;
}
GoogieSpell.prototype.setValue = function(ta, value) {
    ta.value = value;
}
//////
// Set functions (internal)
/////
GoogieSpell.prototype.setStateChanged = function(current_state) {
this.setStateChanged = function(current_state) {
    this.state = current_state;
    if(this.spelling_state_observer != null && this.report_state_change)
    if (this.spelling_state_observer != null && this.report_state_change)
        this.spelling_state_observer(current_state, this);
}
GoogieSpell.prototype.setReportStateChange = function(bool) {
this.setReportStateChange = function(bool) {
    this.report_state_change = bool;
}
@@ -1636,219 +190,181 @@
//////
// Request functions
/////
GoogieSpell.prototype.getGoogleUrl = function() {
this.getUrl = function() {
    return this.server_url + GOOGIE_CUR_LANG;
}
GoogieSpell.escapeSepcial = function(val) {
this.escapeSpecial = function(val) {
    return val.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
}
GoogieSpell.createXMLReq = function (text) {
    return '<?xml version="1.0" encoding="utf-8" ?><spellrequest textalreadyclipped="0" ignoredups="0" ignoredigits="1" ignoreallcaps="1"><text>' + text + '</text></spellrequest>';
this.createXMLReq = function (text) {
    return '<?xml version="1.0" encoding="utf-8" ?>'
    + '<spellrequest textalreadyclipped="0" ignoredups="0" ignoredigits="1" ignoreallcaps="1">'
    + '<text>' + text + '</text></spellrequest>';
}
GoogieSpell.prototype.spellCheck = function(ignore) {
    var me = this;
this.spellCheck = function(ignore) {
    this.cnt_errors_fixed = 0;
    this.cnt_errors = 0;
    this.setStateChanged("checking_spell");
    this.setStateChanged('checking_spell');
    if(this.main_controller)
    if (this.main_controller)
        this.appendIndicator(this.spell_span);
    this.error_links = [];
    this.ta_scroll_top = this.text_area.scrollTop;
    try { this.hideLangWindow(); }
    catch(e) {}
    this.ignore = ignore;
    this.hideLangWindow();
    if(this.getValue(this.text_area) == '' || ignore) {
        if(!me.custom_no_spelling_error)
            me.flashNoSpellingErrorState();
    if ($(this.text_area).val() == '' || ignore) {
        if (!this.custom_no_spelling_error)
            this.flashNoSpellingErrorState();
        else
            me.custom_no_spelling_error(me);
        me.removeIndicator();
        return ;
            this.custom_no_spelling_error(this);
        this.removeIndicator();
        return;
    }
    
    this.createEditLayer(this.text_area.offsetWidth, this.text_area.offsetHeight);
    this.createErrorWindow();
    AJS.getBody().appendChild(this.error_window);
    $('body').append(this.error_window);
    try { netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead"); } 
    catch (e) { }
    if(this.main_controller)
        this.spell_span.onclick = null;
    if (this.main_controller)
        $(this.spell_span).unbind('click');
    this.orginal_text = this.getValue(this.text_area);
    this.orginal_text = $(this.text_area).val();
    var req_text = this.escapeSpecial(this.orginal_text);
    var ref = this;
    //Create request
    var d = AJS.getRequest(this.getGoogleUrl());
    var reqdone = function(res_txt) {
        var r_text = res_txt;
        me.results = me.parseResult(r_text);
        if(r_text.match(/<c.*>/) != null) {
            //Before parsing be sure that errors were found
            me.showErrorsInIframe();
            me.resumeEditingState();
        }
        else {
            if(!me.custom_no_spelling_error)
                me.flashNoSpellingErrorState();
            else
                me.custom_no_spelling_error(me);
        }
        me.removeIndicator();
    };
    d.addCallback(reqdone);
    reqdone = null;
    var reqfailed = function(res_txt, req) {
        if(me.custom_ajax_error)
            me.custom_ajax_error(req);
        else
            alert("An error was encountered on the server. Please try again later.");
        if(me.main_controller) {
            AJS.removeElement(me.spell_span);
            me.removeIndicator();
        }
        me.checkSpellingState();
    };
    d.addErrback(reqfailed);
    reqfailed = null;
    var req_text = GoogieSpell.escapeSepcial(this.orginal_text);
    d.sendReq(GoogieSpell.createXMLReq(req_text));
    $.ajax({ type: 'POST', url: this.getUrl(),
    data: this.createXMLReq(req_text), dataType: 'text',
    error: function(o) {
            if (ref.custom_ajax_error)
            ref.custom_ajax_error(ref);
            else
            alert('An error was encountered on the server. Please try again later.');
            if (ref.main_controller) {
            $(ref.spell_span).remove();
            ref.removeIndicator();
            }
            ref.checkSpellingState();
    },
        success: function(data) {
        var r_text = data;
            ref.results = ref.parseResult(r_text);
            if (r_text.match(/<c.*>/) != null) {
            //Before parsing be sure that errors were found
            ref.showErrorsInIframe();
            ref.resumeEditingState();
            } else {
            if (!ref.custom_no_spelling_error)
            ref.flashNoSpellingErrorState();
            else
                    ref.custom_no_spelling_error(ref);
            }
            ref.removeIndicator();
    }
    });
}
//////
// Spell checking functions
/////
GoogieSpell.prototype.parseResult = function(r_text) {
    /***
     Retunrs an array
     result[item] -> ['attrs'], ['suggestions']
        ***/
this.parseResult = function(r_text) {
    // Returns an array: result[item] -> ['attrs'], ['suggestions']
    var re_split_attr_c = /\w+="(\d+|true)"/g;
    var re_split_text = /\t/g;
    var matched_c = r_text.match(/<c[^>]*>[^<]*<\/c>/g);
    var results = new Array();
    if(matched_c == null)
    if (matched_c == null)
        return results;
    
    for(var i=0; i < matched_c.length; i++) {
    for (var i=0; i < matched_c.length; i++) {
        var item = new Array();
        this.errorFound();
        //Get attributes
        item['attrs'] = new Array();
        var split_c = matched_c[i].match(re_split_attr_c);
        for(var j=0; j < split_c.length; j++) {
        for (var j=0; j < split_c.length; j++) {
            var c_attr = split_c[j].split(/=/);
            var val = c_attr[1].replace(/"/g, '');
            if(val != "true")
                item['attrs'][c_attr[0]] = parseInt(val);
            else {
                item['attrs'][c_attr[0]] = val;
            }
            item['attrs'][c_attr[0]] = val != 'true' ? parseInt(val) : val;
        }
        //Get suggestions
        item['suggestions'] = new Array();
        var only_text = matched_c[i].replace(/<[^>]*>/g, "");
        var only_text = matched_c[i].replace(/<[^>]*>/g, '');
        var split_t = only_text.split(re_split_text);
        for(var k=0; k < split_t.length; k++) {
        if(split_t[k] != "")
            item['suggestions'].push(split_t[k]);
        }
        for (var k=0; k < split_t.length; k++) {
            if(split_t[k] != '')
            item['suggestions'].push(split_t[k]);
        }
        results.push(item);
    }
    return results;
}
//////
// Counters
/////
GoogieSpell.prototype.errorFixed = function() {
    this.cnt_errors_fixed++;
    if(this.all_errors_fixed_observer)
        if(this.cnt_errors_fixed == this.cnt_errors) {
            this.hideErrorWindow();
            this.all_errors_fixed_observer();
        }
}
GoogieSpell.prototype.errorFound = function() { this.cnt_errors++; }
//////
// Error menu functions
/////
GoogieSpell.prototype.createErrorWindow = function() {
    this.error_window = AJS.DIV();
    this.error_window.className = "googie_window";
    this.error_window.googie_action_btn = "1";
this.createErrorWindow = function() {
    this.error_window = document.createElement('DIV');
    $(this.error_window).addClass('googie_window').attr('googie_action_btn', '1');
}
GoogieSpell.prototype.isErrorWindowShown = function() {
    return this.error_window != null && this.error_window.style.visibility == "visible";
this.isErrorWindowShown = function() {
    return $(this.error_window).is(':visible');
}
GoogieSpell.prototype.hideErrorWindow = function() {
    try {
        this.error_window.style.visibility = "hidden";
        if(this.error_window_iframe)
            this.error_window_iframe.style.visibility = "hidden";
    }
    catch(e) {}
this.hideErrorWindow = function() {
    $(this.error_window).css('visibility', 'hidden');
    $(this.error_window_iframe).css('visibility', 'hidden');
}
GoogieSpell.prototype.updateOrginalText = function(offset, old_value, new_value, id) {
this.updateOrginalText = function(offset, old_value, new_value, id) {
    var part_1 = this.orginal_text.substring(0, offset);
    var part_2 = this.orginal_text.substring(offset+old_value.length);
    this.orginal_text = part_1 + new_value + part_2;
    this.setValue(this.text_area, this.orginal_text);
    $(this.text_area).val(this.orginal_text);
    var add_2_offset = new_value.length - old_value.length;
    for(var j=0; j < this.results.length; j++) {
    for (var j=0; j < this.results.length; j++) {
        //Don't edit the offset of the current item
        if(j != id && j > id){
        if (j != id && j > id)
            this.results[j]['attrs']['o'] += add_2_offset;
        }
    }
}
GoogieSpell.prototype.saveOldValue = function(elm, old_value) {
this.saveOldValue = function(elm, old_value) {
    elm.is_changed = true;
    elm.old_value = old_value;
}
GoogieSpell.prototype.createListSeparator = function() {
    var e_col = AJS.TD(" ");
    e_col.googie_action_btn = "1";
    e_col.style.cursor = "default";
    e_col.style.fontSize = "3px";
    e_col.style.borderTop = "1px solid #ccc";
    e_col.style.paddingTop = "3px";
this.createListSeparator = function() {
    var td = document.createElement('TD');
    var tr = document.createElement('TR');
    return AJS.TR(e_col);
    $(td).html(' ').attr('googie_action_btn', '1')
    .css({'cursor': 'default', 'font-size': '3px', 'border-top': '1px solid #ccc', 'padding-top': '3px'});
    tr.appendChild(td);
    return tr;
}
GoogieSpell.prototype.correctError = function(id, elm, l_elm, /*optional*/ rm_pre_space) {
this.correctError = function(id, elm, l_elm, rm_pre_space) {
    var old_value = elm.innerHTML;
    var new_value = l_elm.innerHTML;
    var offset = this.results[id]['attrs']['o'];
    if(rm_pre_space) {
    if (rm_pre_space) {
        var pre_length = elm.previousSibling.innerHTML;
        elm.previousSibling.innerHTML = pre_length.slice(0, pre_length.length-1);
        old_value = " " + old_value;
@@ -1856,44 +372,38 @@
    }
    this.hideErrorWindow();
    this.updateOrginalText(offset, old_value, new_value, id);
    elm.innerHTML = new_value;
    elm.style.color = "green";
    elm.is_corrected = true;
    $(elm).html(new_value).css('color', 'green').attr('is_corrected', true);
    this.results[id]['attrs']['l'] = new_value.length;
    if(!AJS.isDefined(elm.old_value))
    if (!this.isDefined(elm.old_value))
        this.saveOldValue(elm, old_value);
    
    this.errorFixed();
}
GoogieSpell.prototype.showErrorWindow = function(elm, id) {
    if(this.show_menu_observer)
this.showErrorWindow = function(elm, id) {
    if (this.show_menu_observer)
        this.show_menu_observer(this);
    var me = this;
    var abs_pos = AJS.absolutePosition(elm);
    abs_pos.y -= this.edit_layer.scrollTop;
    this.error_window.style.visibility = "visible";
    var ref = this;
    var pos = $(elm).offset();
    pos.top -= this.edit_layer.scrollTop;
    AJS.setTop(this.error_window, (abs_pos.y+20));
    AJS.setLeft(this.error_window, (abs_pos.x));
    $(this.error_window).css({'visibility': 'visible',
    'top': (pos.top+20)+'px', 'left': (pos.left)+'px'}).html('');
    this.error_window.innerHTML = "";
    var table = document.createElement('TABLE');
    var list = document.createElement('TBODY');
    var table = AJS.TABLE({'class': 'googie_list'});
    table.googie_action_btn = "1";
    var list = AJS.TBODY();
    $(table).addClass('googie_list').attr('googie_action_btn', '1');
    //Check if we should use custom menu builder, if not we use the default
    var changed = false;
    if(this.custom_menu_builder != []) {
        for(var k=0; k<this.custom_menu_builder.length; k++) {
    if (this.custom_menu_builder != []) {
        for (var k=0; k<this.custom_menu_builder.length; k++) {
            var eb = this.custom_menu_builder[k];
            if(eb[0]((this.results[id]))){
                changed = eb[1](this, list, elm);
@@ -1901,137 +411,131 @@
            }
        }
    }
    if(!changed) {
    if (!changed) {
        //Build up the result list
        var suggestions = this.results[id]['suggestions'];
        var offset = this.results[id]['attrs']['o'];
        var len = this.results[id]['attrs']['l'];
        if(suggestions.length == 0) {
            var row = AJS.TR();
            var item = AJS.TD({'style': 'cursor: default;'});
            var dummy = AJS.SPAN();
            dummy.innerHTML = this.lang_no_suggestions;
            AJS.ACN(item, AJS.TN(dummy.innerHTML));
            item.googie_action_btn = "1";
        if (suggestions.length == 0) {
            var row = document.createElement('TR');
            var item = document.createElement('TD');
            var dummy = document.createElement('SPAN');
            $(dummy).text(this.lang_no_suggestions);
            $(item).attr('googie_action_btn', '1').css('cursor', 'default');
            item.appendChild(dummy);
            row.appendChild(item);
            list.appendChild(row);
        }
        for(i=0; i < suggestions.length; i++) {
            var row = AJS.TR();
            var item = AJS.TD();
            var dummy = AJS.SPAN();
            dummy.innerHTML = suggestions[i];
            item.appendChild(AJS.TN(dummy.innerHTML));
        for (i=0; i < suggestions.length; i++) {
            var row = document.createElement('TR');
            var item = document.createElement('TD');
            var dummy = document.createElement('SPAN');
            $(dummy).html(suggestions[i]);
            
            var fn = function(e) {
                var l_elm = AJS.getEventElm(e);
                this.correctError(id, elm, l_elm);
            };
            $(item).bind('mouseover', this.item_onmouseover)
            .bind('mouseout', this.item_onmouseout)
            .bind('click', function(e) { ref.correctError(id, elm, e.target.firstChild) });
            AJS.AEV(item, "click", AJS.$b(fn, this));
            item.onmouseover = GoogieSpell.item_onmouseover;
            item.onmouseout = GoogieSpell.item_onmouseout;
            item.appendChild(dummy);
            row.appendChild(item);
            list.appendChild(row);
        }
        //The element is changed, append the revert
        if(elm.is_changed && elm.innerHTML != elm.old_value) {
        if (elm.is_changed && elm.innerHTML != elm.old_value) {
            var old_value = elm.old_value;
            var revert_row = AJS.TR();
            var revert = AJS.TD();
            var revert_row = document.createElement('TR');
            var revert = document.createElement('TD');
            var rev_span = document.createElement('SPAN');
        $(rev_span).addClass('googie_list_revert').html(this.lang_revert + ' ' + old_value);
            revert.onmouseover = GoogieSpell.item_onmouseover;
            revert.onmouseout = GoogieSpell.item_onmouseout;
            var rev_span = AJS.SPAN({'class': 'googie_list_revert'});
            rev_span.innerHTML = this.lang_revert + " " + old_value;
            $(revert).bind('mouseover', this.item_onmouseover)
            .bind('mouseout', this.item_onmouseout)
            .bind('click', function(e) {
                    ref.updateOrginalText(offset, elm.innerHTML, old_value, id);
                    $(elm).attr('is_corrected', true).css('color', '#b91414').html(old_value);
                    ref.hideErrorWindow();
            });
            revert.appendChild(rev_span);
            var fn = function(e) {
                this.updateOrginalText(offset, elm.innerHTML, old_value, id);
                elm.is_corrected = true;
                elm.style.color = "#b91414";
                elm.innerHTML = old_value;
                this.hideErrorWindow();
            };
            AJS.AEV(revert, "click", AJS.$b(fn, this));
            revert_row.appendChild(revert);
            list.appendChild(revert_row);
        }
        
        //Append the edit box
        var edit_row = AJS.TR();
        var edit = AJS.TD({'style': 'cursor: default'});
        var edit_input = AJS.INPUT({'style': 'width: 120px; margin:0; padding:0', 'value': elm.innerHTML});
        edit_input.googie_action_btn = "1";
        var edit_row = document.createElement('TR');
        var edit = document.createElement('TD');
        var edit_input = document.createElement('INPUT');
        var ok_pic = document.createElement('IMG');
    var edit_form = document.createElement('FORM');
        var onsub = function () {
            if(edit_input.value != "") {
                if(!AJS.isDefined(elm.old_value))
                    this.saveOldValue(elm, elm.innerHTML);
            if (edit_input.value != '') {
                if (!ref.isDefined(elm.old_value))
                    ref.saveOldValue(elm, elm.innerHTML);
                this.updateOrginalText(offset, elm.innerHTML, edit_input.value, id);
                elm.style.color = "green"
                elm.is_corrected = true;
                elm.innerHTML = edit_input.value;
                this.hideErrorWindow();
                ref.updateOrginalText(offset, elm.innerHTML, edit_input.value, id);
        $(elm).attr('is_corrected', true).css('color', 'green').html(edit_input.value);
                ref.hideErrorWindow();
            }
            return false;
        };
        onsub = AJS.$b(onsub, this);
        var ok_pic = AJS.IMG({'src': this.img_dir + "ok.gif", 'style': 'width: 32px; height: 16px; margin-left: 2px; margin-right: 2px; cursor: pointer;'});
        var edit_form = AJS.FORM({'style': 'margin: 0; padding: 0; cursor: default;'}, edit_input, ok_pic);
        edit_form.googie_action_btn = "1";
        edit.googie_action_btn = "1";
    $(edit_input).width(120).css({'margin': 0, 'padding': 0});
    $(edit_input).val(elm.innerHTML).attr('googie_action_btn', '1');
    $(edit).css('cursor', 'default').attr('googie_action_btn', '1');
        AJS.AEV(edit_form, "submit", onsub);
        AJS.AEV(ok_pic, "click", onsub);
    $(ok_pic).attr('src', this.img_dir + 'ok.gif')
        .width(32).height(16)
        .css({'cursor': 'pointer', 'margin-left': '2px', 'margin-right': '2px'})
        .bind('click', onsub);
        $(edit_form).attr('googie_action_btn', '1')
        .css({'margin': 0, 'padding': 0, 'cursor': 'default', 'white-space': 'nowrap'})
        .bind('submit', onsub);
        
    edit_form.appendChild(edit_input);
    edit_form.appendChild(ok_pic);
        edit.appendChild(edit_form);
        edit_row.appendChild(edit);
        list.appendChild(edit_row);
        //Append extra menu items
        if(this.extra_menu_items.length > 0)
            AJS.ACN(list, this.createListSeparator());
        if (this.extra_menu_items.length > 0)
        list.appendChild(this.createListSeparator());
        var loop = function(i) {
                if(i < me.extra_menu_items.length) {
                    var e_elm = me.extra_menu_items[i];
                if (i < ref.extra_menu_items.length) {
                    var e_elm = ref.extra_menu_items[i];
                    if(!e_elm[2] || e_elm[2](elm, me)) {
                        var e_row = AJS.TR();
                        var e_col = AJS.TD(e_elm[0]);
                    if (!e_elm[2] || e_elm[2](elm, ref)) {
                        var e_row = document.createElement('TR');
                        var e_col = document.createElement('TD');
                        e_col.onmouseover = GoogieSpell.item_onmouseover;
                        e_col.onmouseout = GoogieSpell.item_onmouseout;
                        var fn = function() {
                            return e_elm[1](elm, me);
                        };
                        AJS.AEV(e_col, "click", fn);
                        AJS.ACN(e_row, e_col);
                        AJS.ACN(list, e_row);
            $(e_col).html(e_elm[0])
                            .bind('mouseover', ref.item_onmouseover)
                            .bind('mouseout', ref.item_onmouseout)
                .bind('click', function() { return e_elm[1](elm, ref) });
            e_row.appendChild(e_col);
                        list.appendChild(e_row);
                    }
                    loop(i+1);
                }
        }
        loop(0);
        loop = null;
        //Close button
        if(this.use_close_btn) {
            AJS.ACN(list, this.createCloseButton(this.hideErrorWindow));
        if (this.use_close_btn) {
            list.appendChild(this.createCloseButton(this.hideErrorWindow));
        }
    }
@@ -2039,154 +543,117 @@
    this.error_window.appendChild(table);
    //Dummy for IE - dropdown bug fix
    if(AJS.isIe() && !this.error_window_iframe) {
        var iframe = AJS.IFRAME({'style': 'position: absolute; z-index: 0;'});
        AJS.ACN(AJS.getBody(), iframe);
        this.error_window_iframe = iframe;
    if ($.browser.msie) {
    if (!this.error_window_iframe) {
            var iframe = $('<iframe>').css('position', 'absolute').css('z-index', 0);
        $('body').append(iframe);
            this.error_window_iframe = iframe;
        }
    $(this.error_window_iframe).css({'visibility': 'visible',
        'top': this.error_window.offsetTop, 'left': this.error_window.offsetLeft,
            'width': this.error_window.offsetWidth, 'height': this.error_window.offsetHeight});
    }
    if(AJS.isIe()) {
        var iframe = this.error_window_iframe;
        AJS.setTop(iframe, this.error_window.offsetTop);
        AJS.setLeft(iframe, this.error_window.offsetLeft);
        AJS.setWidth(iframe, this.error_window.offsetWidth);
        AJS.setHeight(iframe, this.error_window.offsetHeight);
        iframe.style.visibility = "visible";
    }
    //Set focus on the last element
    var link = this.createFocusLink('link');
    list.appendChild(AJS.TR(AJS.TD({'style': 'text-align: right; font-size: 1px; height: 1px; margin: 0; padding: 0;'}, link)));
    link.focus();
}
//////
// Edit layer (the layer where the suggestions are stored)
//////
GoogieSpell.prototype.createEditLayer = function(width, height) {
    this.edit_layer = AJS.DIV({'class': 'googie_edit_layer'});
this.createEditLayer = function(width, height) {
    this.edit_layer = document.createElement('DIV');
    $(this.edit_layer).addClass('googie_edit_layer').width(width-10).height(height);
    //Set the style so it looks like edit areas
    this.edit_layer.className = this.text_area.className;
    this.edit_layer.style.border = "1px solid #999";
    this.edit_layer.style.backgroundColor = "#F1EDFE";  // modified by roundcube
    this.edit_layer.style.padding = "3px";
    this.edit_layer.style.margin = "0px";
    AJS.setWidth(this.edit_layer, (width-8));
    if(AJS.nodeName(this.text_area) != "input" || this.getValue(this.text_area) == "") {
        this.edit_layer.style.overflow = "auto";
        AJS.setHeight(this.edit_layer, (height-6));
    }
    else {
        this.edit_layer.style.overflow = "hidden";
    if (this.text_area.nodeName != 'INPUT' || $(this.text_area).val() == '') {
        $(this.edit_layer).css('overflow', 'auto').height(height-4);
    } else {
        $(this.edit_layer).css('overflow', 'hidden');
    }
    if(this.edit_layer_dbl_click) {
        var me = this;
        var fn = function(e) {
            if(AJS.getEventElm(e).className != "googie_link" && !me.isErrorWindowShown()) {
                me.resumeEditing();
    var ref = this;
    if (this.edit_layer_dbl_click) {
        $(this.edit_layer).bind('click', function(e) {
            if (e.target.className != 'googie_link' && !ref.isErrorWindowShown()) {
                ref.resumeEditing();
                var fn1 = function() {
                    me.text_area.focus();
                    $(ref.text_area).focus();
                    fn1 = null;
                };
                AJS.callLater(fn1, 10);
                window.setTimeout(fn1, 10);
            }
            return false;
        };
        this.edit_layer.ondblclick = fn;
        fn = null;
        });
    }
}
GoogieSpell.prototype.resumeEditing = function() {
    this.setStateChanged("spell_check");
    this.switch_lan_pic.style.display = "inline";
this.resumeEditing = function() {
    this.setStateChanged('ready');
    if(this.edit_layer)
    if (this.edit_layer)
        this.el_scroll_top = this.edit_layer.scrollTop;
    this.hideErrorWindow();
    if(this.main_controller)
        this.spell_span.className = "googie_no_style";
    if (this.main_controller)
        $(this.spell_span).removeClass().addClass('googie_no_style');
    if(!this.ignore) {
        //Remove the EDIT_LAYER
        try {
            this.edit_layer.parentNode.removeChild(this.edit_layer);
            if(this.use_focus) {
                AJS.removeElement(this.focus_link_t);
                AJS.removeElement(this.focus_link_b);
            }
        }
        catch(e) {
    if (!this.ignore) {
        if (this.use_focus) {
            $(this.focus_link_t).remove();
            $(this.focus_link_b).remove();
        }
        AJS.showElement(this.text_area);
        $(this.edit_layer).remove();
        $(this.text_area).show();
        if(this.el_scroll_top != undefined)
        if (this.el_scroll_top != undefined)
            this.text_area.scrollTop = this.el_scroll_top;
    }
    this.checkSpellingState(false);
}
GoogieSpell.prototype.createErrorLink = function(text, id) {
    var elm = AJS.SPAN({'class': 'googie_link'});
    var me = this;
this.createErrorLink = function(text, id) {
    var elm = document.createElement('SPAN');
    var ref = this;
    var d = function (e) {
        me.showErrorWindow(elm, id);
        d = null;
        return false;
            ref.showErrorWindow(elm, id);
            d = null;
            return false;
    };
    AJS.AEV(elm, "click", d);
    $(elm).html(text).addClass('googie_link').bind('click', d)
    .attr({'googie_action_btn' : '1', 'g_id' : id, 'is_corrected' : false});
    elm.googie_action_btn = "1";
    elm.g_id = id;
    elm.is_corrected = false;
    elm.oncontextmenu = d;
    elm.innerHTML = text;
    return elm;
}
GoogieSpell.createPart = function(txt_part) {
    if(txt_part == " ")
        return AJS.TN(" ");
    var result = AJS.SPAN();
this.createPart = function(txt_part) {
    if (txt_part == " ")
        return document.createTextNode(" ");
    var is_first = true;
    var is_safari = (navigator.userAgent.toLowerCase().indexOf("safari") != -1);
    var part = AJS.SPAN();
    txt_part = GoogieSpell.escapeSepcial(txt_part);
    txt_part = this.escapeSpecial(txt_part);
    txt_part = txt_part.replace(/\n/g, "<br>");
    txt_part = txt_part.replace(/    /g, " &nbsp;");
    txt_part = txt_part.replace(/^ /g, "&nbsp;");
    txt_part = txt_part.replace(/ $/g, "&nbsp;");
    
    part.innerHTML = txt_part;
    return part;
    var span = document.createElement('SPAN');
    $(span).html(txt_part);
    return span;
}
GoogieSpell.prototype.showErrorsInIframe = function() {
    var output = AJS.DIV();
    output.style.textAlign = "left";
this.showErrorsInIframe = function() {
    var output = document.createElement('DIV')
    var pointer = 0;
    var results = this.results;
    if(results.length > 0) {
        for(var i=0; i < results.length; i++) {
    if (results.length > 0) {
        for (var i=0; i < results.length; i++) {
            var offset = results[i]['attrs']['o'];
            var len = results[i]['attrs']['l'];
            var part_1_text = this.orginal_text.substring(pointer, offset);
            var part_1 = GoogieSpell.createPart(part_1_text);
            var part_1 = this.createPart(part_1_text);
            output.appendChild(part_1);
            pointer += offset - pointer;
            
@@ -2198,96 +665,90 @@
        }
        //Insert the rest of the orginal text
        var part_2_text = this.orginal_text.substr(pointer, this.orginal_text.length);
        var part_2 = this.createPart(part_2_text);
        var part_2 = GoogieSpell.createPart(part_2_text);
        output.appendChild(part_2);
    }
    else
        output.innerHTML = this.orginal_text;
    $(output).css('text-align', 'left');
    var me = this;
    if(this.custom_item_evaulator)
        AJS.map(this.error_links, function(elm){me.custom_item_evaulator(me, elm)});
    if (this.custom_item_evaulator)
        $.map(this.error_links, function(elm){me.custom_item_evaulator(me, elm)});
    
    AJS.ACN(this.edit_layer, output);
    $(this.edit_layer).append(output);
    //Hide text area
    this.text_area_bottom = this.text_area.offsetTop + this.text_area.offsetHeight;
    //Hide text area and show edit layer
    $(this.text_area).hide();
    $(this.edit_layer).insertBefore(this.text_area);
    AJS.hideElement(this.text_area);
    AJS.insertBefore(this.edit_layer, this.text_area);
    if(this.use_focus) {
    if (this.use_focus) {
        this.focus_link_t = this.createFocusLink('focus_t');
        this.focus_link_b = this.createFocusLink('focus_b');
        AJS.insertBefore(this.focus_link_t, this.edit_layer);
        AJS.insertAfter(this.focus_link_b, this.edit_layer);
        $(this.focus_link_t).insertBefore(this.edit_layer);
        $(this.focus_link_b).insertAfter(this.edit_layer);
    }
    this.edit_layer.scrollTop = this.ta_scroll_top;
//    this.edit_layer.scrollTop = this.ta_scroll_top;
}
//////
// Choose language menu
//////
GoogieSpell.prototype.createLangWindow = function() {
    this.language_window = AJS.DIV({'class': 'googie_window'});
    AJS.setWidth(this.language_window, 100);
    this.language_window.googie_action_btn = "1";
this.createLangWindow = function() {
    this.language_window = document.createElement('DIV');
    $(this.language_window).addClass('googie_window')
    .width(100).attr('googie_action_btn', '1');
    //Build up the result list
    var table = AJS.TABLE({'class': 'googie_list'});
    AJS.setWidth(table, "100%");
    var list = AJS.TBODY();
    var table = document.createElement('TABLE');
    var list = document.createElement('TBODY');
    var ref = this;
    $(table).addClass('googie_list').width('100%');
    this.lang_elms = new Array();
    for(i=0; i < this.langlist_codes.length; i++) {
        var row = AJS.TR();
        var item = AJS.TD();
        item.googieId = this.langlist_codes[i];
    for (i=0; i < this.langlist_codes.length; i++) {
        var row = document.createElement('TR');
        var item = document.createElement('TD');
        var span = document.createElement('SPAN');
    $(span).text(this.lang_to_word[this.langlist_codes[i]]);
        this.lang_elms.push(item);
        var lang_span = AJS.SPAN();
        lang_span.innerHTML = this.lang_to_word[this.langlist_codes[i]];
        item.appendChild(AJS.TN(lang_span.innerHTML));
        var fn = function(e) {
            var elm = AJS.getEventElm(e);
            this.deHighlightCurSel();
        $(item).attr('googieId', this.langlist_codes[i])
            .bind('click', function(e) {
            ref.deHighlightCurSel();
            ref.setCurrentLanguage($(this).attr('googieId'));
            this.setCurrentLanguage(elm.googieId);
            if (ref.lang_state_observer != null) {
                    ref.lang_state_observer();
            }
            if(this.lang_state_observer != null) {
                this.lang_state_observer();
            }
            ref.highlightCurSel();
            ref.hideLangWindow();
            })
            .bind('mouseover', function(e) {
            if (this.className != "googie_list_selected")
                    this.className = "googie_list_onhover";
            })
            .bind('mouseout', function(e) {
            if (this.className != "googie_list_selected")
                    this.className = "googie_list_onout";
            });
            this.highlightCurSel();
            this.hideLangWindow();
        };
        AJS.AEV(item, "click", AJS.$b(fn, this));
        item.onmouseover = function(e) {
            var i_it = AJS.getEventElm(e);
            if(i_it.className != "googie_list_selected")
                i_it.className = "googie_list_onhover";
        };
        item.onmouseout = function(e) {
            var i_it = AJS.getEventElm(e);
            if(i_it.className != "googie_list_selected")
                i_it.className = "googie_list_onout";
        };
    item.appendChild(span);
        row.appendChild(item);
        list.appendChild(row);
    }
    //Close button
    if(this.use_close_btn) {
        list.appendChild(this.createCloseButton(this.hideLangWindow));
    if (this.use_close_btn) {
        list.appendChild(this.createCloseButton(function () { ref.hideLangWindow.apply(ref) }));
    }
    this.highlightCurSel();
@@ -2296,36 +757,24 @@
    this.language_window.appendChild(table);
}
GoogieSpell.prototype.setCurrentLanguage = function(lan_code) {
    GOOGIE_CUR_LANG = lan_code;
    //Set cookie
    var now = new Date();
    now.setTime(now.getTime() + 365 * 24 * 60 * 60 * 1000);
    setCookie('language', lan_code, now);
this.isLangWindowShown = function() {
    return $(this.language_window).is(':hidden');
}
GoogieSpell.prototype.isLangWindowShown = function() {
    return this.language_window != null && this.language_window.style.visibility == "visible";
this.hideLangWindow = function() {
    $(this.language_window).css('visibility', 'hidden');
    $(this.switch_lan_pic).removeClass().addClass('googie_lang_3d_on');
}
GoogieSpell.prototype.hideLangWindow = function() {
    try {
        this.language_window.style.visibility = "hidden";
        this.switch_lan_pic.className = "googie_lang_3d_on";
    }
    catch(e) {}
this.deHighlightCurSel = function() {
    $(this.lang_cur_elm).removeClass().addClass('googie_list_onout');
}
GoogieSpell.prototype.deHighlightCurSel = function() {
    this.lang_cur_elm.className = "googie_list_onout";
}
GoogieSpell.prototype.highlightCurSel = function() {
    if(GOOGIE_CUR_LANG == null)
this.highlightCurSel = function() {
    if (GOOGIE_CUR_LANG == null)
        GOOGIE_CUR_LANG = GOOGIE_DEFAULT_LANG;
    for(var i=0; i < this.lang_elms.length; i++) {
        if(this.lang_elms[i].googieId == GOOGIE_CUR_LANG) {
    for (var i=0; i < this.lang_elms.length; i++) {
        if ($(this.lang_elms[i]).attr('googieId') == GOOGIE_CUR_LANG) {
            this.lang_elms[i].className = "googie_list_selected";
            this.lang_cur_elm = this.lang_elms[i];
        }
@@ -2335,219 +784,223 @@
    }
}
GoogieSpell.prototype.showLangWindow = function(elm, ofst_top, ofst_left) {
    if(this.show_menu_observer)
this.showLangWindow = function(elm) {
    if (this.show_menu_observer)
        this.show_menu_observer(this);
    if(!AJS.isDefined(ofst_top))
        ofst_top = 18;  // modified by roundcube
    if(!AJS.isDefined(ofst_left))
        ofst_left = 22; // modified by roundcube
    this.createLangWindow();
    AJS.getBody().appendChild(this.language_window);
    $('body').append(this.language_window);
    var abs_pos = AJS.absolutePosition(elm);
    AJS.showElement(this.language_window);
    AJS.setTop(this.language_window, (abs_pos.y+ofst_top));
    AJS.setLeft(this.language_window, (abs_pos.x+ofst_left-this.language_window.offsetWidth));
    var pos = $(elm).offset();
    var top = pos.top + $(elm).height();
    var left = this.change_lang_pic_placement == 'right' ?
    pos.left - 100 + $(elm).width() : pos.left + $(elm).width();
    $(this.language_window).css({'visibility': 'visible', 'top' : top+'px','left' : left+'px'});
    this.highlightCurSel();
    this.language_window.style.visibility = "visible";
}
GoogieSpell.prototype.createChangeLangPic = function() {
    var img = AJS.IMG({'src': this.img_dir + 'change_lang.gif', 'alt': "Change language"});
    img.googie_action_btn = "1";
    var switch_lan = AJS.SPAN({'class': 'googie_lang_3d_on', 'style': 'padding-left: 6px;'}, img);
this.createChangeLangPic = function() {
    var img = $('<img>')
    .attr({src: this.img_dir + 'change_lang.gif', 'alt': 'Change language', 'googie_action_btn': '1'});
    var fn = function(e) {
        var elm = AJS.getEventElm(e);
        if(AJS.nodeName(elm) == 'img')
            elm = elm.parentNode;
        if(elm.className == "googie_lang_3d_click") {
            elm.className = "googie_lang_3d_on";
            this.hideLangWindow();
        }
        else {
            elm.className = "googie_lang_3d_click";
            this.showLangWindow(switch_lan);
        }
    }
    var switch_lan = document.createElement('SPAN');
    var ref = this;
    AJS.AEV(switch_lan, "click", AJS.$b(fn, this));
    $(switch_lan).addClass('googie_lang_3d_on')
    .append(img)
    .bind('click', function(e) {
            var elm = this.tagName == 'IMG' ? this.parentNode : this;
            if($(elm).hasClass('googie_lang_3d_click')) {
            elm.className = 'googie_lang_3d_on';
            ref.hideLangWindow();
            }
            else {
            elm.className = 'googie_lang_3d_click';
            ref.showLangWindow(elm);
            }
    });
    return switch_lan;
}
GoogieSpell.prototype.createSpellDiv = function() {
    var chk_spell = AJS.SPAN({'class': 'googie_check_spelling_link'});
this.createSpellDiv = function() {
    var span = document.createElement('SPAN');
    chk_spell.innerHTML = this.lang_chck_spell;
    var spell_img = null;
    if(this.show_spell_img)
        spell_img = AJS.IMG({'src': this.img_dir + "spellc.gif"});
    return AJS.SPAN(spell_img, " ", chk_spell);
    $(span).addClass('googie_check_spelling_link').text(this.lang_chck_spell);
    if (this.show_spell_img) {
    $(span).append(' ').append($('<img>').attr('src', this.img_dir + 'spellc.gif'));
    }
    return span;
}
//////
// State functions
/////
GoogieSpell.prototype.flashNoSpellingErrorState = function(on_finish) {
    var no_spell_errors;
this.flashNoSpellingErrorState = function(on_finish) {
    this.setStateChanged('no_error_found');
    if(on_finish) {
        var fn = function() {
            on_finish();
            this.checkSpellingState();
        };
        no_spell_errors = fn;
    }
    else
        no_spell_errors = this.checkSpellingState;
    var ref = this;
    if (this.main_controller) {
    var no_spell_errors;
    if (on_finish) {
            var fn = function() {
            on_finish();
            ref.checkSpellingState();
            };
            no_spell_errors = fn;
    }
    else
            no_spell_errors = function () { ref.checkSpellingState() };
    this.setStateChanged("no_error_found");
        var rsm = $('<span>').text(this.lang_no_error_found);
        $(this.switch_lan_pic).hide();
    $(this.spell_span).empty().append(rsm)
        .removeClass().addClass('googie_check_spelling_ok');
    if(this.main_controller) {
        AJS.hideElement(this.switch_lan_pic);
        var dummy = AJS.IMG({'src': this.img_dir + "blank.gif", 'style': 'height: 16px; width: 1px;'});
        var rsm = AJS.SPAN();
        rsm.innerHTML = this.lang_no_error_found;
        AJS.RCN(this.spell_span, AJS.SPAN(dummy, rsm));
        this.spell_span.className = "googie_check_spelling_ok";
        this.spell_span.style.textDecoration = "none";
        this.spell_span.style.cursor = "default";
        AJS.callLater(AJS.$b(no_spell_errors, this), 1200, [false]);
        window.setTimeout(no_spell_errors, 1000);
    }
}
GoogieSpell.prototype.resumeEditingState = function() {
    this.setStateChanged("resume_editing");
this.resumeEditingState = function() {
    this.setStateChanged('resume_editing');
    //Change link text to resume
    if(this.main_controller) {
        AJS.hideElement(this.switch_lan_pic);
        var dummy = AJS.IMG({'src': this.img_dir + "blank.gif", 'style': 'height: 16px; width: 1px;'});
        var rsm = AJS.SPAN();
        rsm.innerHTML = this.lang_rsm_edt;
        AJS.RCN(this.spell_span, AJS.SPAN(dummy, rsm));
        var fn = function(e) {
            this.resumeEditing();
        }
        this.spell_span.onclick = AJS.$b(fn, this);
    if (this.main_controller) {
        var rsm = $('<span>').text(this.lang_rsm_edt);
    var ref = this;
        this.spell_span.className = "googie_resume_editing";
        $(this.switch_lan_pic).hide();
        $(this.spell_span).empty().unbind().append(rsm)
            .bind('click', function() { ref.resumeEditing() })
            .removeClass().addClass('googie_resume_editing');
    }
    try { this.edit_layer.scrollTop = this.ta_scroll_top; }
    catch(e) { }
    catch (e) {};
}
GoogieSpell.prototype.checkSpellingState = function(fire) {
    if(!AJS.isDefined(fire) || fire)
        this.setStateChanged("spell_check");
this.checkSpellingState = function(fire) {
    if (fire)
        this.setStateChanged('ready');
    if(this.show_change_lang_pic)
    if (this.show_change_lang_pic)
        this.switch_lan_pic = this.createChangeLangPic();
    else
        this.switch_lan_pic = AJS.SPAN();
        this.switch_lan_pic = document.createElement('SPAN');
    var span_chck = this.createSpellDiv();
    var fn = function() {
        this.spellCheck();
    };
    var ref = this;
    if(this.custom_spellcheck_starter)
        span_chck.onclick = this.custom_spellcheck_starter;
    if (this.custom_spellcheck_starter)
        $(span_chck).bind('click', function(e) { ref.custom_spellcheck_starter() });
    else {
        span_chck.onclick = AJS.$b(fn, this);
        $(span_chck).bind('click', function(e) { ref.spellCheck() });
    }
    if (this.main_controller) {
        if (this.change_lang_pic_placement == 'left') {
        $(this.spell_container).empty().append(this.switch_lan_pic).append(' ').append(span_chck);
        } else {
        $(this.spell_container).empty().append(span_chck).append(' ').append(this.switch_lan_pic);
    }
    }
    this.spell_span = span_chck;
    if(this.main_controller) {
        if(this.change_lang_pic_placement == "left")
            AJS.RCN(this.spell_container, span_chck, " ", this.switch_lan_pic);
        else
            AJS.RCN(this.spell_container, this.switch_lan_pic, " ", span_chck);
    }
    // modified by roundcube
    this.check_link = span_chck;
}
//////
// Misc. functions
/////
GoogieSpell.item_onmouseover = function(e) {
    var elm = AJS.getEventElm(e);
    if(elm.className != "googie_list_revert" && elm.className != "googie_list_close")
        elm.className = "googie_list_onhover";
    else
        elm.parentNode.className = "googie_list_onhover";
}
GoogieSpell.item_onmouseout = function(e) {
    var elm = AJS.getEventElm(e);
    if(elm.className != "googie_list_revert" && elm.className != "googie_list_close")
        elm.className = "googie_list_onout";
    else
        elm.parentNode.className = "googie_list_onout";
this.isDefined = function(o) {
    return (o != 'undefined' && o != null)
}
GoogieSpell.prototype.createCloseButton = function(c_fn) {
    return this.createButton(this.lang_close, 'googie_list_close', AJS.$b(c_fn, this));
this.errorFixed = function() {
    this.cnt_errors_fixed++;
    if (this.all_errors_fixed_observer)
        if (this.cnt_errors_fixed == this.cnt_errors) {
            this.hideErrorWindow();
            this.all_errors_fixed_observer();
        }
}
GoogieSpell.prototype.createButton = function(name, css_class, c_fn) {
    var btn_row = AJS.TR();
    var btn = AJS.TD();
this.errorFound = function() {
    this.cnt_errors++;
}
    btn.onmouseover = GoogieSpell.item_onmouseover;
    btn.onmouseout = GoogieSpell.item_onmouseout;
this.createCloseButton = function(c_fn) {
    return this.createButton(this.lang_close, 'googie_list_close', c_fn);
}
this.createButton = function(name, css_class, c_fn) {
    var btn_row = document.createElement('TR');
    var btn = document.createElement('TD');
    var spn_btn;
    if(css_class != "") {
        spn_btn = AJS.SPAN({'class': css_class});
        spn_btn.innerHTML = name;
    if (css_class) {
        spn_btn = document.createElement('SPAN');
    $(spn_btn).addClass(css_class).html(name);
    } else {
        spn_btn = document.createTextNode(name);
    }
    else {
        spn_btn = AJS.TN(name);
    }
    $(btn).bind('click', c_fn)
    .bind('mouseover', this.item_onmouseover)
    .bind('mouseout', this.item_onmouseout);
    btn.appendChild(spn_btn);
    AJS.AEV(btn, "click", c_fn);
    btn_row.appendChild(btn);
    return btn_row;
}
GoogieSpell.prototype.removeIndicator = function(elm) {
    // modified by roundcube
this.removeIndicator = function(elm) {
    //$(this.indicator).remove();
    // roundcube mod.
    if (window.rcmail)
        rcmail.set_busy(false);
    //try { AJS.removeElement(this.indicator); }
    //catch(e) {}
    rcmail.set_busy(false);
}
GoogieSpell.prototype.appendIndicator = function(elm) {
this.appendIndicator = function(elm) {
    // modified by roundcube
    if (window.rcmail)
        rcmail.set_busy(true, 'checking');
  /*
    var img = AJS.IMG({'src': this.img_dir + 'indicator.gif', 'style': 'margin-right: 5px;'});
    AJS.setWidth(img, 16);
    AJS.setHeight(img, 16);
    this.indicator = img;
    img.style.textDecoration = "none";
    try {
        AJS.insertBefore(img, elm);
    }
    catch(e) {}
  */
    rcmail.set_busy(true, 'checking');
/*
    this.indicator = document.createElement('IMG');
    $(this.indicator).attr('src', this.img_dir + 'indicator.gif')
    .css({'margin-right': '5px', 'text-decoration': 'none'}).width(16).height(16);
    if (elm)
    $(this.indicator).insertBefore(elm);
    else
    $('body').append(this.indicator);
*/
}
GoogieSpell.prototype.createFocusLink = function(name) {
    return AJS.A({'href': 'javascript:;', name: name});
this.createFocusLink = function(name) {
    var link = document.createElement('A');
    $(link).attr({'href': 'javascript:;', 'name': name});
    return link;
}
this.item_onmouseover = function(e) {
    if (this.className != "googie_list_revert" && this.className != "googie_list_close")
        this.className = "googie_list_onhover";
    else
        this.parentNode.className = "googie_list_onhover";
}
this.item_onmouseout = function(e) {
    if (this.className != "googie_list_revert" && this.className != "googie_list_close")
        this.className = "googie_list_onout";
    else
        this.parentNode.className = "googie_list_onout";
}
};
program/steps/mail/compose.inc
@@ -433,6 +433,7 @@
      "googie.lang_no_error_found = \"%s\";\n".
      "googie.setLanguages(%s);\n".
      "googie.setCurrentLanguage('%s');\n".
      "googie.setSpellContainer('spellcheck-control');\n".
      "googie.decorateTextarea('%s');\n".
      "%s.set_env('spellcheck', googie);",
      $RCMAIL->comm_path,
@@ -834,23 +835,26 @@
{
  global $CONFIG, $MESSAGE, $compose_mode;
  $choices = array(
    'html'  => 'htmltoggle',
    'plain' => 'plaintoggle'
  );
  // determine whether HTML or plain text should be checked
  $useHtml = $CONFIG['htmleditor'] ? true : false;
  if ($compose_mode)
    $useHtml = ($useHtml && $MESSAGE->has_html_part());
  $editorid = empty($attrib['editorid']) ? 'rcmComposeMessage' : $attrib['editorid'];
  if (empty($attrib['editorid']))
    $attrib['editorid'] = 'rcmComposeMessage';
  $selector = '';
  $chosenvalue = $useHtml ? 'html' : 'plain';
  $radio = new html_radiobutton(array('name' => '_editorSelect',
    'onclick' => "return rcmail_toggle_editor(this.value=='html', '$editorid', '_is_html')"));
  if (empty($attrib['name']))
    $attrib['name'] = 'editorSelect';
    $attrib['onchange'] = "return rcmail_toggle_editor(this.value=='html', '".$attrib['editorid']."', '_is_html')";
  $select = new html_select($attrib);
  $select->add(Q(rcube_label('htmltoggle')), 'html');
  $select->add(Q(rcube_label('plaintoggle')), 'plain');
  return $select->show($useHtml ? 'html' : 'plain');
  foreach ($choices as $value => $text)
  {
skins/default/common.css
@@ -286,11 +286,6 @@
  background-image: url(images/listheader_aqua.gif);
}
.radios-left label
{
  padding-left: 0.3em;
}
/***** common table settings ******/
table.records-table thead tr td
skins/default/googiespell.css
@@ -12,6 +12,20 @@
  visibility: hidden;
}
.googie_edit_layer {
  border: 1px solid #666666;
  background-color: #ffffff;
  padding: 1px 4px;
  margin: 1px 0px;
  font-size: 9pt;
  font-family: "Courier New", Courier, monospace;
}
.googie_edit_layer span {
  font-size: 9pt;
  font-family: "Courier New", Courier, monospace;
}
.googie_list {
  width: 100%;
  margin: 0;
@@ -62,34 +76,33 @@
  cursor: pointer;
}
.googie_resume_editing,
.googie_check_spelling_link {
.googie_check_spelling_link {
  color: #CC0000;
  font-size: 11px;
  text-decoration: none;
  cursor: pointer;
}
.googie_resume_editing:hover,
.googie_check_spelling_link:hover {
  text-decoration: underline;
}
.googie_resume_editing {
  color: green;
}
.googie_no_style {
  text-decoration: none;
}
.googie_check_spelling_ok {
.googie_check_spelling_ok,
.googie_resume_editing {
  color: green;
  font-size: 11px;
  text-decoration: underline;
  cursor: pointer;
}
.googie_check_spelling_ok:hover,
.googie_resume_editing:hover {
  text-decoration: underline;
}
.googie_lang_3d_click img {
  vertical-align: middle;
  border-top: 1px solid #555;
skins/default/mail.css
@@ -19,11 +19,13 @@
  padding-right: 10px;
}
#messagetoolbar select
#messagetoolbar select,
#compose-container select
{
  font-family: "Lucida Grande", Verdana, Arial, Helvetica, sans-serif;
  font-size: 11px;
  color: #333333;
  height: 21px;
}
#messagetoolbar select.mboxlist
@@ -913,6 +915,7 @@
#receipt-selector
{
  padding-left: 30px;
  white-space: nowrap;
}
#compose-container
@@ -928,13 +931,25 @@
  height: expression((parseInt(document.documentElement.clientHeight)-120)+'px');
}
#spellcheck-control
{
  text-align: right;
  padding-top: 3px;
}
#editor-select
{
  float: left;
}
#compose-div
{
  position: absolute;
  top: 110px;
  bottom: 40px;
  top: 130px;
  bottom: 30px;
  width: 100%;
  vertical-align: top;
  padding-top: 2px;
}
#compose-headers
skins/default/templates/compose.html
@@ -66,24 +66,26 @@
        </tr><tr>
        <td class="title"><label for="compose-subject"><roundcube:label name="subject" /></label></td>
        <td><roundcube:object name="composeSubject" id="compose-subject" form="form" tabindex="6" /></td>
        </tr><tr>
        <td class="title"><roundcube:label name="editortype" /></td>
        <td>
            <div id="editor-select"><roundcube:object name="editorSelector" editorid="compose-body" tabindex="7" /></div>
            <div id="spellcheck-control"></div>
        </td>
        </tr>
    </tbody>
    </table>
    </div>
    <div id="compose-div">
    <roundcube:object name="composeBody" id="compose-body" form="form" cols="70" rows="20" tabindex="7" />
    <roundcube:object name="composeBody" id="compose-body" form="form" cols="70" rows="20" tabindex="8" />
    <table border="0" cellspacing="0" summary="" style="width:100%; margin-top: 5px;"><tbody>
        <tr>
        <td style="white-space: nowrap">
            <roundcube:button type="input" command="send" class="button mainaction" label="sendmessage" tabindex="8" />
            <roundcube:button type="input" command="list" class="button" label="cancel" tabindex="9" />
        </td>
        <td style="text-align:center; white-space: nowrap">
            <label><roundcube:label name="savesentmessagein" />: <roundcube:object name="storetarget" maxlength="30" /></label>
        </td>
        <td style="text-align:right; white-space:nowrap">
            <roundcube:label name="editortype" />:&nbsp;
            <span class="radios-left"><roundcube:object name="editorSelector" editorid="compose-body" tabindex="10" /></span>
        <td style="text-align:right; white-space: nowrap">
            <label><roundcube:label name="savesentmessagein" />: <roundcube:object name="storetarget" maxlength="30" tabindex="10" /></label>
        </td>
        </tr>
    </tbody></table>