Changeset ffa6c10 in github


Ignore:
Timestamp:
May 1, 2009 12:58:25 PM (4 years ago)
Author:
thomascube <thomas@…>
Branches:
master, HEAD, courier-fix, dev-browser-capabilities, pdo, release-0.6, release-0.7, release-0.8
Children:
762a699
Parents:
82475f4
Message:

Update Googiespell to version 4.0

Files:
3 edited

Legend:

Unmodified
Added
Removed
  • program/js/app.js

    r23387ef rffa6c10  
    903903        } 
    904904        else if (this.env.spellcheck && this.env.spellcheck.spellCheck && this.spellcheck_ready) { 
    905           this.env.spellcheck.spellCheck(this.env.spellcheck.check_link); 
    906           this.set_spellcheck_state('checking'); 
     905          this.env.spellcheck.spellCheck(); 
    907906        } 
    908907        break; 
     
    21242123      // stop spellchecking process 
    21252124      if (!vis) 
    2126         this.stop_spellchecking(); 
    2127                                
     2125        this.stop_spellchecking(); 
     2126 
    21282127      this.env.spellcheck.check_link.style.visibility = vis ? 'visible' : 'hidden'; 
    21292128      this.env.spellcheck.switch_lan_pic.style.visibility = vis ? 'visible' : 'hidden'; 
     
    21332132  this.set_spellcheck_state = function(s) 
    21342133    { 
    2135     this.spellcheck_ready = (s=='check_spelling' || s=='ready'); 
     2134    this.spellcheck_ready = (s=='check_spelling' || s=='spell_check' || s=='ready'); 
    21362135    this.enable_command('spellcheck', this.spellcheck_ready); 
    21372136    }; 
  • program/js/googiespell.js

    r2c63370 rffa6c10  
    11/* 
    2 Last Modified: 28/04/06 16:28:09 
    3  
    4   AmiJs library 
    5     A very small library with DOM and Ajax functions. 
    6     For a much larger script look on http://www.mochikit.com/ 
    7   AUTHOR 
     2Last Modified: 29/04/07 18:44:48 
     3 
     4AJS JavaScript library 
     5    A very small library with a lot of functionality 
     6AUTHOR 
    87    4mir Salihefendic (http://amix.dk) - amix@amix.dk 
    9   LICENSE 
     8LICENSE 
    109    Copyright (c) 2006 Amir Salihefendic. All rights reserved. 
    1110    Copyright (c) 2005 Bob Ippolito. All rights reserved. 
    1211    http://www.opensource.org/licenses/mit-license.php 
    13   VERSION 
    14     2.1 
    15   SITE 
    16     http://amix.dk/amijs 
     12VERSION 
     13    4.0 
     14SITE 
     15    http://orangoo.com/AmiNation/AJS 
    1716**/ 
    18  
     17if(!AJS) { 
    1918var AJS = { 
     19    BASE_URL: "", 
     20 
     21    drag_obj: null, 
     22    drag_elm: null, 
     23    _drop_zones: [], 
     24    _drag_zones: [], 
     25    _cur_pos: null, 
     26 
     27    ajaxErrorHandler: null, 
     28 
    2029//// 
    21 // Accessor functions 
     30// General accessor functions 
    2231//// 
    23   /** 
    24    * @returns The element with the id 
    25    */ 
    26   getElement: function(id) { 
    27     if(typeof(id) == "string")  
    28       return document.getElementById(id); 
    29     else 
    30       return id; 
    31   }, 
    32  
    33   /** 
    34    * @returns The elements with the ids 
    35    */ 
    36   getElements: function(/*id1, id2, id3*/) { 
    37     var elements = new Array(); 
    38       for (var i = 0; i < arguments.length; i++) { 
    39         var element = this.getElement(arguments[i]); 
    40         elements.push(element); 
    41       } 
    42       return elements; 
    43   }, 
    44  
    45   /** 
    46    * @returns The GET query argument 
    47    */ 
    48   getQueryArgument: function(var_name) { 
    49     var query = window.location.search.substring(1); 
    50     var vars = query.split("&"); 
    51     for (var i=0;i<vars.length;i++) { 
    52       var pair = vars[i].split("="); 
    53       if (pair[0] == var_name) { 
    54         return pair[1]; 
    55       } 
    56     } 
    57     return null; 
    58   }, 
    59  
    60   /** 
    61    * @returns If the browser is Internet Explorer 
    62    */ 
    63   isIe: function() { 
    64     return (navigator.userAgent.toLowerCase().indexOf("msie") != -1 && navigator.userAgent.toLowerCase().indexOf("opera") == -1); 
    65   }, 
    66  
    67   /** 
    68    * @returns The document body    
    69    */ 
    70   getBody: function() { 
    71     return this.getElementsByTagAndClassName('body')[0]  
    72   }, 
    73  
    74   /** 
    75    * @returns All the elements that have a specific tag name or class name 
    76    */ 
    77   getElementsByTagAndClassName: function(tag_name, class_name, /*optional*/ parent) { 
    78     var class_elements = new Array(); 
    79     if(!this.isDefined(parent)) 
    80       parent = document; 
    81     if(!this.isDefined(tag_name)) 
    82       tag_name = '*'; 
    83  
    84     var els = parent.getElementsByTagName(tag_name); 
    85     var els_len = els.length; 
    86     var pattern = new RegExp("(^|\\s)" + class_name + "(\\s|$)"); 
    87  
    88     for (i = 0, j = 0; i < els_len; i++) { 
    89       if ( pattern.test(els[i].className) || class_name == null ) { 
    90         class_elements[j] = els[i]; 
    91         j++; 
    92       } 
    93     } 
    94     return class_elements; 
    95   }, 
     32    getQueryArgument: function(var_name) { 
     33        var query = window.location.search.substring(1); 
     34        var vars = query.split("&"); 
     35        for (var i=0;i<vars.length;i++) { 
     36            var pair = vars[i].split("="); 
     37            if (pair[0] == var_name) { 
     38                return pair[1]; 
     39            } 
     40        } 
     41        return null; 
     42    }, 
     43 
     44    isIe: function() { 
     45        return (navigator.userAgent.toLowerCase().indexOf("msie") != -1 && navigator.userAgent.toLowerCase().indexOf("opera") == -1); 
     46    }, 
     47    isNetscape7: function() { 
     48        return (navigator.userAgent.toLowerCase().indexOf("netscape") != -1 && navigator.userAgent.toLowerCase().indexOf("7.") != -1); 
     49    }, 
     50    isSafari: function() { 
     51        return (navigator.userAgent.toLowerCase().indexOf("khtml") != -1); 
     52    }, 
     53    isOpera: function() { 
     54        return (navigator.userAgent.toLowerCase().indexOf("opera") != -1); 
     55    }, 
     56    isMozilla: function() { 
     57        return (navigator.userAgent.toLowerCase().indexOf("gecko") != -1 && navigator.productSub >= 20030210); 
     58    }, 
     59    isMac: function() { 
     60        return (navigator.userAgent.toLowerCase().indexOf('macintosh') != -1); 
     61    }, 
    9662 
    9763 
    9864//// 
    99 // DOM manipulation 
     65// Array functions 
    10066//// 
    101   /** 
    102    * Appends some nodes to a node 
    103    */ 
    104   appendChildNodes: function(node/*, nodes...*/) { 
    105     if(arguments.length >= 2) { 
    106       for(var i=1; i < arguments.length; i++) { 
    107         var n = arguments[i]; 
    108         if(typeof(n) == "string") 
    109           n = document.createTextNode(n); 
    110         if(this.isDefined(n)) 
    111           node.appendChild(n); 
    112       } 
    113     } 
    114     return node; 
    115   }, 
    116  
    117   /** 
    118    * Replaces a nodes children with another node(s) 
    119    */ 
    120   replaceChildNodes: function(node/*, nodes...*/) { 
    121     var child; 
    122     while ((child = node.firstChild)) { 
    123       node.removeChild(child); 
    124     } 
    125     if (arguments.length < 2) { 
    126       return node; 
    127     } else { 
    128       return this.appendChildNodes.apply(this, arguments); 
    129     } 
    130   }, 
    131  
    132   /** 
    133    * Insert a node after another node 
    134    */ 
    135   insertAfter: function(node, referenceNode) { 
    136     referenceNode.parentNode.insertBefore(node, referenceNode.nextSibling); 
    137   }, 
    138    
    139   /** 
    140    * Insert a node before another node 
    141    */ 
    142   insertBefore: function(node, referenceNode) { 
    143     referenceNode.parentNode.insertBefore(node, referenceNode); 
    144   }, 
    145    
    146   /** 
    147    * Shows the element 
    148    */ 
    149   showElement: function(elm) { 
    150     elm.style.display = ''; 
    151   }, 
    152    
    153   /** 
    154    * Hides the element 
    155    */ 
    156   hideElement: function(elm) { 
    157     elm.style.display = 'none'; 
    158   }, 
    159  
    160   isElementHidden: function(elm) { 
    161     return elm.style.visibility == "hidden"; 
    162   }, 
    163    
    164   /** 
    165    * Swaps one element with another. To delete use swapDOM(elm, null) 
    166    */ 
    167   swapDOM: function(dest, src) { 
    168     dest = this.getElement(dest); 
    169     var parent = dest.parentNode; 
    170     if (src) { 
    171       src = this.getElement(src); 
    172       parent.replaceChild(src, dest); 
    173     } else { 
    174       parent.removeChild(dest); 
    175     } 
    176     return src; 
    177   }, 
    178  
    179   /** 
    180    * Removes an element from the world 
    181    */ 
    182   removeElement: function(elm) { 
    183     this.swapDOM(elm, null); 
    184   }, 
    185  
    186   /** 
    187    * @returns Is an object a dictionary? 
    188    */ 
    189   isDict: function(o) { 
    190     var str_repr = String(o); 
    191     return str_repr.indexOf(" Object") != -1; 
    192   }, 
    193    
    194   /** 
    195    * Creates a DOM element 
    196    * @param {String} name The elements DOM name 
    197    * @param {Dict} attrs Attributes sent to the function 
    198    */ 
    199   createDOM: function(name, attrs) { 
    200     var i=0; 
    201     elm = document.createElement(name); 
    202  
    203     if(this.isDict(attrs[i])) { 
    204       for(k in attrs[0]) { 
    205         if(k == "style") 
    206           elm.style.cssText = attrs[0][k]; 
    207         else if(k == "class") 
    208           elm.className = attrs[0][k]; 
     67    //Shortcut: AJS.$A 
     68    createArray: function(v) { 
     69        if(AJS.isArray(v) && !AJS.isString(v)) 
     70            return v; 
     71        else if(!v) 
     72            return []; 
    20973        else 
    210           elm.setAttribute(k, attrs[0][k]); 
    211       } 
    212       i++; 
    213     } 
    214  
    215     if(attrs[0] == null) 
    216       i = 1; 
    217  
    218     for(i; i < attrs.length; i++) { 
    219       var n = attrs[i]; 
    220       if(this.isDefined(n)) { 
    221         if(typeof(n) == "string") 
    222           n = document.createTextNode(n); 
    223         elm.appendChild(n); 
    224       } 
    225     } 
    226     return elm; 
    227   }, 
    228  
    229   UL: function() { return this.createDOM.apply(this, ["ul", arguments]); }, 
    230   LI: function() { return this.createDOM.apply(this, ["li", arguments]); }, 
    231   TD: function() { return this.createDOM.apply(this, ["td", arguments]); }, 
    232   TR: function() { return this.createDOM.apply(this, ["tr", arguments]); }, 
    233   TH: function() { return this.createDOM.apply(this, ["th", arguments]); }, 
    234   TBODY: function() { return this.createDOM.apply(this, ["tbody", arguments]); }, 
    235   TABLE: function() { return this.createDOM.apply(this, ["table", arguments]); }, 
    236   INPUT: function() { return this.createDOM.apply(this, ["input", arguments]); }, 
    237   SPAN: function() { return this.createDOM.apply(this, ["span", arguments]); }, 
    238   B: function() { return this.createDOM.apply(this, ["b", arguments]); }, 
    239   A: function() { return this.createDOM.apply(this, ["a", arguments]); }, 
    240   DIV: function() { return this.createDOM.apply(this, ["div", arguments]); }, 
    241   IMG: function() { return this.createDOM.apply(this, ["img", arguments]); }, 
    242   BUTTON: function() { return this.createDOM.apply(this, ["button", arguments]); }, 
    243   H1: function() { return this.createDOM.apply(this, ["h1", arguments]); }, 
    244   H2: function() { return this.createDOM.apply(this, ["h2", arguments]); }, 
    245   H3: function() { return this.createDOM.apply(this, ["h3", arguments]); }, 
    246   BR: function() { return this.createDOM.apply(this, ["br", arguments]); }, 
    247   TEXTAREA: function() { return this.createDOM.apply(this, ["textarea", arguments]); }, 
    248   FORM: function() { return this.createDOM.apply(this, ["form", arguments]); }, 
    249   P: function() { return this.createDOM.apply(this, ["p", arguments]); }, 
    250   SELECT: function() { return this.createDOM.apply(this, ["select", arguments]); }, 
    251   OPTION: function() { return this.createDOM.apply(this, ["option", arguments]); }, 
    252   TN: function(text) { return document.createTextNode(text); }, 
    253   IFRAME: function() { return this.createDOM.apply(this, ["iframe", arguments]); }, 
    254   SCRIPT: function() { return this.createDOM.apply(this, ["script", arguments]); }, 
     74            return [v]; 
     75    }, 
     76 
     77    forceArray: function(args) { 
     78        var r = []; 
     79        AJS.map(args, function(elm) { 
     80            r.push(elm); 
     81        }); 
     82        return r; 
     83    }, 
     84 
     85    join: function(delim, list) { 
     86        try { 
     87            return list.join(delim); 
     88        } 
     89        catch(e) { 
     90            var r = list[0] || ''; 
     91            AJS.map(list, function(elm) { 
     92                r += delim + elm; 
     93            }, 1); 
     94            return r + ''; 
     95        } 
     96    }, 
     97 
     98    isIn: function(elm, list) { 
     99        var i = AJS.getIndex(elm, list); 
     100        if(i != -1) 
     101            return true; 
     102        else 
     103            return false; 
     104    }, 
     105 
     106    getIndex: function(elm, list/*optional*/, eval_fn) { 
     107        for(var i=0; i < list.length; i++) 
     108            if(eval_fn && eval_fn(list[i]) || elm == list[i]) 
     109                return i; 
     110        return -1; 
     111    }, 
     112 
     113    getFirst: function(list) { 
     114        if(list.length > 0) 
     115            return list[0]; 
     116        else 
     117            return null; 
     118    }, 
     119 
     120    getLast: function(list) { 
     121        if(list.length > 0) 
     122            return list[list.length-1]; 
     123        else 
     124            return null; 
     125    }, 
     126 
     127    update: function(l1, l2) { 
     128        for(var i in l2) 
     129            l1[i] = l2[i]; 
     130        return l1; 
     131    }, 
     132 
     133    flattenList: function(list) { 
     134        var r = []; 
     135        var _flatten = function(r, l) { 
     136            AJS.map(l, function(o) { 
     137                if(o == null) {} 
     138                else if (AJS.isArray(o)) 
     139                    _flatten(r, o); 
     140                else 
     141                    r.push(o); 
     142            }); 
     143        } 
     144        _flatten(r, list); 
     145        return r; 
     146    }, 
     147 
     148 
     149//// 
     150// Functional programming 
     151//// 
     152    map: function(list, fn,/*optional*/ start_index, end_index) { 
     153        var i = 0, l = list.length; 
     154        if(start_index) 
     155             i = start_index; 
     156        if(end_index) 
     157             l = end_index; 
     158        for(i; i < l; i++) { 
     159            var val = fn.apply(null, [list[i], i]); 
     160            if(val != undefined) 
     161                return val; 
     162        } 
     163    }, 
     164 
     165    rmap: function(list, fn) { 
     166        var i = list.length-1, l = 0; 
     167        for(i; i >= l; i--) { 
     168            var val = fn.apply(null, [list[i], i]); 
     169            if(val != undefined) 
     170                return val; 
     171        } 
     172    }, 
     173 
     174    filter: function(list, fn, /*optional*/ start_index, end_index) { 
     175        var r = []; 
     176        AJS.map(list, function(elm) { 
     177            if(fn(elm)) 
     178                r.push(elm); 
     179        }, start_index, end_index); 
     180        return r; 
     181    }, 
     182 
     183    partial: function(fn) { 
     184        var args = AJS.$FA(arguments); 
     185        args.shift(); 
     186        return function() { 
     187            args = args.concat(AJS.$FA(arguments)); 
     188            return fn.apply(window, args); 
     189        } 
     190    }, 
     191 
     192 
     193//// 
     194// DOM functions 
     195//// 
     196    //Shortcut: AJS.$ 
     197    getElement: function(id) { 
     198        if(AJS.isString(id) || AJS.isNumber(id)) 
     199            return document.getElementById(id); 
     200        else 
     201            return id; 
     202    }, 
     203 
     204    //Shortcut: AJS.$$ 
     205    getElements: function(/*id1, id2, id3*/) { 
     206        var args = AJS.forceArray(arguments); 
     207        var elements = new Array(); 
     208            for (var i = 0; i < args.length; i++) { 
     209                var element = AJS.getElement(args[i]); 
     210                elements.push(element); 
     211            } 
     212            return elements; 
     213    }, 
     214 
     215    //Shortcut: AJS.$bytc 
     216    getElementsByTagAndClassName: function(tag_name, class_name, /*optional*/ parent) { 
     217        var class_elements = []; 
     218        if(!AJS.isDefined(parent)) 
     219            parent = document; 
     220        if(!AJS.isDefined(tag_name)) 
     221            tag_name = '*'; 
     222 
     223        var els = parent.getElementsByTagName(tag_name); 
     224        var els_len = els.length; 
     225        var pattern = new RegExp("(^|\\s)" + class_name + "(\\s|$)"); 
     226 
     227        for (i = 0, j = 0; i < els_len; i++) { 
     228            if ( pattern.test(els[i].className) || class_name == null ) { 
     229                class_elements[j] = els[i]; 
     230                j++; 
     231            } 
     232        } 
     233        return class_elements; 
     234    }, 
     235 
     236    _nodeWalk: function(elm, tag_name, class_name, fn_next_elm) { 
     237        var p = fn_next_elm(elm); 
     238 
     239        var checkFn; 
     240        if(tag_name && class_name) { 
     241            checkFn = function(p) { 
     242                return AJS.nodeName(p) == tag_name && AJS.hasClass(p, class_name); 
     243            } 
     244        } 
     245        else if(tag_name) { 
     246            checkFn = function(p) { return AJS.nodeName(p) == tag_name; } 
     247        } 
     248        else { 
     249            checkFn = function(p) { return AJS.hasClass(p, class_name); } 
     250        } 
     251 
     252        while(p) { 
     253            if(checkFn(p)) 
     254                return p; 
     255            p = fn_next_elm(p); 
     256        } 
     257        return null; 
     258    }, 
     259 
     260    getParentBytc: function(elm, tag_name, class_name) { 
     261        return AJS._nodeWalk(elm, tag_name, class_name, function(m) { return m.parentNode; }); 
     262    }, 
     263 
     264    getPreviousSiblingBytc: function(elm, tag_name, class_name) { 
     265        return AJS._nodeWalk(elm, tag_name, class_name, function(m) { return m.previousSibling; }); 
     266    }, 
     267 
     268    getNextSiblingBytc: function(elm, tag_name, class_name) { 
     269        return AJS._nodeWalk(elm, tag_name, class_name, function(m) { return m.nextSibling; }); 
     270    }, 
     271 
     272    //Shortcut: AJS.$f 
     273    getFormElement: function(form, name) { 
     274        form = AJS.$(form); 
     275        var r = null; 
     276        AJS.map(form.elements, function(elm) { 
     277            if(elm.name && elm.name == name) 
     278                r = elm; 
     279        }); 
     280        return r; 
     281    }, 
     282 
     283    formContents: function(form) { 
     284        var form = AJS.$(form); 
     285        var r = {}; 
     286        var fn = function(elms) { 
     287            AJS.map(elms, function(e) { 
     288                if(e.name) 
     289                    r[e.name] = e.value || ''; 
     290            }); 
     291        } 
     292        fn(AJS.$bytc('input', null, form)); 
     293        fn(AJS.$bytc('textarea', null, form)); 
     294        return r; 
     295    }, 
     296 
     297    getBody: function() { 
     298        return AJS.$bytc('body')[0] 
     299    }, 
     300 
     301    nodeName: function(elm) { 
     302        return elm.nodeName.toLowerCase(); 
     303    }, 
     304 
     305    hasParent: function(elm, parent_to_consider, max_look_up) { 
     306        if(elm == parent_to_consider) 
     307            return true; 
     308        if(max_look_up == 0) 
     309            return false; 
     310        return AJS.hasParent(elm.parentNode, parent_to_consider, max_look_up-1); 
     311    }, 
     312 
     313    isElementHidden: function(elm) { 
     314        return ((elm.style.display == "none") || (elm.style.visibility == "hidden")); 
     315    }, 
     316 
     317    //Shortcut: AJS.DI 
     318    documentInsert: function(elm) { 
     319        if(typeof(elm) == 'string') 
     320            elm = AJS.HTML2DOM(elm); 
     321        document.write('<span id="dummy_holder"></span>'); 
     322        AJS.swapDOM(AJS.$('dummy_holder'), elm); 
     323    }, 
     324 
     325    cloner: function(element) { 
     326        return function() { 
     327            return element.cloneNode(true); 
     328        } 
     329    }, 
     330 
     331    appendToTop: function(elm/*, elms...*/) { 
     332        var args = AJS.forceArray(arguments).slice(1); 
     333        if(args.length >= 1) { 
     334            var first_child = elm.firstChild; 
     335            if(first_child) { 
     336                while(true) { 
     337                    var t_elm = args.shift(); 
     338                    if(t_elm) 
     339                        AJS.insertBefore(t_elm, first_child); 
     340                    else 
     341                        break; 
     342                } 
     343            } 
     344            else { 
     345                AJS.ACN.apply(null, arguments); 
     346            } 
     347        } 
     348        return elm; 
     349    }, 
     350 
     351    //Shortcut: AJS.ACN 
     352    appendChildNodes: function(elm/*, elms...*/) { 
     353        if(arguments.length >= 2) { 
     354            AJS.map(arguments, function(n) { 
     355                if(AJS.isString(n)) 
     356                    n = AJS.TN(n); 
     357                if(AJS.isDefined(n)) 
     358                    elm.appendChild(n); 
     359            }, 1); 
     360        } 
     361        return elm; 
     362    }, 
     363 
     364    //Shortcut: AJS.RCN 
     365    replaceChildNodes: function(elm/*, elms...*/) { 
     366        var child; 
     367        while ((child = elm.firstChild)) 
     368            elm.removeChild(child); 
     369        if (arguments.length < 2) 
     370            return elm; 
     371        else 
     372            return AJS.appendChildNodes.apply(null, arguments); 
     373        return elm; 
     374    }, 
     375 
     376    insertAfter: function(elm, reference_elm) { 
     377        reference_elm.parentNode.insertBefore(elm, reference_elm.nextSibling); 
     378        return elm; 
     379    }, 
     380 
     381    insertBefore: function(elm, reference_elm) { 
     382        reference_elm.parentNode.insertBefore(elm, reference_elm); 
     383        return elm; 
     384    }, 
     385 
     386    showElement: function(/*elms...*/) { 
     387        var args = AJS.forceArray(arguments); 
     388        AJS.map(args, function(elm) { elm.style.display = ''}); 
     389    }, 
     390 
     391    hideElement: function(elm) { 
     392        var args = AJS.forceArray(arguments); 
     393        AJS.map(args, function(elm) { elm.style.display = 'none'}); 
     394    }, 
     395 
     396    swapDOM: function(dest, src) { 
     397        dest = AJS.getElement(dest); 
     398        var parent = dest.parentNode; 
     399        if (src) { 
     400            src = AJS.getElement(src); 
     401            parent.replaceChild(src, dest); 
     402        } else { 
     403            parent.removeChild(dest); 
     404        } 
     405        return src; 
     406    }, 
     407 
     408    removeElement: function(/*elm1, elm2...*/) { 
     409        var args = AJS.forceArray(arguments); 
     410        AJS.map(args, function(elm) { AJS.swapDOM(elm, null); }); 
     411    }, 
     412 
     413    createDOM: function(name, attrs) { 
     414        var i=0, attr; 
     415        elm = document.createElement(name); 
     416 
     417        if(AJS.isDict(attrs[i])) { 
     418            for(k in attrs[0]) { 
     419                attr = attrs[0][k]; 
     420                if(k == "style") 
     421                    elm.style.cssText = attr; 
     422                else if(k == "class" || k == 'className') 
     423                    elm.className = attr; 
     424                else { 
     425                    elm.setAttribute(k, attr); 
     426                } 
     427            } 
     428            i++; 
     429        } 
     430 
     431        if(attrs[0] == null) 
     432            i = 1; 
     433 
     434        AJS.map(attrs, function(n) { 
     435            if(n) { 
     436                if(AJS.isString(n) || AJS.isNumber(n)) 
     437                    n = AJS.TN(n); 
     438                elm.appendChild(n); 
     439            } 
     440        }, i); 
     441        return elm; 
     442    }, 
     443 
     444    _createDomShortcuts: function() { 
     445        var elms = [ 
     446                "ul", "li", "td", "tr", "th", 
     447                "tbody", "table", "input", "span", "b", 
     448                "a", "div", "img", "button", "h1", 
     449                "h2", "h3", "br", "textarea", "form", 
     450                "p", "select", "option", "optgroup", "iframe", "script", 
     451                "center", "dl", "dt", "dd", "small", 
     452                "pre" 
     453        ]; 
     454        var extends_ajs = function(elm) { 
     455            AJS[elm.toUpperCase()] = function() { 
     456                return AJS.createDOM.apply(null, [elm, arguments]);  
     457            }; 
     458        } 
     459        AJS.map(elms, extends_ajs); 
     460        AJS.TN = function(text) { return document.createTextNode(text) }; 
     461    }, 
     462 
     463    getCssDim: function(dim) { 
     464        if(AJS.isString(dim)) 
     465            return dim; 
     466        else 
     467            return dim + "px"; 
     468    }, 
     469    getCssProperty: function(elm, prop) { 
     470        elm = AJS.$(elm); 
     471        var y; 
     472        if(elm.currentStyle) 
     473            y = elm.currentStyle[prop]; 
     474        else if (window.getComputedStyle) 
     475            y = document.defaultView.getComputedStyle(elm,null).getPropertyValue(prop); 
     476        return y; 
     477    }, 
     478 
     479    setStyle: function(/*elm1, elm2..., property, new_value*/) { 
     480        var args = AJS.forceArray(arguments); 
     481        var new_val = args.pop(); 
     482        var property = args.pop(); 
     483        AJS.map(args, function(elm) {  
     484            elm.style[property] = AJS.getCssDim(new_val); 
     485        }); 
     486    }, 
     487 
     488    setWidth: function(/*elm1, elm2..., width*/) { 
     489        var args = AJS.forceArray(arguments); 
     490        args.splice(args.length-1, 0, 'width'); 
     491        AJS.setStyle.apply(null, args); 
     492    }, 
     493    setHeight: function(/*elm1, elm2..., height*/) { 
     494        var args = AJS.forceArray(arguments); 
     495        args.splice(args.length-1, 0, 'height'); 
     496        AJS.setStyle.apply(null, args); 
     497    }, 
     498    setLeft: function(/*elm1, elm2..., left*/) { 
     499        var args = AJS.forceArray(arguments); 
     500        args.splice(args.length-1, 0, 'left'); 
     501        AJS.setStyle.apply(null, args); 
     502    }, 
     503    setTop: function(/*elm1, elm2..., top*/) { 
     504        var args = AJS.forceArray(arguments); 
     505        args.splice(args.length-1, 0, 'top'); 
     506        AJS.setStyle.apply(null, args); 
     507    }, 
     508    setClass: function(/*elm1, elm2..., className*/) { 
     509        var args = AJS.forceArray(arguments); 
     510        var c = args.pop(); 
     511        AJS.map(args, function(elm) { elm.className = c}); 
     512    }, 
     513    addClass: function(/*elm1, elm2..., className*/) { 
     514        var args = AJS.forceArray(arguments); 
     515        var cls = args.pop(); 
     516        var add_class = function(o) { 
     517            if(!new RegExp("(^|\\s)" + cls + "(\\s|$)").test(o.className)) 
     518                o.className += (o.className ? " " : "") + cls; 
     519        }; 
     520        AJS.map(args, function(elm) { add_class(elm); }); 
     521    }, 
     522    hasClass: function(elm, cls) { 
     523        if(!elm.className) 
     524            return false; 
     525        return elm.className == cls ||  
     526               elm.className.search(new RegExp(" " + cls + "|^" + cls)) != -1 
     527    }, 
     528    removeClass: function(/*elm1, elm2..., className*/) { 
     529        var args = AJS.forceArray(arguments); 
     530        var cls = args.pop(); 
     531        var rm_class = function(o) { 
     532            o.className = o.className.replace(new RegExp("\\s?" + cls, 'g'), ""); 
     533        }; 
     534        AJS.map(args, function(elm) { rm_class(elm); }); 
     535    }, 
     536 
     537    setHTML: function(elm, html) { 
     538        elm.innerHTML = html; 
     539        return elm; 
     540    }, 
     541 
     542    RND: function(tmpl, ns, scope) { 
     543        scope = scope || window; 
     544        var fn = function(w, g) { 
     545            g = g.split("|"); 
     546            var cnt = ns[g[0]]; 
     547            for(var i=1; i < g.length; i++) 
     548                cnt = scope[g[i]](cnt); 
     549            if(cnt == '') 
     550                return ''; 
     551            if(cnt == 0 || cnt == -1) 
     552                cnt += ''; 
     553            return cnt || w; 
     554        }; 
     555        return tmpl.replace(/%\(([A-Za-z0-9_|.]*)\)/g, fn); 
     556    }, 
     557 
     558    HTML2DOM: function(html,/*optional*/ first_child) { 
     559        var d = AJS.DIV(); 
     560        d.innerHTML = html; 
     561        if(first_child) 
     562            return d.childNodes[0]; 
     563        else 
     564            return d; 
     565    }, 
     566 
     567    preloadImages: function(/*img_src1, ..., img_srcN*/) { 
     568        AJS.AEV(window, 'load', AJS.$p(function(args) { 
     569            AJS.map(args, function(src) { 
     570                var pic = new Image(); 
     571                pic.src = src; 
     572            }); 
     573        }, arguments)); 
     574    }, 
     575 
     576 
     577//// 
     578// Effects 
     579//// 
     580    setOpacity: function(elm, p) { 
     581        elm.style.opacity = p; 
     582        elm.style.filter = "alpha(opacity="+ p*100 +")"; 
     583    }, 
     584 
     585    resetOpacity: function(elm) { 
     586        elm.style.opacity = 1; 
     587        elm.style.filter = ""; 
     588    }, 
    255589 
    256590//// 
    257591// Ajax functions 
    258592//// 
    259   /** 
    260    * @returns A new XMLHttpRequest object  
    261    */ 
    262   getXMLHttpRequest: function() { 
    263     var try_these = [ 
    264       function () { return new XMLHttpRequest(); }, 
    265       function () { return new ActiveXObject('Msxml2.XMLHTTP'); }, 
    266       function () { return new ActiveXObject('Microsoft.XMLHTTP'); }, 
    267       function () { return new ActiveXObject('Msxml2.XMLHTTP.4.0'); }, 
    268       function () { throw "Browser does not support XMLHttpRequest"; } 
    269     ]; 
    270     for (var i = 0; i < try_these.length; i++) { 
    271       var func = try_these[i]; 
    272       try { 
    273         return func(); 
    274       } catch (e) { 
    275       } 
    276     } 
    277   }, 
    278    
    279   /** 
    280    * Use this function to do a simple HTTP Request 
    281    */ 
    282   doSimpleXMLHttpRequest: function(url) { 
    283     var req = this.getXMLHttpRequest(); 
    284     req.open("GET", url, true); 
    285     return this.sendXMLHttpRequest(req); 
    286   }, 
    287  
    288   getRequest: function(url, data) { 
    289     var req = this.getXMLHttpRequest(); 
    290     req.open("POST", url, true); 
    291     req.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); 
    292     return this.sendXMLHttpRequest(req); 
    293   }, 
    294  
    295   /** 
    296    * Send a XMLHttpRequest 
    297    */ 
    298   sendXMLHttpRequest: function(req, data) { 
    299     var d = new AJSDeferred(req); 
    300  
    301     var onreadystatechange = function () { 
    302       if (req.readyState == 4) { 
     593    getXMLHttpRequest: function() { 
     594        var try_these = [ 
     595            function () { return new XMLHttpRequest(); }, 
     596            function () { return new ActiveXObject('Msxml2.XMLHTTP'); }, 
     597            function () { return new ActiveXObject('Microsoft.XMLHTTP'); }, 
     598            function () { return new ActiveXObject('Msxml2.XMLHTTP.4.0'); }, 
     599            function () { throw "Browser does not support XMLHttpRequest"; } 
     600        ]; 
     601        for (var i = 0; i < try_these.length; i++) { 
     602            var func = try_these[i]; 
     603            try { 
     604                return func(); 
     605            } catch (e) { 
     606            } 
     607        } 
     608    }, 
     609 
     610    getRequest: function(url, data, type) { 
     611        if(!type) 
     612            type = "POST"; 
     613        var req = AJS.getXMLHttpRequest(); 
     614 
     615        if(url.indexOf("http://") == -1) { 
     616            if(AJS.BASE_URL != '') { 
     617                if(AJS.BASE_URL.lastIndexOf('/') != AJS.BASE_URL.length-1) 
     618                    AJS.BASE_URL += '/'; 
     619                url = AJS.BASE_URL + url; 
     620            } 
     621        } 
     622 
     623        req.open(type, url, true); 
     624        if(type == "POST") 
     625            req.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); 
     626        return AJS._sendXMLHttpRequest(req); 
     627    }, 
     628 
     629    _sendXMLHttpRequest: function(req, data) { 
     630        var d = new AJSDeferred(req); 
     631 
     632        var onreadystatechange = function () { 
     633            if (req.readyState == 4) { 
     634                var status = ''; 
     635                try { 
     636                    status = req.status; 
     637                } 
     638                catch(e) {}; 
     639                if(status == 200 || status == 304 || req.responseText == null) { 
     640                    d.callback(); 
     641                } 
     642                else { 
     643                    if(d.errbacks.length == 0) { 
     644                        if(AJS.ajaxErrorHandler) 
     645                            AJS.ajaxErrorHandler(req.responseText, req); 
     646                    } 
     647                    else  
     648                        d.errback(); 
     649                } 
     650            } 
     651        } 
     652        req.onreadystatechange = onreadystatechange; 
     653        return d; 
     654    }, 
     655 
     656    _reprString: function(o) { 
     657        return ('"' + o.replace(/(["\\])/g, '\\$1') + '"' 
     658        ).replace(/[\f]/g, "\\f" 
     659        ).replace(/[\b]/g, "\\b" 
     660        ).replace(/[\n]/g, "\\n" 
     661        ).replace(/[\t]/g, "\\t" 
     662        ).replace(/[\r]/g, "\\r"); 
     663    }, 
     664 
     665    _reprDate: function(db) { 
     666        var year = db.getFullYear(); 
     667        var dd = db.getDate(); 
     668        var mm = db.getMonth()+1; 
     669 
     670        var hh = db.getHours(); 
     671        var mins = db.getMinutes(); 
     672 
     673        function leadingZero(nr) { 
     674            if (nr < 10) nr = "0" + nr; 
     675            return nr; 
     676        } 
     677        if(hh == 24) hh = '00'; 
     678 
     679        var time = leadingZero(hh) + ':' + leadingZero(mins); 
     680        return '"' + year + '-' + mm + '-' + dd + 'T' + time + '"'; 
     681    }, 
     682 
     683    serializeJSON: function(o) { 
     684        var objtype = typeof(o); 
     685        if (objtype == "undefined") { 
     686            return "undefined"; 
     687        } else if (objtype == "number" || objtype == "boolean") { 
     688            return o + ""; 
     689        } else if (o === null) { 
     690            return "null"; 
     691        } 
     692        if (objtype == "string") { 
     693            return AJS._reprString(o); 
     694        } 
     695        if(objtype == 'object' && o.getFullYear) { 
     696            return AJS._reprDate(o); 
     697        } 
     698        var me = arguments.callee; 
     699        if (objtype != "function" && typeof(o.length) == "number") { 
     700            var res = []; 
     701            for (var i = 0; i < o.length; i++) { 
     702                var val = me(o[i]); 
     703                if (typeof(val) != "string") { 
     704                    val = "undefined"; 
     705                } 
     706                res.push(val); 
     707            } 
     708            return "[" + res.join(",") + "]"; 
     709        } 
     710        // it's a function with no adapter, bad 
     711        if (objtype == "function") 
     712            return null; 
     713        res = []; 
     714        for (var k in o) { 
     715            var useKey; 
     716            if (typeof(k) == "number") { 
     717                useKey = '"' + k + '"'; 
     718            } else if (typeof(k) == "string") { 
     719                useKey = AJS._reprString(k); 
     720            } else { 
     721                // skip non-string or number keys 
     722                continue; 
     723            } 
     724            val = me(o[k]); 
     725            if (typeof(val) != "string") { 
     726                // skip non-serializable values 
     727                continue; 
     728            } 
     729            res.push(useKey + ":" + val); 
     730        } 
     731        return "{" + res.join(",") + "}"; 
     732    }, 
     733 
     734    loadJSONDoc: function(url) { 
     735        var d = AJS.getRequest(url); 
     736        var eval_req = function(data, req) { 
     737            var text = req.responseText; 
     738            if(text == "Error") 
     739                d.errback(req); 
     740            else 
     741                return AJS.evalTxt(text); 
     742        }; 
     743        d.addCallback(eval_req); 
     744        return d; 
     745    }, 
     746 
     747    evalTxt: function(txt) { 
    303748        try { 
    304           var status = req.status; 
    305         } 
    306         catch(e) {}; 
    307         if(status == 200 || status == 304 || req.responseText == null) { 
    308           d.callback(req, data); 
     749            return eval('('+ txt + ')'); 
     750        } 
     751        catch(e) { 
     752            return eval(txt); 
     753        } 
     754    }, 
     755 
     756    evalScriptTags: function(html) { 
     757        var script_data = html.match(/<script.*?>((\n|\r|.)*?)<\/script>/g); 
     758        if(script_data != null) { 
     759            for(var i=0; i < script_data.length; i++) { 
     760                var script_only = script_data[i].replace(/<script.*?>/g, ""); 
     761                script_only = script_only.replace(/<\/script>/g, ""); 
     762                eval(script_only); 
     763            } 
     764        } 
     765    }, 
     766 
     767    queryArguments: function(data) { 
     768        var post_data = []; 
     769        for(k in data) 
     770            post_data.push(k + "=" + AJS.urlencode(data[k])); 
     771        return post_data.join("&"); 
     772    }, 
     773 
     774 
     775//// 
     776// Position and size 
     777//// 
     778    getMousePos: function(e) { 
     779        var posx = 0; 
     780        var posy = 0; 
     781        if (!e) var e = window.event; 
     782        if (e.pageX || e.pageY) { 
     783            posx = e.pageX; 
     784            posy = e.pageY; 
     785        } 
     786        else if (e.clientX || e.clientY) { 
     787            posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; 
     788            posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop; 
     789        } 
     790        return {x: posx, y: posy}; 
     791    }, 
     792 
     793    getScrollTop: function() { 
     794        //From: http://www.quirksmode.org/js/doctypes.html 
     795        var t; 
     796        if (document.documentElement && document.documentElement.scrollTop) 
     797                t = document.documentElement.scrollTop; 
     798        else if (document.body) 
     799                t = document.body.scrollTop; 
     800        return t; 
     801    }, 
     802 
     803    absolutePosition: function(elm) { 
     804        var posObj = {'x': elm.offsetLeft, 'y': elm.offsetTop}; 
     805 
     806        if(elm.offsetParent) { 
     807            var next = elm.offsetParent; 
     808            while(next) { 
     809                posObj.x += next.offsetLeft; 
     810                posObj.y += next.offsetTop; 
     811                next = next.offsetParent; 
     812            } 
     813        } 
     814        // safari bug 
     815        if (AJS.isSafari() && elm.style.position == 'absolute' ) { 
     816            posObj.x -= document.body.offsetLeft; 
     817            posObj.y -= document.body.offsetTop; 
     818        } 
     819        return posObj; 
     820    }, 
     821 
     822    getWindowSize: function(doc) { 
     823        doc = doc || document; 
     824        var win_w, win_h; 
     825        if (self.innerHeight) { 
     826            win_w = self.innerWidth; 
     827            win_h = self.innerHeight; 
     828        } else if (doc.documentElement && doc.documentElement.clientHeight) { 
     829            win_w = doc.documentElement.clientWidth; 
     830            win_h = doc.documentElement.clientHeight; 
     831        } else if (doc.body) { 
     832            win_w = doc.body.clientWidth; 
     833            win_h = doc.body.clientHeight; 
     834        } 
     835        return {'w': win_w, 'h': win_h}; 
     836    }, 
     837 
     838    isOverlapping: function(elm1, elm2) { 
     839        var pos_elm1 = AJS.absolutePosition(elm1); 
     840        var pos_elm2 = AJS.absolutePosition(elm2); 
     841 
     842        var top1 = pos_elm1.y; 
     843        var left1 = pos_elm1.x; 
     844        var right1 = left1 + elm1.offsetWidth; 
     845        var bottom1 = top1 + elm1.offsetHeight; 
     846        var top2 = pos_elm2.y; 
     847        var left2 = pos_elm2.x; 
     848        var right2 = left2 + elm2.offsetWidth; 
     849        var bottom2 = top2 + elm2.offsetHeight; 
     850        var getSign = function(v) { 
     851            if(v > 0) return "+"; 
     852            else if(v < 0) return "-"; 
     853            else return 0; 
     854        } 
     855 
     856        if ((getSign(top1 - bottom2) != getSign(bottom1 - top2)) && 
     857                (getSign(left1 - right2) != getSign(right1 - left2))) 
     858            return true; 
     859        return false; 
     860    }, 
     861 
     862 
     863//// 
     864// Events 
     865//// 
     866    getEventElm: function(e) { 
     867        if(e && !e.type && !e.keyCode) 
     868            return e 
     869        var targ; 
     870        if (!e) var e = window.event; 
     871        if (e.target) targ = e.target; 
     872        else if (e.srcElement) targ = e.srcElement; 
     873        if (targ.nodeType == 3) // defeat Safari bug 
     874            targ = targ.parentNode; 
     875        return targ; 
     876    }, 
     877 
     878    _getRealScope: function(fn, /*optional*/ extra_args) { 
     879        extra_args = AJS.$A(extra_args); 
     880        var scope = fn._cscope || window; 
     881 
     882        return function() { 
     883            var args = AJS.$FA(arguments).concat(extra_args); 
     884            return fn.apply(scope, args); 
     885        }; 
     886    }, 
     887 
     888    _unloadListeners: function() { 
     889        if(AJS.listeners) 
     890            AJS.map(AJS.listeners, function(elm, type, fn) { AJS.REV(elm, type, fn) }); 
     891        AJS.listeners = []; 
     892    }, 
     893 
     894    setEventKey: function(e) { 
     895        e.key = e.keyCode ? e.keyCode : e.charCode; 
     896 
     897        if(window.event) { 
     898            e.ctrl = window.event.ctrlKey; 
     899            e.shift = window.event.shiftKey; 
    309900        } 
    310901        else { 
    311           d.errback(); 
    312         } 
    313       } 
    314     } 
    315     req.onreadystatechange = onreadystatechange; 
    316     return d; 
    317   }, 
    318    
    319   /** 
    320    * Represent an object as a string 
    321    */ 
    322   reprString: function(o) { 
    323     return ('"' + o.replace(/(["\\])/g, '\\$1') + '"' 
    324     ).replace(/[\f]/g, "\\f" 
    325     ).replace(/[\b]/g, "\\b" 
    326     ).replace(/[\n]/g, "\\n" 
    327     ).replace(/[\t]/g, "\\t" 
    328     ).replace(/[\r]/g, "\\r"); 
    329   }, 
    330    
    331   /** 
    332    * Serialize an object to JSON notation 
    333    */ 
    334   serializeJSON: function(o) { 
    335     var objtype = typeof(o); 
    336     if (objtype == "undefined") { 
    337       return "undefined"; 
    338     } else if (objtype == "number" || objtype == "boolean") { 
    339       return o + ""; 
    340     } else if (o === null) { 
    341       return "null"; 
    342     } 
    343     if (objtype == "string") { 
    344       return this.reprString(o); 
    345     } 
    346     var me = arguments.callee; 
    347     var newObj; 
    348     if (typeof(o.__json__) == "function") { 
    349       newObj = o.__json__(); 
    350       if (o !== newObj) { 
    351         return me(newObj); 
    352       } 
    353     } 
    354     if (typeof(o.json) == "function") { 
    355       newObj = o.json(); 
    356       if (o !== newObj) { 
    357         return me(newObj); 
    358       } 
    359     } 
    360     if (objtype != "function" && typeof(o.length) == "number") { 
    361       var res = []; 
    362       for (var i = 0; i < o.length; i++) { 
    363         var val = me(o[i]); 
    364         if (typeof(val) != "string") { 
    365           val = "undefined"; 
    366         } 
    367         res.push(val); 
    368       } 
    369       return "[" + res.join(",") + "]"; 
    370     } 
    371     res = []; 
    372     for (var k in o) { 
    373       var useKey; 
    374       if (typeof(k) == "number") { 
    375         useKey = '"' + k + '"'; 
    376       } else if (typeof(k) == "string") { 
    377         useKey = this.reprString(k); 
    378       } else { 
    379         // skip non-string or number keys 
    380         continue; 
    381       } 
    382       val = me(o[k]); 
    383       if (typeof(val) != "string") { 
    384         // skip non-serializable values 
    385         continue; 
    386       } 
    387       res.push(useKey + ":" + val); 
    388     } 
    389     return "{" + res.join(",") + "}"; 
    390   }, 
    391  
    392   /** 
    393    * Send and recive JSON using GET 
    394    */ 
    395   loadJSONDoc: function(url) { 
    396     var d = this.getRequest(url); 
    397     var eval_req = function(req) { 
    398       var text = req.responseText; 
    399       return eval('(' + text + ')'); 
    400     }; 
    401     d.addCallback(eval_req); 
    402     return d; 
    403   }, 
    404    
    405    
     902            e.ctrl = e.ctrlKey; 
     903            e.shift = e.shiftKey; 
     904        } 
     905        switch(e.key) { 
     906            case 63232: 
     907                e.key = 38; 
     908                break; 
     909            case 63233: 
     910                e.key = 40; 
     911                break; 
     912            case 63235: 
     913                e.key = 39; 
     914                break; 
     915            case 63234: 
     916                e.key = 37; 
     917                break; 
     918        } 
     919    }, 
     920 
     921    //Shortcut: AJS.AEV 
     922    addEventListener: function(elm, type, fn, /*optional*/listen_once, cancle_bubble) { 
     923        if(!cancle_bubble) 
     924            cancle_bubble = false; 
     925 
     926        var elms = AJS.$A(elm); 
     927        AJS.map(elms, function(elmz) { 
     928            if(listen_once) 
     929                fn = AJS._listenOnce(elmz, type, fn); 
     930             
     931            //Hack since it does not work in all browsers 
     932            if(AJS.isIn(type, ['submit', 'load', 'scroll', 'resize'])) { 
     933                var old = elm['on' + type]; 
     934                elm['on' + type] = function() { 
     935                    if(old) { 
     936                        fn(arguments); 
     937                        return old(arguments); 
     938                    } 
     939                    else 
     940                        return fn(arguments); 
     941                }; 
     942                return; 
     943            } 
     944 
     945            //Fix keyCode 
     946            if(AJS.isIn(type, ['keypress', 'keydown', 'keyup', 'click'])) { 
     947                var old_fn = fn; 
     948                fn = function(e) { 
     949                    AJS.setEventKey(e); 
     950                    return old_fn.apply(null, arguments); 
     951                } 
     952            } 
     953 
     954            if (elmz.attachEvent) { 
     955                //FIXME: We ignore cancle_bubble for IE... could be a problem? 
     956                elmz.attachEvent("on" + type, fn); 
     957            } 
     958            else if(elmz.addEventListener) 
     959                elmz.addEventListener(type, fn, cancle_bubble); 
     960 
     961            AJS.listeners = AJS.$A(AJS.listeners); 
     962            AJS.listeners.push([elmz, type, fn]); 
     963        }); 
     964    }, 
     965 
     966    //Shortcut: AJS.REV 
     967    removeEventListener: function(elm, type, fn, /*optional*/cancle_bubble) { 
     968        if(!cancle_bubble) 
     969            cancle_bubble = false; 
     970        if(elm.removeEventListener) { 
     971            elm.removeEventListener(type, fn, cancle_bubble); 
     972            if(AJS.isOpera()) 
     973                elm.removeEventListener(type, fn, !cancle_bubble); 
     974        } 
     975        else if(elm.detachEvent) 
     976            elm.detachEvent("on" + type, fn); 
     977    }, 
     978 
     979    //Shortcut: AJS.$b 
     980    bind: function(fn, scope, /*optional*/ extra_args) { 
     981        fn._cscope = scope; 
     982        return AJS._getRealScope(fn, extra_args); 
     983    }, 
     984 
     985    bindMethods: function(self) { 
     986        for (var k in self) { 
     987            var func = self[k]; 
     988            if (typeof(func) == 'function') { 
     989                self[k] = AJS.$b(func, self); 
     990            } 
     991        } 
     992    }, 
     993 
     994    _listenOnce: function(elm, type, fn) { 
     995        var r_fn = function() { 
     996            AJS.removeEventListener(elm, type, r_fn); 
     997            fn(arguments); 
     998        } 
     999        return r_fn; 
     1000    }, 
     1001 
     1002    callLater: function(fn, interval) { 
     1003        var fn_no_send = function() { 
     1004            fn(); 
     1005        }; 
     1006        window.setTimeout(fn_no_send, interval); 
     1007    }, 
     1008 
     1009    preventDefault: function(e) { 
     1010        if(AJS.isIe())  
     1011            window.event.returnValue = false; 
     1012        else  
     1013            e.preventDefault(); 
     1014    }, 
     1015 
     1016 
     1017//// 
     1018// Drag and drop 
     1019//// 
     1020    dragAble: function(elm, /*optional*/ handler, args) { 
     1021        if(!args) 
     1022            args = {}; 
     1023        if(!AJS.isDefined(args['move_x'])) 
     1024            args['move_x'] = true; 
     1025        if(!AJS.isDefined(args['move_y'])) 
     1026            args['move_y'] = true; 
     1027        if(!AJS.isDefined(args['moveable'])) 
     1028            args['moveable'] = false; 
     1029        if(!AJS.isDefined(args['hide_on_move'])) 
     1030            args['hide_on_move'] = true; 
     1031        if(!AJS.isDefined(args['on_mouse_up'])) 
     1032            args['on_mouse_up'] = null; 
     1033        if(!AJS.isDefined(args['cursor'])) 
     1034            args['cursor'] = 'move'; 
     1035        if(!AJS.isDefined(args['max_move'])) 
     1036            args['max_move'] = {'top': null, 'left': null}; 
     1037 
     1038        elm = AJS.$(elm); 
     1039 
     1040        if(!handler) 
     1041            handler = elm; 
     1042 
     1043        handler = AJS.$(handler); 
     1044        var old_cursor = handler.style.cursor; 
     1045        handler.style.cursor = args['cursor']; 
     1046        elm.style.position = 'relative'; 
     1047 
     1048        AJS.addClass(handler, '_ajs_handler'); 
     1049        handler._args = args; 
     1050        handler._elm = elm; 
     1051        AJS.AEV(handler, 'mousedown', AJS._dragStart); 
     1052    }, 
     1053 
     1054    _dragStart: function(e) { 
     1055        var handler = AJS.getEventElm(e); 
     1056        if(!AJS.hasClass(handler, '_ajs_handler')) { 
     1057            handler = AJS.getParentBytc(handler, null, '_ajs_handler'); 
     1058        } 
     1059        if(handler) 
     1060            AJS._dragInit(e, handler._elm, handler._args); 
     1061    }, 
     1062 
     1063    dropZone: function(elm, args) { 
     1064        elm = AJS.$(elm); 
     1065        var item = {elm: elm}; 
     1066        AJS.update(item, args); 
     1067        AJS._drop_zones.push(item); 
     1068    }, 
     1069 
     1070    removeDragAble: function(elm) { 
     1071        AJS.REV(elm, 'mousedown', AJS._dragStart); 
     1072        elm.style.cursor = ''; 
     1073    }, 
     1074 
     1075    removeDropZone: function(elm) { 
     1076        var i = AJS.getIndex(elm, AJS._drop_zones, function(item) { 
     1077            if(item.elm == elm) return true; 
     1078        }); 
     1079        if(i != -1) { 
     1080            AJS._drop_zones.splice(i, 1); 
     1081        } 
     1082    }, 
     1083 
     1084    _dragInit: function(e, click_elm, args) { 
     1085        AJS.drag_obj = new Object(); 
     1086        AJS.drag_obj.args = args; 
     1087 
     1088        AJS.drag_obj.click_elm = click_elm; 
     1089        AJS.drag_obj.mouse_pos = AJS.getMousePos(e); 
     1090        AJS.drag_obj.click_elm_pos = AJS.absolutePosition(click_elm); 
     1091 
     1092        AJS.AEV(document, 'mousemove', AJS._dragMove, false, true); 
     1093        AJS.AEV(document, 'mouseup', AJS._dragStop, false, true); 
     1094 
     1095        if (AJS.isIe()) 
     1096            window.event.cancelBubble = true; 
     1097        AJS.preventDefault(e); 
     1098    }, 
     1099 
     1100    _initDragElm: function(elm) { 
     1101        if(AJS.drag_elm && AJS.drag_elm.style.display == 'none') 
     1102            AJS.removeElement(AJS.drag_elm); 
     1103 
     1104        if(!AJS.drag_elm) { 
     1105            AJS.drag_elm = AJS.DIV(); 
     1106            var d = AJS.drag_elm; 
     1107            AJS.insertBefore(d, AJS.getBody().firstChild); 
     1108            AJS.setHTML(d, elm.innerHTML); 
     1109 
     1110            d.className = elm.className; 
     1111            d.style.cssText = elm.style.cssText; 
     1112 
     1113            d.style.position = 'absolute'; 
     1114            d.style.zIndex = 10000; 
     1115 
     1116            var t = AJS.absolutePosition(elm); 
     1117            AJS.setTop(d, t.y); 
     1118            AJS.setLeft(d, t.x); 
     1119 
     1120            if(AJS.drag_obj.args.on_init) { 
     1121                AJS.drag_obj.args.on_init(elm); 
     1122            } 
     1123        } 
     1124    }, 
     1125 
     1126    _dragMove: function(e) { 
     1127        var drag_obj = AJS.drag_obj; 
     1128        var click_elm = drag_obj.click_elm; 
     1129 
     1130        AJS._initDragElm(click_elm); 
     1131        var drag_elm = AJS.drag_elm; 
     1132 
     1133        if(drag_obj.args['hide_on_move']) 
     1134            click_elm.style.visibility = 'hidden'; 
     1135 
     1136        var cur_pos = AJS.getMousePos(e); 
     1137 
     1138        var mouse_pos = drag_obj.mouse_pos; 
     1139 
     1140        var click_elm_pos = drag_obj.click_elm_pos; 
     1141 
     1142        var p_x, p_y; 
     1143        p_x = cur_pos.x - (mouse_pos.x - click_elm_pos.x); 
     1144        p_y = cur_pos.y - (mouse_pos.y - click_elm_pos.y); 
     1145 
     1146        AJS.map(AJS._drop_zones, function(d_z) { 
     1147            if(AJS.isOverlapping(d_z['elm'], drag_elm)) { 
     1148                if(d_z['elm'] != drag_elm) { 
     1149                    var on_hover = d_z['on_hover']; 
     1150                    if(on_hover) 
     1151                        on_hover(d_z['elm'], click_elm, drag_elm); 
     1152                } 
     1153            } 
     1154        }); 
     1155 
     1156        if(drag_obj.args['on_drag']) 
     1157            drag_obj.args['on_drag'](click_elm, e); 
     1158 
     1159        var max_move_top = drag_obj.args['max_move']['top']; 
     1160        var max_move_left = drag_obj.args['max_move']['left']; 
     1161        if(drag_obj.args['move_x']) { 
     1162            if(max_move_left == null || max_move_left <= p) 
     1163                AJS.setLeft(elm, p_x); 
     1164        } 
     1165 
     1166        if(drag_obj.args['move_y']) { 
     1167            if(max_move_top == null || max_move_top <= p_y) 
     1168                AJS.setTop(elm, p_y); 
     1169        } 
     1170        if(AJS.isIe()) { 
     1171            window.event.cancelBubble = true; 
     1172            window.event.returnValue = false; 
     1173        } 
     1174        else 
     1175            e.preventDefault(); 
     1176 
     1177        //Moving scroll to the top, should move the scroll up 
     1178        var sc_top = AJS.getScrollTop(); 
     1179        var sc_bottom = sc_top + AJS.getWindowSize().h; 
     1180        var d_e_top = AJS.absolutePosition(drag_elm).y; 
     1181        var d_e_bottom = drag_elm.offsetTop + drag_elm.offsetHeight; 
     1182 
     1183        if(d_e_top <= sc_top + 20) { 
     1184            window.scrollBy(0, -15); 
     1185        } 
     1186        else if(d_e_bottom >= sc_bottom - 20) { 
     1187            window.scrollBy(0, 15); 
     1188        } 
     1189    }, 
     1190 
     1191    _dragStop: function(e) { 
     1192        var drag_obj = AJS.drag_obj; 
     1193        var drag_elm = AJS.drag_elm; 
     1194        var click_elm = drag_obj.click_elm; 
     1195 
     1196        AJS.REV(document, "mousemove", AJS._dragMove, true); 
     1197        AJS.REV(document, "mouseup", AJS._dragStop, true); 
     1198 
     1199        var dropped = false; 
     1200        AJS.map(AJS._drop_zones, function(d_z) { 
     1201            if(AJS.isOverlapping(d_z['elm'], click_elm)) { 
     1202                if(d_z['elm'] != click_elm) { 
     1203                    var on_drop = d_z['on_drop']; 
     1204                    if(on_drop) { 
     1205                        dropped = true; 
     1206                        on_drop(d_z['elm'], click_elm); 
     1207                    } 
     1208                } 
     1209            } 
     1210        }); 
     1211 
     1212        if(drag_obj.args['moveable']) { 
     1213            var t = parseInt(click_elm.style.top) || 0; 
     1214            var l = parseInt(click_elm.style.left) || 0; 
     1215            var drag_elm_xy = AJS.absolutePosition(drag_elm); 
     1216            var click_elm_xy = AJS.absolutePosition(click_elm); 
     1217            AJS.setTop(click_elm, t + drag_elm_xy.y - click_elm_xy.y); 
     1218            AJS.setLeft(click_elm, l + drag_elm_xy.x - click_elm_xy.x); 
     1219        } 
     1220 
     1221        if(!dropped && drag_obj.args['on_mouse_up']) 
     1222            drag_obj.args['on_mouse_up'](click_elm, e); 
     1223 
     1224        if(drag_obj.args['hide_on_move']) 
     1225            drag_obj.click_elm.style.visibility = 'visible'; 
     1226 
     1227        if(drag_obj.args.on_end) { 
     1228            drag_obj.args.on_end(click_elm); 
     1229        } 
     1230 
     1231        AJS._dragObj = null; 
     1232        if(drag_elm) 
     1233            AJS.hideElement(drag_elm); 
     1234        AJS.drag_elm = null; 
     1235    }, 
     1236 
     1237 
    4061238//// 
    4071239// Misc. 
    4081240//// 
    409   /** 
    410    * Alert the objects key attrs  
    411    */ 
    412   keys: function(obj) { 
    413     var rval = []; 
    414     for (var prop in obj) { 
    415       rval.push(prop); 
    416     } 
    417     return rval; 
    418   }, 
    419  
    420   urlencode: function(str) { 
    421     return encodeURIComponent(str.toString()); 
    422   }, 
    423  
    424   /** 
    425    * @returns True if the object is defined, otherwise false 
    426    */ 
    427   isDefined: function(o) { 
    428     return (o != "undefined" && o != null) 
    429   }, 
    430    
    431   /** 
    432    * @returns True if an object is a array, false otherwise 
    433    */ 
    434   isArray: function(obj) { 
    435     try { return (typeof(obj.length) == "undefined") ? false : true; } 
    436     catch(e) 
    437     { return false; } 
    438   }, 
    439  
    440   isObject: function(obj) { 
    441     return (obj && typeof obj == 'object'); 
    442   }, 
    443  
    444   /** 
    445    * Export DOM elements to the global namespace 
    446    */ 
    447   exportDOMElements: function() { 
    448     UL = this.UL; 
    449     LI = this.LI; 
    450     TD = this.TD; 
    451     TR = this.TR; 
    452     TH = this.TH; 
    453     TBODY = this.TBODY; 
    454     TABLE = this.TABLE; 
    455     INPUT = this.INPUT; 
    456     SPAN = this.SPAN; 
    457     B = this.B; 
    458     A = this.A; 
    459     DIV = this.DIV; 
    460     IMG = this.IMG; 
    461     BUTTON = this.BUTTON; 
    462     H1 = this.H1; 
    463     H2 = this.H2; 
    464     H3 = this.H3; 
    465     BR = this.BR; 
    466     TEXTAREA = this.TEXTAREA; 
    467     FORM = this.FORM; 
    468     P = this.P; 
    469     SELECT = this.SELECT; 
    470     OPTION = this.OPTION; 
    471     TN = this.TN; 
    472     IFRAME = this.IFRAME; 
    473     SCRIPT = this.SCRIPT; 
    474   }, 
    475  
    476   /** 
    477    * Export AmiJS functions to the global namespace 
    478    */ 
    479   exportToGlobalScope: function() { 
    480     getElement = this.getElement; 
    481     getQueryArgument = this.getQueryArgument; 
    482     isIe = this.isIe; 
    483     $ = this.getElement; 
    484     getElements = this.getElements; 
    485     getBody = this.getBody; 
    486     getElementsByTagAndClassName = this.getElementsByTagAndClassName; 
    487     appendChildNodes = this.appendChildNodes; 
    488     ACN = appendChildNodes; 
    489     replaceChildNodes = this.replaceChildNodes; 
    490     RCN = replaceChildNodes; 
    491     insertAfter = this.insertAfter; 
    492     insertBefore = this.insertBefore; 
    493     showElement = this.showElement; 
    494     hideElement = this.hideElement; 
    495     isElementHidden = this.isElementHidden; 
    496     swapDOM = this.swapDOM; 
    497     removeElement = this.removeElement; 
    498     isDict = this.isDict; 
    499     createDOM = this.createDOM; 
    500     this.exportDOMElements(); 
    501     getXMLHttpRequest = this.getXMLHttpRequest; 
    502     doSimpleXMLHttpRequest = this.doSimpleXMLHttpRequest; 
    503     getRequest = this.getRequest; 
    504     sendXMLHttpRequest = this.sendXMLHttpRequest; 
    505     reprString = this.reprString; 
    506     serializeJSON = this.serializeJSON; 
    507     loadJSONDoc = this.loadJSONDoc; 
    508     keys = this.keys; 
    509     isDefined = this.isDefined; 
    510     isArray = this.isArray; 
    511   } 
    512 } 
    513  
    514  
     1241    keys: function(obj) { 
     1242        var rval = []; 
     1243        for (var prop in obj) { 
     1244            rval.push(prop); 
     1245        } 
     1246        return rval; 
     1247    }, 
     1248 
     1249    values: function(obj) { 
     1250        var rval = []; 
     1251        for (var prop in obj) { 
     1252            rval.push(obj[prop]); 
     1253        } 
     1254        return rval; 
     1255    }, 
     1256 
     1257    urlencode: function(str) { 
     1258        return encodeURIComponent(str.toString()); 
     1259    }, 
     1260 
     1261    isDefined: function(o) { 
     1262        return (o != "undefined" && o != null) 
     1263    }, 
     1264 
     1265    isArray: function(obj) { 
     1266        return obj instanceof Array; 
     1267    }, 
     1268 
     1269    isString: function(obj) { 
     1270        return (typeof obj == 'string'); 
     1271    }, 
     1272 
     1273    isNumber: function(obj) { 
     1274        return (typeof obj == 'number'); 
     1275    }, 
     1276 
     1277    isObject: function(obj) { 
     1278        return (typeof obj == 'object'); 
     1279    }, 
     1280 
     1281    isFunction: function(obj) { 
     1282        return (typeof obj == 'function'); 
     1283    }, 
     1284 
     1285    isDict: function(o) { 
     1286        var str_repr = String(o); 
     1287        return str_repr.indexOf(" Object") != -1; 
     1288    }, 
     1289 
     1290    exportToGlobalScope: function() { 
     1291        for(e in AJS) 
     1292            window[e] = AJS[e]; 
     1293    }, 
     1294 
     1295    log: function(o) { 
     1296        if(window.console) 
     1297            console.log(o); 
     1298        else { 
     1299            var div = AJS.$('ajs_logger'); 
     1300            if(!div) { 
     1301                div = AJS.DIV({id: 'ajs_logger', 'style': 'color: green; position: absolute; left: 0'}); 
     1302                div.style.top = AJS.getScrollTop() + 'px'; 
     1303                AJS.ACN(AJS.getBody(), div); 
     1304            } 
     1305            AJS.setHTML(div, ''+o); 
     1306        } 
     1307    } 
     1308 
     1309} 
     1310 
     1311AJS.Class = function(members) { 
     1312    var fn = function() { 
     1313        if(arguments[0] != 'no_init') { 
     1314            return this.init.apply(this, arguments); 
     1315        } 
     1316    } 
     1317    fn.prototype = members; 
     1318    AJS.update(fn, AJS.Class.prototype); 
     1319    return fn; 
     1320} 
     1321AJS.Class.prototype = { 
     1322    extend: function(members) { 
     1323        var parent = new this('no_init'); 
     1324        for(k in members) { 
     1325            var prev = parent[k]; 
     1326            var cur = members[k]; 
     1327            if (prev && prev != cur && typeof cur == 'function') { 
     1328                cur = this._parentize(cur, prev); 
     1329            } 
     1330            parent[k] = cur; 
     1331        } 
     1332        return new AJS.Class(parent); 
     1333    }, 
     1334 
     1335    implement: function(members) { 
     1336        AJS.update(this.prototype, members); 
     1337    }, 
     1338 
     1339    _parentize: function(cur, prev) { 
     1340        return function(){ 
     1341            this.parent = prev; 
     1342            return cur.apply(this, arguments); 
     1343        } 
     1344    } 
     1345}; 
     1346 
     1347//Shortcuts 
     1348AJS.$ = AJS.getElement; 
     1349AJS.$$ = AJS.getElements; 
     1350AJS.$f = AJS.getFormElement; 
     1351AJS.$b = AJS.bind; 
     1352AJS.$p = AJS.partial; 
     1353AJS.$FA = AJS.forceArray; 
     1354AJS.$A = AJS.createArray; 
     1355AJS.DI = AJS.documentInsert; 
     1356AJS.ACN = AJS.appendChildNodes; 
     1357AJS.RCN = AJS.replaceChildNodes; 
     1358AJS.AEV = AJS.addEventListener; 
     1359AJS.REV = AJS.removeEventListener; 
     1360AJS.$bytc = AJS.getElementsByTagAndClassName; 
    5151361 
    5161362AJSDeferred = function(req) { 
    517   this.callbacks = []; 
    518   this.req = req; 
    519  
    520   this.callback = function (res) { 
    521     while (this.callbacks.length > 0) { 
    522       var fn = this.callbacks.pop(); 
    523       res = fn(res); 
    524     } 
    525   }; 
    526  
    527   this.errback = function(e){ 
    528     alert("Error encountered:\n" + e); 
    529   }; 
    530  
    531   this.addErrback = function(fn) { 
    532     this.errback = fn; 
    533   }; 
    534  
    535   this.addCallback = function(fn) { 
    536     this.callbacks.unshift(fn); 
    537   }; 
    538  
    539   this.addCallbacks = function(fn1, fn2) { 
    540     this.addCallback(fn1); 
    541     this.addErrback(fn2); 
    542   }; 
    543  
    544   this.sendReq = function(data) { 
    545     if(AJS.isObject(data)) { 
    546       var post_data = []; 
    547       for(k in data) { 
    548         post_data.push(k + "=" + AJS.urlencode(data[k])); 
    549       } 
    550       post_data = post_data.join("&"); 
    551       this.req.send(post_data); 
    552     } 
    553     else if(AJS.isDefined(data)) 
    554       this.req.send(data); 
     1363    this.callbacks = []; 
     1364    this.errbacks = []; 
     1365    this.req = req; 
     1366} 
     1367AJSDeferred.prototype = { 
     1368    excCallbackSeq: function(req, list) { 
     1369        var data = req.responseText; 
     1370        while (list.length > 0) { 
     1371            var fn = list.pop(); 
     1372            var new_data = fn(data, req); 
     1373            if(new_data) 
     1374                data = new_data; 
     1375        } 
     1376    }, 
     1377 
     1378    callback: function () { 
     1379        this.excCallbackSeq(this.req, this.callbacks); 
     1380    }, 
     1381 
     1382    errback: function() { 
     1383        if(this.errbacks.length == 0) 
     1384            alert("Error encountered:\n" + this.req.responseText); 
     1385 
     1386        this.excCallbackSeq(this.req, this.errbacks); 
     1387    }, 
     1388 
     1389    addErrback: function(fn) { 
     1390        this.errbacks.unshift(fn); 
     1391    }, 
     1392 
     1393    addCallback: function(fn) { 
     1394        this.callbacks.unshift(fn); 
     1395    }, 
     1396 
     1397    abort: function() { 
     1398        this.req.abort(); 
     1399    }, 
     1400 
     1401    addCallbacks: function(fn1, fn2) { 
     1402        this.addCallback(fn1); 
     1403        this.addErrback(fn2); 
     1404    }, 
     1405 
     1406    sendReq: function(data) { 
     1407        if(AJS.isObject(data)) { 
     1408            this.req.send(AJS.queryArguments(data)); 
     1409        } 
     1410        else if(AJS.isDefined(data)) 
     1411            this.req.send(data); 
     1412        else { 
     1413            this.req.send(""); 
     1414        } 
     1415    } 
     1416}; 
     1417 
     1418//Prevent memory-leaks 
     1419AJS.addEventListener(window, 'unload', AJS._unloadListeners); 
     1420AJS._createDomShortcuts() 
     1421} 
     1422 
     1423script_loaded = true; 
     1424 
     1425/**** 
     1426Last Modified: 13/05/07 00:25:28 
     1427 
     1428 GoogieSpell 
     1429     Google spell checker for your own web-apps :) 
     1430 Copyright Amir Salihefendic 2006 
     1431     LICENSE 
     1432     GPL (see gpl.txt for more information) 
     1433     This basically means that you can't use this script with/in proprietary software! 
     1434     There is another license that permits you to use this script with proprietary software. Check out:... for more info. 
     1435     AUTHOR 
     1436         4mir Salihefendic (http://amix.dk) - amix@amix.dk 
     1437 VERSION 
     1438     4.0 
     1439****/ 
     1440var GOOGIE_CUR_LANG = null; 
     1441var GOOGIE_DEFAULT_LANG = "en"; 
     1442 
     1443function GoogieSpell(img_dir, server_url) { 
     1444    var cookie_value; 
     1445    var lang; 
     1446    cookie_value = getCookie('language'); 
     1447 
     1448    if(cookie_value != null) 
     1449        GOOGIE_CUR_LANG = cookie_value; 
     1450    else 
     1451        GOOGIE_CUR_LANG = GOOGIE_DEFAULT_LANG; 
     1452 
     1453    this.img_dir = img_dir; 
     1454    this.server_url = server_url; 
     1455 
     1456    this.org_lang_to_word = {"da": "Dansk", "de": "Deutsch", "en": "English", 
     1457                                             "es": "Espa&#241;ol", "fr": "Fran&#231;ais", "it": "Italiano",  
     1458                                             "nl": "Nederlands", "pl": "Polski", "pt": "Portugu&#234;s", 
     1459                                             "fi": "Suomi", "sv": "Svenska"}; 
     1460    this.lang_to_word = this.org_lang_to_word; 
     1461    this.langlist_codes = AJS.keys(this.lang_to_word); 
     1462 
     1463    this.show_change_lang_pic = true; 
     1464    this.change_lang_pic_placement = "left"; 
     1465 
     1466    this.report_state_change = true; 
     1467 
     1468    this.ta_scroll_top = 0; 
     1469    this.el_scroll_top = 0; 
     1470 
     1471    this.lang_chck_spell = "Check spelling"; 
     1472    this.lang_revert = "Revert to"; 
     1473    this.lang_close = "Close"; 
     1474    this.lang_rsm_edt = "Resume editing"; 
     1475    this.lang_no_error_found = "No spelling errors found"; 
     1476    this.lang_no_suggestions = "No suggestions"; 
     1477     
     1478    this.show_spell_img = false;  // modified by roundcube 
     1479    this.decoration = true; 
     1480    this.use_close_btn = true; 
     1481    this.edit_layer_dbl_click = true; 
     1482    this.report_ta_not_found = true; 
     1483 
     1484    //Extensions 
     1485    this.custom_ajax_error = null; 
     1486    this.custom_no_spelling_error = null; 
     1487    this.custom_menu_builder = []; //Should take an eval function and a build menu function 
     1488    this.custom_item_evaulator = null; //Should take an eval function and a build menu function 
     1489    this.extra_menu_items = []; 
     1490    this.custom_spellcheck_starter = null; 
     1491    this.main_controller = true; 
     1492 
     1493    //Observers 
     1494    this.lang_state_observer = null; 
     1495    this.spelling_state_observer = null; 
     1496    this.show_menu_observer = null; 
     1497    this.all_errors_fixed_observer = null; 
     1498 
     1499    //Focus links - used to give the text box focus 
     1500    this.use_focus = false; 
     1501    this.focus_link_t = null; 
     1502    this.focus_link_b = null; 
     1503 
     1504    //Counters 
     1505    this.cnt_errors = 0; 
     1506    this.cnt_errors_fixed = 0; 
     1507 
     1508    //Set document on click to hide the language and error menu 
     1509    var fn = function(e) { 
     1510        var elm = AJS.getEventElm(e); 
     1511        if(elm.googie_action_btn != "1" && this.isLangWindowShown()) 
     1512            this.hideLangWindow(); 
     1513        if(elm.googie_action_btn != "1" && this.isErrorWindowShown()) 
     1514            this.hideErrorWindow(); 
     1515    }; 
     1516    AJS.AEV(document, "click", AJS.$b(fn, this)); 
     1517} 
     1518 
     1519GoogieSpell.prototype.decorateTextarea = function(id) { 
     1520    if(typeof(id) == "string") 
     1521        this.text_area = AJS.$(id); 
     1522    else 
     1523        this.text_area = id; 
     1524 
     1525    var r_width, r_height; 
     1526 
     1527    if(this.text_area != null) { 
     1528        if(!AJS.isDefined(this.spell_container) && this.decoration) { 
     1529            var table = AJS.TABLE(); 
     1530            var tbody = AJS.TBODY(); 
     1531            var tr = AJS.TR(); 
     1532            if(AJS.isDefined(this.force_width)) 
     1533                r_width = this.force_width; 
     1534            else 
     1535                r_width = this.text_area.offsetWidth + "px"; 
     1536 
     1537            if(AJS.isDefined(this.force_height)) 
     1538                r_height = this.force_height; 
     1539            else 
     1540                r_height = ""; 
     1541 
     1542            var spell_container = AJS.TD(); 
     1543            this.spell_container = spell_container; 
     1544 
     1545            tr.appendChild(spell_container); 
     1546 
     1547            tbody.appendChild(tr); 
     1548            table.appendChild(tbody); 
     1549 
     1550            AJS.insertBefore(table, this.text_area); 
     1551 
     1552            //Set width 
     1553            AJS.setHeight(table, spell_container, r_height); 
     1554            AJS.setWidth(table, spell_container, '100%');  // modified by roundcube (old: r_width) 
     1555 
     1556            spell_container.style.textAlign = "right"; 
     1557        } 
     1558 
     1559        this.checkSpellingState(); 
     1560    } 
     1561    else  
     1562        if(this.report_ta_not_found) 
     1563            alert("Text area not found"); 
     1564} 
     1565 
     1566////// 
     1567// API Functions (the ones that you can call) 
     1568///// 
     1569GoogieSpell.prototype.setSpellContainer = function(elm) { 
     1570    this.spell_container = AJS.$(elm); 
     1571} 
     1572 
     1573GoogieSpell.prototype.setLanguages = function(lang_dict) { 
     1574    this.lang_to_word = lang_dict; 
     1575    this.langlist_codes = AJS.keys(lang_dict); 
     1576} 
     1577 
     1578GoogieSpell.prototype.setForceWidthHeight = function(width, height) { 
     1579    /*** 
     1580        Set to null if you want to use one of them 
     1581    ***/ 
     1582    this.force_width = width; 
     1583    this.force_height = height; 
     1584} 
     1585 
     1586GoogieSpell.prototype.setDecoration = function(bool) { 
     1587    this.decoration = bool; 
     1588} 
     1589 
     1590GoogieSpell.prototype.dontUseCloseButtons = function() { 
     1591    this.use_close_btn = false; 
     1592} 
     1593 
     1594GoogieSpell.prototype.appendNewMenuItem = function(name, call_back_fn, checker) { 
     1595    this.extra_menu_items.push([name, call_back_fn, checker]); 
     1596} 
     1597 
     1598GoogieSpell.prototype.appendCustomMenuBuilder = function(eval, builder) { 
     1599    this.custom_menu_builder.push([eval, builder]); 
     1600} 
     1601 
     1602GoogieSpell.prototype.setFocus = function() { 
     1603    try { 
     1604        this.focus_link_b.focus(); 
     1605        this.focus_link_t.focus(); 
     1606        return true; 
     1607    } 
     1608    catch(e) { 
     1609        return false; 
     1610    } 
     1611} 
     1612 
     1613GoogieSpell.prototype.getValue = function(ta) { 
     1614    return ta.value; 
     1615} 
     1616 
     1617GoogieSpell.prototype.setValue = function(ta, value) { 
     1618    ta.value = value; 
     1619} 
     1620 
     1621 
     1622////// 
     1623// Set functions (internal) 
     1624///// 
     1625GoogieSpell.prototype.setStateChanged = function(current_state) { 
     1626    this.state = current_state; 
     1627    if(this.spelling_state_observer != null && this.report_state_change) 
     1628        this.spelling_state_observer(current_state, this); 
     1629} 
     1630 
     1631GoogieSpell.prototype.setReportStateChange = function(bool) { 
     1632    this.report_state_change = bool; 
     1633} 
     1634 
     1635 
     1636////// 
     1637// Request functions 
     1638///// 
     1639GoogieSpell.prototype.getGoogleUrl = function() { 
     1640    return this.server_url + GOOGIE_CUR_LANG; 
     1641} 
     1642 
     1643GoogieSpell.escapeSepcial = function(val) { 
     1644    return val.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"); 
     1645} 
     1646 
     1647GoogieSpell.createXMLReq = function (text) { 
     1648    return '<?xml version="1.0" encoding="utf-8" ?><spellrequest textalreadyclipped="0" ignoredups="0" ignoredigits="1" ignoreallcaps="1"><text>' + text + '</text></spellrequest>'; 
     1649} 
     1650 
     1651GoogieSpell.prototype.spellCheck = function(ignore) { 
     1652    var me = this; 
     1653 
     1654    this.cnt_errors_fixed = 0; 
     1655    this.cnt_errors = 0; 
     1656    this.setStateChanged("checking_spell"); 
     1657 
     1658    if(this.main_controller) 
     1659        this.appendIndicator(this.spell_span); 
     1660 
     1661    this.error_links = []; 
     1662    this.ta_scroll_top = this.text_area.scrollTop; 
     1663 
     1664    try { this.hideLangWindow(); } 
     1665    catch(e) {} 
     1666 
     1667    this.ignore = ignore; 
     1668 
     1669    if(this.getValue(this.text_area) == '' || ignore) { 
     1670        if(!me.custom_no_spelling_error) 
     1671            me.flashNoSpellingErrorState(); 
     1672        else 
     1673            me.custom_no_spelling_error(me); 
     1674        me.removeIndicator(); 
     1675        return ; 
     1676    } 
     1677     
     1678    this.createEditLayer(this.text_area.offsetWidth, this.text_area.offsetHeight); 
     1679 
     1680    this.createErrorWindow(); 
     1681    AJS.getBody().appendChild(this.error_window); 
     1682 
     1683    try { netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead"); }  
     1684    catch (e) { } 
     1685 
     1686    if(this.main_controller) 
     1687        this.spell_span.onclick = null; 
     1688 
     1689    this.orginal_text = this.getValue(this.text_area); 
     1690 
     1691    //Create request 
     1692    var d = AJS.getRequest(this.getGoogleUrl()); 
     1693    var reqdone = function(res_txt) { 
     1694        var r_text = res_txt; 
     1695        me.results = me.parseResult(r_text); 
     1696 
     1697        if(r_text.match(/<c.*>/) != null) { 
     1698            //Before parsing be sure that errors were found 
     1699            me.showErrorsInIframe(); 
     1700            me.resumeEditingState(); 
     1701        } 
     1702        else { 
     1703            if(!me.custom_no_spelling_error) 
     1704                me.flashNoSpellingErrorState(); 
     1705            else 
     1706                me.custom_no_spelling_error(me); 
     1707        } 
     1708        me.removeIndicator(); 
     1709    }; 
     1710 
     1711    d.addCallback(reqdone); 
     1712    reqdone = null; 
     1713 
     1714    var reqfailed = function(res_txt, req) { 
     1715        if(me.custom_ajax_error) 
     1716            me.custom_ajax_error(req); 
     1717        else 
     1718            alert("An error was encountered on the server. Please try again later."); 
     1719 
     1720        if(me.main_controller) { 
     1721            AJS.removeElement(me.spell_span); 
     1722            me.removeIndicator(); 
     1723        } 
     1724        me.checkSpellingState(); 
     1725    }; 
     1726    d.addErrback(reqfailed); 
     1727    reqfailed = null; 
     1728 
     1729    var req_text = GoogieSpell.escapeSepcial(this.orginal_text); 
     1730    d.sendReq(GoogieSpell.createXMLReq(req_text)); 
     1731} 
     1732 
     1733 
     1734////// 
     1735// Spell checking functions 
     1736///// 
     1737GoogieSpell.prototype.parseResult = function(r_text) { 
     1738    /*** 
     1739     Retunrs an array 
     1740     result[item] -> ['attrs'], ['suggestions'] 
     1741        ***/ 
     1742    var re_split_attr_c = /\w+="(\d+|true)"/g; 
     1743    var re_split_text = /\t/g; 
     1744 
     1745    var matched_c = r_text.match(/<c[^>]*>[^<]*<\/c>/g); 
     1746    var results = new Array(); 
     1747 
     1748    if(matched_c == null) 
     1749        return results; 
     1750     
     1751    for(var i=0; i < matched_c.length; i++) { 
     1752        var item = new Array(); 
     1753        this.errorFound(); 
     1754 
     1755        //Get attributes 
     1756        item['attrs'] = new Array(); 
     1757        var split_c = matched_c[i].match(re_split_attr_c); 
     1758        for(var j=0; j < split_c.length; j++) { 
     1759            var c_attr = split_c[j].split(/=/); 
     1760            var val = c_attr[1].replace(/"/g, ''); 
     1761            if(val != "true") 
     1762                item['attrs'][c_attr[0]] = parseInt(val); 
     1763            else { 
     1764                item['attrs'][c_attr[0]] = val; 
     1765            } 
     1766        } 
     1767 
     1768        //Get suggestions 
     1769        item['suggestions'] = new Array(); 
     1770        var only_text = matched_c[i].replace(/<[^>]*>/g, ""); 
     1771        var split_t = only_text.split(re_split_text); 
     1772        for(var k=0; k < split_t.length; k++) { 
     1773        if(split_t[k] != "") 
     1774            item['suggestions'].push(split_t[k]); 
     1775        } 
     1776        results.push(item); 
     1777    } 
     1778    return results; 
     1779} 
     1780 
     1781////// 
     1782// Counters 
     1783///// 
     1784GoogieSpell.prototype.errorFixed = function() {  
     1785    this.cnt_errors_fixed++;  
     1786    if(this.all_errors_fixed_observer) 
     1787        if(this.cnt_errors_fixed == this.cnt_errors) { 
     1788            this.hideErrorWindow(); 
     1789            this.all_errors_fixed_observer(); 
     1790        } 
     1791} 
     1792GoogieSpell.prototype.errorFound = function() { this.cnt_errors++; } 
     1793 
     1794////// 
     1795// Error menu functions 
     1796///// 
     1797GoogieSpell.prototype.createErrorWindow = function() { 
     1798    this.error_window = AJS.DIV(); 
     1799    this.error_window.className = "googie_window"; 
     1800    this.error_window.googie_action_btn = "1"; 
     1801} 
     1802 
     1803GoogieSpell.prototype.isErrorWindowShown = function() { 
     1804    return this.error_window != null && this.error_window.style.visibility == "visible"; 
     1805} 
     1806 
     1807GoogieSpell.prototype.hideErrorWindow = function() { 
     1808    try { 
     1809        this.error_window.style.visibility = "hidden"; 
     1810        if(this.error_window_iframe) 
     1811            this.error_window_iframe.style.visibility = "hidden"; 
     1812    } 
     1813    catch(e) {} 
     1814} 
     1815 
     1816GoogieSpell.prototype.updateOrginalText = function(offset, old_value, new_value, id) { 
     1817    var part_1 = this.orginal_text.substring(0, offset); 
     1818    var part_2 = this.orginal_text.substring(offset+old_value.length); 
     1819    this.orginal_text = part_1 + new_value + part_2; 
     1820    this.setValue(this.text_area, this.orginal_text); 
     1821    var add_2_offset = new_value.length - old_value.length; 
     1822    for(var j=0; j < this.results.length; j++) { 
     1823        //Don't edit the offset of the current item 
     1824        if(j != id && j > id){ 
     1825            this.results[j]['attrs']['o'] += add_2_offset; 
     1826        } 
     1827    } 
     1828} 
     1829 
     1830GoogieSpell.prototype.saveOldValue = function(elm, old_value) { 
     1831    elm.is_changed = true; 
     1832    elm.old_value = old_value; 
     1833} 
     1834 
     1835GoogieSpell.prototype.createListSeparator = function() { 
     1836    var e_col = AJS.TD(" "); 
     1837    e_col.googie_action_btn = "1"; 
     1838    e_col.style.cursor = "default"; 
     1839    e_col.style.fontSize = "3px"; 
     1840    e_col.style.borderTop = "1px solid #ccc"; 
     1841    e_col.style.paddingTop = "3px"; 
     1842 
     1843    return AJS.TR(e_col); 
     1844} 
     1845 
     1846GoogieSpell.prototype.correctError = function(id, elm, l_elm, /*optional*/ rm_pre_space) { 
     1847    var old_value = elm.innerHTML; 
     1848    var new_value = l_elm.innerHTML; 
     1849    var offset = this.results[id]['attrs']['o']; 
     1850 
     1851    if(rm_pre_space) { 
     1852        var pre_length = elm.previousSibling.innerHTML; 
     1853        elm.previousSibling.innerHTML = pre_length.slice(0, pre_length.length-1); 
     1854        old_value = " " + old_value; 
     1855        offset--; 
     1856    } 
     1857 
     1858    this.hideErrorWindow(); 
     1859 
     1860    this.updateOrginalText(offset, old_value, new_value, id); 
     1861 
     1862    elm.innerHTML = new_value; 
     1863     
     1864    elm.style.color = "green"; 
     1865    elm.is_corrected = true; 
     1866 
     1867    this.results[id]['attrs']['l'] = new_value.length; 
     1868 
     1869    if(!AJS.isDefined(elm.old_value)) 
     1870        this.saveOldValue(elm, old_value); 
     1871     
     1872    this.errorFixed(); 
     1873} 
     1874 
     1875GoogieSpell.prototype.showErrorWindow = function(elm, id) { 
     1876    if(this.show_menu_observer) 
     1877        this.show_menu_observer(this); 
     1878    var me = this; 
     1879 
     1880    var abs_pos = AJS.absolutePosition(elm); 
     1881    abs_pos.y -= this.edit_layer.scrollTop; 
     1882    this.error_window.style.visibility = "visible"; 
     1883 
     1884    AJS.setTop(this.error_window, (abs_pos.y+20)); 
     1885    AJS.setLeft(this.error_window, (abs_pos.x)); 
     1886 
     1887    this.error_window.innerHTML = ""; 
     1888 
     1889    var table = AJS.TABLE({'class': 'googie_list'}); 
     1890    table.googie_action_btn = "1"; 
     1891    var list = AJS.TBODY(); 
     1892 
     1893    //Check if we should use custom menu builder, if not we use the default 
     1894    var changed = false; 
     1895    if(this.custom_menu_builder != []) { 
     1896        for(var k=0; k<this.custom_menu_builder.length; k++) { 
     1897            var eb = this.custom_menu_builder[k]; 
     1898            if(eb[0]((this.results[id]))){ 
     1899                changed = eb[1](this, list, elm); 
     1900                break; 
     1901            } 
     1902        } 
     1903    } 
     1904    if(!changed) { 
     1905        //Build up the result list 
     1906        var suggestions = this.results[id]['suggestions']; 
     1907        var offset = this.results[id]['attrs']['o']; 
     1908        var len = this.results[id]['attrs']['l']; 
     1909 
     1910        if(suggestions.length == 0) { 
     1911            var row = AJS.TR(); 
     1912            var item = AJS.TD({'style': 'cursor: default;'}); 
     1913            var dummy = AJS.SPAN(); 
     1914            dummy.innerHTML = this.lang_no_suggestions; 
     1915            AJS.ACN(item, AJS.TN(dummy.innerHTML)); 
     1916            item.googie_action_btn = "1"; 
     1917            row.appendChild(item); 
     1918            list.appendChild(row); 
     1919        } 
     1920 
     1921        for(i=0; i < suggestions.length; i++) { 
     1922            var row = AJS.TR(); 
     1923            var item = AJS.TD(); 
     1924            var dummy = AJS.SPAN(); 
     1925            dummy.innerHTML = suggestions[i]; 
     1926            item.appendChild(AJS.TN(dummy.innerHTML)); 
     1927             
     1928            var fn = function(e) { 
     1929                var l_elm = AJS.getEventElm(e); 
     1930                this.correctError(id, elm, l_elm); 
     1931            }; 
     1932 
     1933            AJS.AEV(item, "click", AJS.$b(fn, this)); 
     1934 
     1935            item.onmouseover = GoogieSpell.item_onmouseover; 
     1936            item.onmouseout = GoogieSpell.item_onmouseout; 
     1937            row.appendChild(item); 
     1938            list.appendChild(row); 
     1939        } 
     1940 
     1941        //The element is changed, append the revert 
     1942        if(elm.is_changed && elm.innerHTML != elm.old_value) { 
     1943            var old_value = elm.old_value; 
     1944            var revert_row = AJS.TR(); 
     1945            var revert = AJS.TD(); 
     1946 
     1947            revert.onmouseover = GoogieSpell.item_onmouseover; 
     1948            revert.onmouseout = GoogieSpell.item_onmouseout; 
     1949            var rev_span = AJS.SPAN({'class': 'googie_list_revert'}); 
     1950            rev_span.innerHTML = this.lang_revert + " " + old_value; 
     1951            revert.appendChild(rev_span); 
     1952 
     1953            var fn = function(e) {  
     1954                this.updateOrginalText(offset, elm.innerHTML, old_value, id); 
     1955                elm.is_corrected = true; 
     1956                elm.style.color = "#b91414"; 
     1957                elm.innerHTML = old_value; 
     1958                this.hideErrorWindow(); 
     1959            }; 
     1960            AJS.AEV(revert, "click", AJS.$b(fn, this)); 
     1961 
     1962            revert_row.appendChild(revert); 
     1963            list.appendChild(revert_row); 
     1964        } 
     1965         
     1966        //Append the edit box 
     1967        var edit_row = AJS.TR(); 
     1968        var edit = AJS.TD({'style': 'cursor: default'}); 
     1969 
     1970        var edit_input = AJS.INPUT({'style': 'width: 120px; margin:0; padding:0', 'value': elm.innerHTML}); 
     1971        edit_input.googie_action_btn = "1"; 
     1972 
     1973        var onsub = function () { 
     1974            if(edit_input.value != "") { 
     1975                if(!AJS.isDefined(elm.old_value)) 
     1976                    this.saveOldValue(elm, elm.innerHTML); 
     1977 
     1978                this.updateOrginalText(offset, elm.innerHTML, edit_input.value, id); 
     1979                elm.style.color = "green" 
     1980                elm.is_corrected = true; 
     1981                elm.innerHTML = edit_input.value; 
     1982                 
     1983                this.hideErrorWindow(); 
     1984            } 
     1985            return false; 
     1986        }; 
     1987        onsub = AJS.$b(onsub, this); 
     1988         
     1989        var ok_pic = AJS.IMG({'src': this.img_dir + "ok.gif", 'style': 'width: 32px; height: 16px; margin-left: 2px; margin-right: 2px; cursor: pointer;'}); 
     1990        var edit_form = AJS.FORM({'style': 'margin: 0; padding: 0; cursor: default;'}, edit_input, ok_pic); 
     1991 
     1992        edit_form.googie_action_btn = "1"; 
     1993        edit.googie_action_btn = "1"; 
     1994 
     1995        AJS.AEV(edit_form, "submit", onsub); 
     1996        AJS.AEV(ok_pic, "click", onsub); 
     1997         
     1998        edit.appendChild(edit_form); 
     1999        edit_row.appendChild(edit); 
     2000        list.appendChild(edit_row); 
     2001 
     2002        //Append extra menu items 
     2003        if(this.extra_menu_items.length > 0) 
     2004            AJS.ACN(list, this.createListSeparator()); 
     2005 
     2006        var loop = function(i) { 
     2007                if(i < me.extra_menu_items.length) { 
     2008                    var e_elm = me.extra_menu_items[i]; 
     2009 
     2010                    if(!e_elm[2] || e_elm[2](elm, me)) { 
     2011                        var e_row = AJS.TR(); 
     2012                        var e_col = AJS.TD(e_elm[0]); 
     2013 
     2014                        e_col.onmouseover = GoogieSpell.item_onmouseover; 
     2015                        e_col.onmouseout = GoogieSpell.item_onmouseout; 
     2016 
     2017                        var fn = function() { 
     2018                            return e_elm[1](elm, me); 
     2019                        }; 
     2020                        AJS.AEV(e_col, "click", fn); 
     2021 
     2022                        AJS.ACN(e_row, e_col); 
     2023                        AJS.ACN(list, e_row); 
     2024 
     2025                    } 
     2026                    loop(i+1); 
     2027                } 
     2028        } 
     2029        loop(0); 
     2030        loop = null; 
     2031 
     2032        //Close button 
     2033        if(this.use_close_btn) { 
     2034            AJS.ACN(list, this.createCloseButton(this.hideErrorWindow)); 
     2035        } 
     2036    } 
     2037 
     2038    table.appendChild(list); 
     2039    this.error_window.appendChild(table); 
     2040 
     2041    //Dummy for IE - dropdown bug fix 
     2042    if(AJS.isIe() && !this.error_window_iframe) { 
     2043        var iframe = AJS.IFRAME({'style': 'position: absolute; z-index: 0;'}); 
     2044        AJS.ACN(AJS.getBody(), iframe); 
     2045        this.error_window_iframe = iframe; 
     2046    } 
     2047    if(AJS.isIe()) { 
     2048        var iframe = this.error_window_iframe; 
     2049        AJS.setTop(iframe, this.error_window.offsetTop); 
     2050        AJS.setLeft(iframe, this.error_window.offsetLeft); 
     2051 
     2052        AJS.setWidth(iframe, this.error_window.offsetWidth); 
     2053        AJS.setHeight(iframe, this.error_window.offsetHeight); 
     2054 
     2055        iframe.style.visibility = "visible"; 
     2056    } 
     2057 
     2058    //Set focus on the last element 
     2059    var link = this.createFocusLink('link'); 
     2060    list.appendChild(AJS.TR(AJS.TD({'style': 'text-align: right; font-size: 1px; height: 1px; margin: 0; padding: 0;'}, link))); 
     2061    link.focus(); 
     2062} 
     2063 
     2064 
     2065////// 
     2066// Edit layer (the layer where the suggestions are stored) 
     2067////// 
     2068GoogieSpell.prototype.createEditLayer = function(width, height) { 
     2069    this.edit_layer = AJS.DIV({'class': 'googie_edit_layer'}); 
     2070 
     2071    //Set the style so it looks like edit areas 
     2072    this.edit_layer.className = this.text_area.className; 
     2073    this.edit_layer.style.border = "1px solid #999"; 
     2074    this.edit_layer.style.backgroundColor = "#F1EDFE";  // modified by roundcube 
     2075    this.edit_layer.style.padding = "3px"; 
     2076    this.edit_layer.style.margin = "0px"; 
     2077 
     2078    AJS.setWidth(this.edit_layer, (width-8)); 
     2079 
     2080    if(AJS.nodeName(this.text_area) != "input" || this.getValue(this.text_area) == "") { 
     2081        this.edit_layer.style.overflow = "auto"; 
     2082        AJS.setHeight(this.edit_layer, (height-6)); 
     2083    } 
    5552084    else { 
    556       this.req.send(""); 
    557     } 
    558   }; 
    559 }; 
    560 AJSDeferred.prototype = new AJSDeferred(); 
    561  
    562  
    563  
    564  
    565  
    566  
    567 /**** 
    568 Last Modified: 28/04/06 15:26:06 
    569  
    570  GoogieSpell 
    571    Google spell checker for your own web-apps :) 
    572    Copyright Amir Salihefendic 2006 
    573  LICENSE 
    574   GPL (see gpl.txt for more information) 
    575   This basically means that you can't use this script with/in proprietary software! 
    576   There is another license that permits you to use this script with proprietary software. Check out:... for more info. 
    577   AUTHOR 
    578    4mir Salihefendic (http://amix.dk) - amix@amix.dk 
    579  VERSION 
    580          3.22 
    581 ****/ 
    582 var GOOGIE_CUR_LANG = "en"; 
    583  
    584 function GoogieSpell(img_dir, server_url) { 
    585   var cookie_value; 
    586   var lang; 
    587   cookie_value = getCookie('language'); 
    588  
    589   if(cookie_value != null) 
    590     GOOGIE_CUR_LANG = cookie_value; 
    591  
    592   this.img_dir = img_dir; 
    593   this.server_url = server_url; 
    594  
    595   this.lang_to_word = {"da": "Dansk", "de": "Deutsch", "en": "English", 
    596                        "es": "Espa&#241;ol", "fr": "Fran&#231;ais", "it": "Italiano",  
    597                        "nl": "Nederlands", "pl": "Polski", "pt": "Portugu&#234;s", 
    598                        "fi": "Suomi", "sv": "Svenska"}; 
    599   this.langlist_codes = AJS.keys(this.lang_to_word); 
    600  
    601   this.show_change_lang_pic = true; 
    602  
    603   this.lang_state_observer = null; 
    604  
    605   this.spelling_state_observer = null; 
    606  
    607   this.request = null; 
    608   this.error_window = null; 
    609   this.language_window = null; 
    610   this.edit_layer = null; 
    611   this.orginal_text = null; 
    612   this.results = null; 
    613   this.text_area = null; 
    614   this.gselm = null; 
    615   this.ta_scroll_top = 0; 
    616   this.el_scroll_top = 0; 
    617  
    618   this.lang_chck_spell = "Check spelling"; 
    619   this.lang_rsm_edt = "Resume editing"; 
    620   this.lang_close = "Close"; 
    621   this.lang_no_error_found = "No spelling errors found"; 
    622   this.lang_revert = "Revert to"; 
    623   this.show_spell_img = false;  // modified by roundcube 
    624 } 
    625  
    626 GoogieSpell.prototype.setStateChanged = function(current_state) { 
    627   if(this.spelling_state_observer != null) 
    628     this.spelling_state_observer(current_state); 
    629 } 
    630  
     2085        this.edit_layer.style.overflow = "hidden"; 
     2086    } 
     2087 
     2088    if(this.edit_layer_dbl_click) { 
     2089        var me = this; 
     2090        var fn = function(e) { 
     2091            if(AJS.getEventElm(e).className != "googie_link" && !me.isErrorWindowShown()) { 
     2092                me.resumeEditing(); 
     2093                var fn1 = function() { 
     2094                    me.text_area.focus(); 
     2095                    fn1 = null; 
     2096                }; 
     2097                AJS.callLater(fn1, 10); 
     2098            } 
     2099            return false; 
     2100        }; 
     2101        this.edit_layer.ondblclick = fn; 
     2102        fn = null; 
     2103    } 
     2104} 
     2105 
     2106GoogieSpell.prototype.resumeEditing = function() { 
     2107    this.setStateChanged("spell_check"); 
     2108    this.switch_lan_pic.style.display = "inline"; 
     2109 
     2110    if(this.edit_layer) 
     2111        this.el_scroll_top = this.edit_layer.scrollTop; 
     2112 
     2113    this.hideErrorWindow(); 
     2114 
     2115    if(this.main_controller) 
     2116        this.spell_span.className = "googie_no_style"; 
     2117 
     2118    if(!this.ignore) { 
     2119        //Remove the EDIT_LAYER 
     2120        try { 
     2121            this.edit_layer.parentNode.removeChild(this.edit_layer); 
     2122            if(this.use_focus) { 
     2123                AJS.removeElement(this.focus_link_t); 
     2124                AJS.removeElement(this.focus_link_b); 
     2125            } 
     2126        } 
     2127        catch(e) { 
     2128        } 
     2129 
     2130        AJS.showElement(this.text_area); 
     2131 
     2132        if(this.el_scroll_top != undefined) 
     2133            this.text_area.scrollTop = this.el_scroll_top; 
     2134    } 
     2135 
     2136    this.checkSpellingState(false); 
     2137} 
     2138 
     2139GoogieSpell.prototype.createErrorLink = function(text, id) { 
     2140    var elm = AJS.SPAN({'class': 'googie_link'}); 
     2141    var me = this; 
     2142    var d = function (e) { 
     2143        me.showErrorWindow(elm, id); 
     2144        d = null; 
     2145        return false; 
     2146    }; 
     2147    AJS.AEV(elm, "click", d); 
     2148 
     2149    elm.googie_action_btn = "1"; 
     2150    elm.g_id = id; 
     2151    elm.is_corrected = false; 
     2152    elm.oncontextmenu = d; 
     2153    elm.innerHTML = text; 
     2154    return elm; 
     2155} 
     2156 
     2157GoogieSpell.createPart = function(txt_part) { 
     2158    if(txt_part == " ") 
     2159        return AJS.TN(" "); 
     2160    var result = AJS.SPAN(); 
     2161 
     2162    var is_first = true; 
     2163    var is_safari = (navigator.userAgent.toLowerCase().indexOf("safari") != -1); 
     2164 
     2165    var part = AJS.SPAN(); 
     2166    txt_part = GoogieSpell.escapeSepcial(txt_part); 
     2167    txt_part = txt_part.replace(/\n/g, "<br>"); 
     2168    txt_part = txt_part.replace(/    /g, " &nbsp;"); 
     2169    txt_part = txt_part.replace(/^ /g, "&nbsp;"); 
     2170    txt_part = txt_part.replace(/ $/g, "&nbsp;"); 
     2171     
     2172    part.innerHTML = txt_part; 
     2173 
     2174    return part; 
     2175} 
     2176 
     2177GoogieSpell.prototype.showErrorsInIframe = function() { 
     2178    var output = AJS.DIV(); 
     2179    output.style.textAlign = "left"; 
     2180    var pointer = 0; 
     2181    var results = this.results; 
     2182 
     2183    if(results.length > 0) { 
     2184        for(var i=0; i < results.length; i++) { 
     2185            var offset = results[i]['attrs']['o']; 
     2186            var len = results[i]['attrs']['l']; 
     2187             
     2188            var part_1_text = this.orginal_text.substring(pointer, offset); 
     2189            var part_1 = GoogieSpell.createPart(part_1_text); 
     2190            output.appendChild(part_1); 
     2191            pointer += offset - pointer; 
     2192             
     2193            //If the last child was an error, then insert some space 
     2194            var err_link = this.createErrorLink(this.orginal_text.substr(offset, len), i); 
     2195            this.error_links.push(err_link); 
     2196            output.appendChild(err_link); 
     2197            pointer += len; 
     2198        } 
     2199        //Insert the rest of the orginal text 
     2200        var part_2_text = this.orginal_text.substr(pointer, this.orginal_text.length); 
     2201 
     2202        var part_2 = GoogieSpell.createPart(part_2_text); 
     2203        output.appendChild(part_2); 
     2204    } 
     2205    else 
     2206        output.innerHTML = this.orginal_text; 
     2207 
     2208    var me = this; 
     2209    if(this.custom_item_evaulator) 
     2210        AJS.map(this.error_links, function(elm){me.custom_item_evaulator(me, elm)}); 
     2211     
     2212    AJS.ACN(this.edit_layer, output); 
     2213 
     2214    //Hide text area 
     2215    this.text_area_bottom = this.text_area.offsetTop + this.text_area.offsetHeight; 
     2216 
     2217    AJS.hideElement(this.text_area); 
     2218 
     2219    AJS.insertBefore(this.edit_layer, this.text_area); 
     2220 
     2221    if(this.use_focus) { 
     2222        this.focus_link_t = this.createFocusLink('focus_t'); 
     2223        this.focus_link_b = this.createFocusLink('focus_b'); 
     2224 
     2225        AJS.insertBefore(this.focus_link_t, this.edit_layer); 
     2226        AJS.insertAfter(this.focus_link_b, this.edit_layer); 
     2227    } 
     2228 
     2229    this.edit_layer.scrollTop = this.ta_scroll_top; 
     2230} 
     2231 
     2232 
     2233////// 
     2234// Choose language menu 
     2235////// 
     2236GoogieSpell.prototype.createLangWindow = function() { 
     2237    this.language_window = AJS.DIV({'class': 'googie_window'}); 
     2238    AJS.setWidth(this.language_window, 100); 
     2239 
     2240    this.language_window.googie_action_btn = "1"; 
     2241 
     2242    //Build up the result list 
     2243    var table = AJS.TABLE({'class': 'googie_list'}); 
     2244    AJS.setWidth(table, "100%"); 
     2245    var list = AJS.TBODY(); 
     2246 
     2247    this.lang_elms = new Array(); 
     2248 
     2249    for(i=0; i < this.langlist_codes.length; i++) { 
     2250        var row = AJS.TR(); 
     2251        var item = AJS.TD(); 
     2252        item.googieId = this.langlist_codes[i]; 
     2253        this.lang_elms.push(item); 
     2254        var lang_span = AJS.SPAN(); 
     2255        lang_span.innerHTML = this.lang_to_word[this.langlist_codes[i]]; 
     2256        item.appendChild(AJS.TN(lang_span.innerHTML)); 
     2257 
     2258        var fn = function(e) { 
     2259            var elm = AJS.getEventElm(e); 
     2260            this.deHighlightCurSel(); 
     2261 
     2262            this.setCurrentLanguage(elm.googieId); 
     2263 
     2264            if(this.lang_state_observer != null) { 
     2265                this.lang_state_observer(); 
     2266            } 
     2267 
     2268            this.highlightCurSel(); 
     2269            this.hideLangWindow(); 
     2270        }; 
     2271        AJS.AEV(item, "click", AJS.$b(fn, this)); 
     2272 
     2273        item.onmouseover = function(e) {  
     2274            var i_it = AJS.getEventElm(e); 
     2275            if(i_it.className != "googie_list_selected") 
     2276                i_it.className = "googie_list_onhover"; 
     2277        }; 
     2278        item.onmouseout = function(e) {  
     2279            var i_it = AJS.getEventElm(e); 
     2280            if(i_it.className != "googie_list_selected") 
     2281                i_it.className = "googie_list_onout";  
     2282        }; 
     2283 
     2284        row.appendChild(item); 
     2285        list.appendChild(row); 
     2286    } 
     2287 
     2288    //Close button 
     2289    if(this.use_close_btn) { 
     2290        list.appendChild(this.createCloseButton(this.hideLangWindow)); 
     2291    } 
     2292 
     2293    this.highlightCurSel(); 
     2294 
     2295    table.appendChild(list); 
     2296    this.language_window.appendChild(table); 
     2297} 
     2298 
     2299GoogieSpell.prototype.setCurrentLanguage = function(lan_code) { 
     2300    GOOGIE_CUR_LANG = lan_code; 
     2301 
     2302    //Set cookie 
     2303    var now = new Date(); 
     2304    now.setTime(now.getTime() + 365 * 24 * 60 * 60 * 1000); 
     2305    setCookie('language', lan_code, now); 
     2306} 
     2307 
     2308GoogieSpell.prototype.isLangWindowShown = function() { 
     2309    return this.language_window != null && this.language_window.style.visibility == "visible"; 
     2310} 
     2311 
     2312GoogieSpell.prototype.hideLangWindow = function() { 
     2313    try { 
     2314        this.language_window.style.visibility = "hidden"; 
     2315        this.switch_lan_pic.className = "googie_lang_3d_on"; 
     2316    } 
     2317    catch(e) {} 
     2318} 
     2319 
     2320GoogieSpell.prototype.deHighlightCurSel = function() { 
     2321    this.lang_cur_elm.className = "googie_list_onout"; 
     2322} 
     2323 
     2324GoogieSpell.prototype.highlightCurSel = function() { 
     2325    if(GOOGIE_CUR_LANG == null) 
     2326        GOOGIE_CUR_LANG = GOOGIE_DEFAULT_LANG; 
     2327    for(var i=0; i < this.lang_elms.length; i++) { 
     2328        if(this.lang_elms[i].googieId == GOOGIE_CUR_LANG) { 
     2329            this.lang_elms[i].className = "googie_list_selected"; 
     2330            this.lang_cur_elm = this.lang_elms[i]; 
     2331        } 
     2332        else { 
     2333            this.lang_elms[i].className = "googie_list_onout"; 
     2334        } 
     2335    } 
     2336} 
     2337 
     2338GoogieSpell.prototype.showLangWindow = function(elm, ofst_top, ofst_left) { 
     2339    if(this.show_menu_observer) 
     2340        this.show_menu_observer(this); 
     2341    if(!AJS.isDefined(ofst_top)) 
     2342        ofst_top = 18;  // modified by roundcube 
     2343    if(!AJS.isDefined(ofst_left)) 
     2344        ofst_left = 22; // modified by roundcube 
     2345 
     2346    this.createLangWindow(); 
     2347    AJS.getBody().appendChild(this.language_window); 
     2348 
     2349    var abs_pos = AJS.absolutePosition(elm); 
     2350    AJS.showElement(this.language_window); 
     2351    AJS.setTop(this.language_window, (abs_pos.y+ofst_top)); 
     2352    AJS.setLeft(this.language_window, (abs_pos.x+ofst_left-this.language_window.offsetWidth)); 
     2353 
     2354    this.highlightCurSel(); 
     2355    this.language_window.style.visibility = "visible"; 
     2356} 
     2357 
     2358GoogieSpell.prototype.createChangeLangPic = function() { 
     2359    var img = AJS.IMG({'src': this.img_dir + 'change_lang.gif', 'alt': "Change language"}); 
     2360    img.googie_action_btn = "1"; 
     2361    var switch_lan = AJS.SPAN({'class': 'googie_lang_3d_on', 'style': 'padding-left: 6px;'}, img); 
     2362 
     2363    var fn = function(e) { 
     2364        var elm = AJS.getEventElm(e); 
     2365        if(AJS.nodeName(elm) == 'img') 
     2366            elm = elm.parentNode; 
     2367        if(elm.className == "googie_lang_3d_click") { 
     2368            elm.className = "googie_lang_3d_on"; 
     2369            this.hideLangWindow(); 
     2370        } 
     2371        else { 
     2372            elm.className = "googie_lang_3d_click"; 
     2373            this.showLangWindow(switch_lan); 
     2374        } 
     2375    } 
     2376 
     2377    AJS.AEV(switch_lan, "click", AJS.$b(fn, this)); 
     2378    return switch_lan; 
     2379} 
     2380 
     2381GoogieSpell.prototype.createSpellDiv = function() { 
     2382    var chk_spell = AJS.SPAN({'class': 'googie_check_spelling_link'}); 
     2383 
     2384    chk_spell.innerHTML = this.lang_chck_spell; 
     2385    var spell_img = null; 
     2386    if(this.show_spell_img) 
     2387        spell_img = AJS.IMG({'src': this.img_dir + "spellc.gif"}); 
     2388    return AJS.SPAN(spell_img, " ", chk_spell); 
     2389} 
     2390 
     2391 
     2392////// 
     2393// State functions 
     2394///// 
     2395GoogieSpell.prototype.flashNoSpellingErrorState = function(on_finish) { 
     2396    var no_spell_errors; 
     2397 
     2398    if(on_finish) { 
     2399        var fn = function() { 
     2400            on_finish(); 
     2401            this.checkSpellingState(); 
     2402        }; 
     2403        no_spell_errors = fn; 
     2404    } 
     2405    else 
     2406        no_spell_errors = this.checkSpellingState; 
     2407 
     2408    this.setStateChanged("no_error_found"); 
     2409 
     2410    if(this.main_controller) { 
     2411        AJS.hideElement(this.switch_lan_pic); 
     2412 
     2413        var dummy = AJS.IMG({'src': this.img_dir + "blank.gif", 'style': 'height: 16px; width: 1px;'}); 
     2414        var rsm = AJS.SPAN(); 
     2415        rsm.innerHTML = this.lang_no_error_found; 
     2416 
     2417        AJS.RCN(this.spell_span, AJS.SPAN(dummy, rsm)); 
     2418 
     2419        this.spell_span.className = "googie_check_spelling_ok"; 
     2420        this.spell_span.style.textDecoration = "none"; 
     2421        this.spell_span.style.cursor = "default"; 
     2422 
     2423        AJS.callLater(AJS.$b(no_spell_errors, this), 1200, [false]); 
     2424    } 
     2425} 
     2426 
     2427GoogieSpell.prototype.resumeEditingState = function() { 
     2428    this.setStateChanged("resume_editing"); 
     2429 
     2430    //Change link text to resume 
     2431    if(this.main_controller) { 
     2432        AJS.hideElement(this.switch_lan_pic); 
     2433        var dummy = AJS.IMG({'src': this.img_dir + "blank.gif", 'style': 'height: 16px; width: 1px;'}); 
     2434        var rsm = AJS.SPAN(); 
     2435        rsm.innerHTML = this.lang_rsm_edt; 
     2436        AJS.RCN(this.spell_span, AJS.SPAN(dummy, rsm)); 
     2437     
     2438        var fn = function(e) { 
     2439            this.resumeEditing(); 
     2440        } 
     2441        this.spell_span.onclick = AJS.$b(fn, this); 
     2442 
     2443        this.spell_span.className = "googie_resume_editing"; 
     2444    } 
     2445 
     2446    try { this.edit_layer.scrollTop = this.ta_scroll_top; } 
     2447    catch(e) { } 
     2448} 
     2449 
     2450GoogieSpell.prototype.checkSpellingState = function(fire) { 
     2451    if(!AJS.isDefined(fire) || fire) 
     2452        this.setStateChanged("spell_check"); 
     2453 
     2454    if(this.show_change_lang_pic) 
     2455        this.switch_lan_pic = this.createChangeLangPic(); 
     2456    else 
     2457        this.switch_lan_pic = AJS.SPAN(); 
     2458 
     2459    var span_chck = this.createSpellDiv(); 
     2460    var fn = function() { 
     2461        this.spellCheck(); 
     2462    }; 
     2463 
     2464    if(this.custom_spellcheck_starter) 
     2465        span_chck.onclick = this.custom_spellcheck_starter; 
     2466    else { 
     2467        span_chck.onclick = AJS.$b(fn, this); 
     2468    } 
     2469 
     2470    this.spell_span = span_chck; 
     2471    if(this.main_controller) { 
     2472        if(this.change_lang_pic_placement == "left") 
     2473            AJS.RCN(this.spell_container, span_chck, " ", this.switch_lan_pic); 
     2474        else 
     2475            AJS.RCN(this.spell_container, this.switch_lan_pic, " ", span_chck); 
     2476    } 
     2477    // modified by roundcube 
     2478    this.check_link = span_chck; 
     2479} 
     2480 
     2481 
     2482////// 
     2483// Misc. functions 
     2484///// 
    6312485GoogieSpell.item_onmouseover = function(e) { 
    632   var elm = GoogieSpell.getEventElm(e); 
    633   if(elm.className != "googie_list_close" && elm.className != "googie_list_revert") 
    634     elm.className = "googie_list_onhover"; 
    635   else 
    636     elm.parentNode.className = "googie_list_onhover"; 
    637 } 
    638  
     2486    var elm = AJS.getEventElm(e); 
     2487    if(elm.className != "googie_list_revert" && elm.className != "googie_list_close") 
     2488        elm.className = "googie_list_onhover"; 
     2489    else 
     2490        elm.parentNode.className = "googie_list_onhover"; 
     2491} 
    6392492GoogieSpell.item_onmouseout = function(e) { 
    640   var elm = GoogieSpell.getEventElm(e); 
    641   if(elm.className != "googie_list_close" && elm.className != "googie_list_revert") 
    642     elm.className = "googie_list_onout"; 
    643   else 
    644     elm.parentNode.className = "googie_list_onout"; 
    645 } 
    646  
    647 GoogieSpell.prototype.getGoogleUrl = function() { 
    648   return this.server_url + GOOGIE_CUR_LANG; 
    649 } 
    650  
    651 GoogieSpell.prototype.spellCheck = function(elm, name) { 
    652   this.ta_scroll_top = this.text_area.scrollTop; 
    653  
    654   this.appendIndicator(elm); 
    655  
    656   try { 
    657     this.hideLangWindow(); 
    658   } 
    659   catch(e) {} 
    660    
    661   this.gselm = elm; 
    662  
    663   this.createEditLayer(this.text_area.offsetWidth, this.text_area.offsetHeight); 
    664  
    665   this.createErrorWindow(); 
    666   AJS.getBody().appendChild(this.error_window); 
    667  
    668   try { netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead"); }  
    669   catch (e) { } 
    670  
    671   this.gselm.onclick = null; 
    672  
    673   this.orginal_text = this.text_area.value; 
    674   var me = this; 
    675  
    676   //Create request 
    677   var d = AJS.getRequest(this.getGoogleUrl()); 
    678   var reqdone = function(req) { 
    679     var r_text = req.responseText; 
    680     if(r_text.match(/<c.*>/) != null) { 
    681       var results = GoogieSpell.parseResult(r_text); 
    682       //Before parsing be sure that errors were found 
    683       me.results = results; 
    684       me.showErrorsInIframe(results); 
    685       me.resumeEditingState(); 
     2493    var elm = AJS.getEventElm(e); 
     2494    if(elm.className != "googie_list_revert" && elm.className != "googie_list_close") 
     2495        elm.className = "googie_list_onout"; 
     2496    else 
     2497        elm.parentNode.className = "googie_list_onout"; 
     2498} 
     2499 
     2500GoogieSpell.prototype.createCloseButton = function(c_fn) { 
     2501    return this.createButton(this.lang_close, 'googie_list_close', AJS.$b(c_fn, this)); 
     2502} 
     2503 
     2504GoogieSpell.prototype.createButton = function(name, css_class, c_fn) { 
     2505    var btn_row = AJS.TR(); 
     2506    var btn = AJS.TD(); 
     2507 
     2508    btn.onmouseover = GoogieSpell.item_onmouseover; 
     2509    btn.onmouseout = GoogieSpell.item_onmouseout; 
     2510 
     2511    var spn_btn; 
     2512    if(css_class != "") { 
     2513        spn_btn = AJS.SPAN({'class': css_class}); 
     2514        spn_btn.innerHTML = name; 
    6862515    } 
    6872516    else { 
    688       me.flashNoSpellingErrorState(); 
    689     } 
    690     me.removeIndicator(); 
    691   }; 
    692  
    693   var reqfailed = function(req) { 
    694     alert("An error was encountered on the server. Please try again later."); 
    695     AJS.removeElement(me.gselm); 
    696     me.checkSpellingState(); 
    697     me.removeIndicator(); 
    698   }; 
    699    
    700   d.addCallback(reqdone); 
    701   d.addErrback(reqfailed); 
    702  
    703   var req_text = GoogieSpell.escapeSepcial(this.orginal_text); 
    704   d.sendReq(GoogieSpell.createXMLReq(req_text)); 
    705 } 
    706  
    707 GoogieSpell.escapeSepcial = function(val) { 
    708   return val.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"); 
    709 } 
    710  
    711 GoogieSpell.createXMLReq = function (text) { 
    712   return '<?xml version="1.0" encoding="utf-8" ?><spellrequest textalreadyclipped="0" ignoredups="0" ignoredigits="1" ignoreallcaps="1"><text>' + text + '</text></spellrequest>'; 
    713 } 
    714  
    715 //Retunrs an array 
    716 //result[item] -> ['attrs'] 
    717 //                ['suggestions'] 
    718 GoogieSpell.parseResult = function(r_text) { 
    719   var re_split_attr_c = /\w="\d+"/g; 
    720   var re_split_text = /\t/g; 
    721  
    722   var matched_c = r_text.match(/<c[^>]*>[^<]*<\/c>/g); 
    723   var results = new Array(); 
    724    
    725   for(var i=0; i < matched_c.length; i++) { 
    726     var item = new Array(); 
    727  
    728     //Get attributes 
    729     item['attrs'] = new Array(); 
    730     var split_c = matched_c[i].match(re_split_attr_c); 
    731     for(var j=0; j < split_c.length; j++) { 
    732       var c_attr = split_c[j].split(/=/); 
    733       item['attrs'][c_attr[0]] = parseInt(c_attr[1].replace('"', '')); 
    734     } 
    735  
    736     //Get suggestions 
    737     item['suggestions'] = new Array(); 
    738     var only_text = matched_c[i].replace(/<[^>]*>/g, ""); 
    739     var split_t = only_text.split(re_split_text); 
    740     for(var k=0; k < split_t.length; k++) { 
    741     if(split_t[k] != "") 
    742       item['suggestions'].push(split_t[k]); 
    743     } 
    744     results.push(item); 
    745   } 
    746   return results; 
    747 } 
    748  
    749 /**** 
    750  Error window (the drop-down window) 
    751 ****/ 
    752 GoogieSpell.prototype.createErrorWindow = function() { 
    753   this.error_window = AJS.DIV(); 
    754   this.error_window.className = "googie_window"; 
    755 } 
    756  
    757 GoogieSpell.prototype.hideErrorWindow = function() { 
    758   this.error_window.style.visibility = "hidden"; 
    759 } 
    760  
    761 GoogieSpell.prototype.updateOrginalText = function(offset, old_value, new_value, id) { 
    762   var part_1 = this.orginal_text.substring(0, offset); 
    763   var part_2 = this.orginal_text.substring(offset+old_value.length); 
    764   this.orginal_text = part_1 + new_value + part_2; 
    765   var add_2_offset = new_value.length - old_value.length; 
    766   for(var j=0; j < this.results.length; j++) { 
    767     //Don't edit the offset of the current item 
    768     if(j != id && j > id){ 
    769       this.results[j]['attrs']['o'] += add_2_offset; 
    770     } 
    771   } 
    772 } 
    773  
    774 GoogieSpell.prototype.saveOldValue = function (id, old_value) { 
    775   this.results[id]['is_changed'] = true; 
    776   this.results[id]['old_value'] = old_value; 
    777 } 
    778  
    779 GoogieSpell.prototype.showErrorWindow = function(elm, id) { 
    780   var me = this; 
    781  
    782   var abs_pos = GoogieSpell.absolutePosition(elm); 
    783   abs_pos.y -= this.edit_layer.scrollTop; 
    784   this.error_window.style.visibility = "visible"; 
    785   this.error_window.style.top = (abs_pos.y+20) + "px"; 
    786   this.error_window.style.left = (abs_pos.x) + "px"; 
    787   this.error_window.innerHTML = ""; 
    788  
    789   //Build up the result list 
    790   var table = AJS.TABLE({'class': 'googie_list'}); 
    791   var list = AJS.TBODY(); 
    792  
    793   var suggestions = this.results[id]['suggestions']; 
    794   var offset = this.results[id]['attrs']['o']; 
    795   var len = this.results[id]['attrs']['l']; 
    796  
    797   if(suggestions.length == 0) { 
    798     var row = AJS.TR(); 
    799     var item = AJS.TD(); 
    800     var dummy = AJS.SPAN(); 
    801     item.appendChild(AJS.TN("No suggestions :(")); 
    802     row.appendChild(item); 
    803     list.appendChild(row); 
    804   } 
    805  
    806   for(i=0; i < suggestions.length; i++) { 
    807     var row = AJS.TR(); 
    808     var item = AJS.TD(); 
    809     var dummy = AJS.SPAN(); 
    810     dummy.innerHTML = suggestions[i]; 
    811     item.appendChild(AJS.TN(dummy.innerHTML)); 
    812      
    813     item.onclick = function(e) { 
    814       var l_elm = GoogieSpell.getEventElm(e); 
    815       var old_value = elm.innerHTML; 
    816       var new_value = l_elm.innerHTML; 
    817  
    818       elm.style.color = "green"; 
    819       elm.innerHTML = l_elm.innerHTML; 
    820       me.hideErrorWindow(); 
    821  
    822       me.updateOrginalText(offset, old_value, new_value, id); 
    823  
    824       //Update to the new length 
    825       me.results[id]['attrs']['l'] = new_value.length; 
    826       me.saveOldValue(id, old_value); 
    827     }; 
    828     item.onmouseover = GoogieSpell.item_onmouseover; 
    829     item.onmouseout = GoogieSpell.item_onmouseout; 
    830     row.appendChild(item); 
    831     list.appendChild(row); 
    832   } 
    833    
    834   //The element is changed, append the revert 
    835   if(this.results[id]['is_changed']) { 
    836     var old_value = this.results[id]['old_value']; 
    837     var offset = this.results[id]['attrs']['o']; 
    838     var revert_row = AJS.TR(); 
    839     var revert = AJS.TD(); 
    840  
    841     revert.onmouseover = GoogieSpell.item_onmouseover; 
    842     revert.onmouseout = GoogieSpell.item_onmouseout; 
    843     var rev_span = AJS.SPAN({'class': 'googie_list_revert'}); 
    844     rev_span.innerHTML = this.lang_revert + " " + old_value; 
    845     revert.appendChild(rev_span); 
    846  
    847     revert.onclick = function(e) {  
    848       me.updateOrginalText(offset, elm.innerHTML, old_value, id); 
    849       elm.style.color = "#b91414"; 
    850       elm.innerHTML = old_value; 
    851       me.hideErrorWindow(); 
    852     }; 
    853  
    854     revert_row.appendChild(revert); 
    855     list.appendChild(revert_row); 
    856   } 
    857  
    858   //Append the edit box 
    859   var edit_row = AJS.TR(); 
    860   var edit = AJS.TD(); 
    861  
    862   var edit_input = AJS.INPUT({'style': 'width: 120px; margin:0; padding:0'}); 
    863  
    864   var onsub = function () { 
    865     if(edit_input.value != "") { 
    866       me.saveOldValue(id, elm.innerHTML); 
    867       me.updateOrginalText(offset, elm.innerHTML, edit_input.value, id); 
    868       elm.style.color = "green" 
    869       elm.innerHTML = edit_input.value; 
    870        
    871       me.hideErrorWindow(); 
    872       return false; 
    873     } 
    874   }; 
    875    
    876   var ok_pic = AJS.IMG({'src': this.img_dir + "ok.gif", 'style': 'width: 32px; height: 16px; margin-left: 2px; margin-right: 2px;'}); 
    877   var edit_form = AJS.FORM({'style': 'margin: 0; padding: 0'}, edit_input, ok_pic); 
    878   ok_pic.onclick = onsub; 
    879   edit_form.onsubmit = onsub; 
    880    
    881   edit.appendChild(edit_form); 
    882   edit_row.appendChild(edit); 
    883   list.appendChild(edit_row); 
    884  
    885   //Close button 
    886   var close_row = AJS.TR(); 
    887   var close = AJS.TD(); 
    888  
    889   close.onmouseover = GoogieSpell.item_onmouseover; 
    890   close.onmouseout = GoogieSpell.item_onmouseout; 
    891  
    892   var spn_close = AJS.SPAN({'class': 'googie_list_close'}); 
    893   spn_close.innerHTML = this.lang_close; 
    894   close.appendChild(spn_close); 
    895   close.onclick = function() { me.hideErrorWindow()}; 
    896   close_row.appendChild(close); 
    897   list.appendChild(close_row); 
    898  
    899   table.appendChild(list); 
    900   this.error_window.appendChild(table); 
    901 } 
    902  
    903  
    904 /**** 
    905   Edit layer (the layer where the suggestions are stored) 
    906 ****/ 
    907 GoogieSpell.prototype.createEditLayer = function(width, height) { 
    908   this.edit_layer = AJS.DIV({'class': 'googie_edit_layer'}); 
    909    
    910   //Set the style so it looks like edit areas 
    911   this.edit_layer.className = this.text_area.className; 
    912   this.edit_layer.style.border = "1px solid #999"; 
    913   this.edit_layer.style.overflow = "auto"; 
    914   this.edit_layer.style.backgroundColor = "#F1EDFE"; 
    915   this.edit_layer.style.padding = "3px"; 
    916  
    917   this.edit_layer.style.width = (width-8) + "px"; 
    918   this.edit_layer.style.height = height + "px"; 
    919 } 
    920  
    921 GoogieSpell.prototype.resumeEditing = function(e, me) { 
    922   this.setStateChanged("check_spelling"); 
    923   me.switch_lan_pic.style.display = "inline"; 
    924  
    925   this.el_scroll_top = me.edit_layer.scrollTop; 
    926  
    927   var elm = GoogieSpell.getEventElm(e); 
    928   AJS.replaceChildNodes(elm, this.createSpellDiv()); 
    929  
    930   elm.onclick = function(e) { 
    931     me.spellCheck(elm, me.text_area.id); 
    932   }; 
    933   me.hideErrorWindow(); 
    934  
    935   //Remove the EDIT_LAYER 
    936   me.edit_layer.parentNode.removeChild(me.edit_layer); 
    937  
    938   me.text_area.value = me.orginal_text; 
    939   AJS.showElement(me.text_area); 
    940   me.gselm.className = "googie_no_style"; 
    941  
    942   me.text_area.scrollTop = this.el_scroll_top; 
    943  
    944   elm.onmouseout = null; 
    945 } 
    946  
    947 GoogieSpell.prototype.createErrorLink = function(text, id) { 
    948   var elm = AJS.SPAN({'class': 'googie_link'}); 
    949   var me = this; 
    950   elm.onclick = function () { 
    951     me.showErrorWindow(elm, id); 
    952   }; 
    953   elm.innerHTML = text; 
    954   return elm; 
    955 } 
    956  
    957 GoogieSpell.createPart = function(txt_part) { 
    958   if(txt_part == " ") 
    959     return AJS.TN(" "); 
    960   var result = AJS.SPAN(); 
    961  
    962   var is_first = true; 
    963   var is_safari = (navigator.userAgent.toLowerCase().indexOf("safari") != -1); 
    964  
    965   var part = AJS.SPAN(); 
    966   txt_part = GoogieSpell.escapeSepcial(txt_part); 
    967   txt_part = txt_part.replace(/\n/g, "<br>"); 
    968   txt_part = txt_part.replace(/  /g, " &nbsp;"); 
    969   txt_part = txt_part.replace(/^ /g, "&nbsp;"); 
    970   txt_part = txt_part.replace(/ $/g, "&nbsp;"); 
    971    
    972   part.innerHTML = txt_part; 
    973  
    974   return part; 
    975 } 
    976  
    977 GoogieSpell.prototype.showErrorsInIframe = function(results) { 
    978   var output = AJS.DIV(); 
    979   output.style.textAlign = "left"; 
    980   var pointer = 0; 
    981   for(var i=0; i < results.length; i++) { 
    982     var offset = results[i]['attrs']['o']; 
    983     var len = results[i]['attrs']['l']; 
    984      
    985     var part_1_text = this.orginal_text.substring(pointer, offset); 
    986     var part_1 = GoogieSpell.createPart(part_1_text); 
    987     output.appendChild(part_1); 
    988     pointer += offset - pointer; 
    989      
    990     //If the last child was an error, then insert some space 
    991     output.appendChild(this.createErrorLink(this.orginal_text.substr(offset, len), i)); 
    992     pointer += len; 
    993   } 
    994   //Insert the rest of the orginal text 
    995   var part_2_text = this.orginal_text.substr(pointer, this.orginal_text.length); 
    996  
    997   var part_2 = GoogieSpell.createPart(part_2_text); 
    998   output.appendChild(part_2); 
    999  
    1000   this.edit_layer.appendChild(output); 
    1001  
    1002   //Hide text area 
    1003   AJS.hideElement(this.text_area); 
    1004   this.text_area.parentNode.insertBefore(this.edit_layer, this.text_area.nextSibling); 
    1005   this.edit_layer.scrollTop = this.ta_scroll_top; 
    1006 } 
    1007  
    1008 GoogieSpell.Position = function(x, y) { 
    1009   this.x = x; 
    1010   this.y = y; 
    1011 }        
    1012  
    1013 //Get the absolute position of menu_slide 
    1014 GoogieSpell.absolutePosition = function(element) { 
    1015   //Create a new object that has elements y and x pos... 
    1016   var posObj = new GoogieSpell.Position(element.offsetLeft, element.offsetTop); 
    1017  
    1018   //Check if the element has an offsetParent - if it has .. loop until it has not 
    1019   if(element.offsetParent) { 
    1020     var temp_pos =      GoogieSpell.absolutePosition(element.offsetParent); 
    1021     posObj.x += temp_pos.x; 
    1022     posObj.y += temp_pos.y; 
    1023   } 
    1024   return posObj; 
    1025 } 
    1026  
    1027 GoogieSpell.getEventElm = function(e) { 
    1028         var targ; 
    1029         if (!e) var e = window.event; 
    1030         if (e.target) targ = e.target; 
    1031         else if (e.srcElement) targ = e.srcElement; 
    1032         if (targ.nodeType == 3) // defeat Safari bug 
    1033                 targ = targ.parentNode; 
    1034   return targ; 
     2517        spn_btn = AJS.TN(name); 
     2518    } 
     2519    btn.appendChild(spn_btn); 
     2520    AJS.AEV(btn, "click", c_fn); 
     2521    btn_row.appendChild(btn); 
     2522 
     2523    return btn_row; 
    10352524} 
    10362525 
    10372526GoogieSpell.prototype.removeIndicator = function(elm) { 
    1038   // modified by roundcube 
    1039   if (window.rcube_webmail_client) 
    1040     rcube_webmail_client.set_busy(false); 
    1041   //AJS.removeElement(this.indicator); 
     2527    // modified by roundcube 
     2528    if (window.rcmail) 
     2529        rcmail.set_busy(false); 
     2530    //try { AJS.removeElement(this.indicator); } 
     2531    //catch(e) {} 
    10422532} 
    10432533 
    10442534GoogieSpell.prototype.appendIndicator = function(elm) { 
    1045   // modified by roundcube 
    1046   if (window.rcube_webmail_client) 
    1047     rcube_webmail_client.set_busy(true, 'checking'); 
    1048 /* 
    1049   var img = AJS.IMG({'src': this.img_dir + 'indicator.gif', 'style': 'margin-right: 5px;'}); 
    1050   img.style.width = "16px"; 
    1051   img.style.height = "16px"; 
    1052   this.indicator = img; 
    1053   img.style.textDecoration = "none"; 
    1054   AJS.insertBefore(img, elm); 
     2535    // modified by roundcube 
     2536    if (window.rcmail) 
     2537        rcmail.set_busy(true, 'checking'); 
     2538  /* 
     2539    var img = AJS.IMG({'src': this.img_dir + 'indicator.gif', 'style': 'margin-right: 5px;'}); 
     2540    AJS.setWidth(img, 16); 
     2541    AJS.setHeight(img, 16); 
     2542    this.indicator = img; 
     2543    img.style.textDecoration = "none"; 
     2544    try { 
     2545        AJS.insertBefore(img, elm); 
     2546    } 
     2547    catch(e) {} 
    10552548  */ 
    10562549} 
    10572550 
    1058 /**** 
    1059  Choose language 
    1060 ****/ 
    1061 GoogieSpell.prototype.createLangWindow = function() { 
    1062   this.language_window = AJS.DIV({'class': 'googie_window'}); 
    1063   this.language_window.style.width = "130px"; 
    1064  
    1065   //Build up the result list 
    1066   var table = AJS.TABLE({'class': 'googie_list'}); 
    1067   var list = AJS.TBODY(); 
    1068  
    1069   this.lang_elms = new Array(); 
    1070  
    1071   for(i=0; i < this.langlist_codes.length; i++) { 
    1072     var row = AJS.TR(); 
    1073     var item = AJS.TD(); 
    1074     item.googieId = this.langlist_codes[i]; 
    1075     this.lang_elms.push(item); 
    1076     var lang_span = AJS.SPAN(); 
    1077     lang_span.innerHTML = this.lang_to_word[this.langlist_codes[i]]; 
    1078     item.appendChild(AJS.TN(lang_span.innerHTML)); 
    1079  
    1080     var me = this; 
    1081      
    1082     item.onclick = function(e) { 
    1083       var elm = GoogieSpell.getEventElm(e); 
    1084       me.deHighlightCurSel(); 
    1085  
    1086       me.setCurrentLanguage(elm.googieId); 
    1087  
    1088       if(me.lang_state_observer != null) { 
    1089         me.lang_state_observer(); 
    1090       } 
    1091  
    1092       me.highlightCurSel(); 
    1093       me.hideLangWindow(); 
    1094     }; 
    1095  
    1096     item.onmouseover = function(e) {  
    1097       var i_it = GoogieSpell.getEventElm(e); 
    1098       if(i_it.className != "googie_list_selected") 
    1099         i_it.className = "googie_list_onhover"; 
    1100     }; 
    1101     item.onmouseout = function(e) {  
    1102       var i_it = GoogieSpell.getEventElm(e); 
    1103       if(i_it.className != "googie_list_selected") 
    1104         i_it.className = "googie_list_onout";  
    1105     }; 
    1106  
    1107     row.appendChild(item); 
    1108     list.appendChild(row); 
    1109   } 
    1110  
    1111   this.highlightCurSel(); 
    1112  
    1113   //Close button 
    1114   var close_row = AJS.TR(); 
    1115   var close = AJS.TD(); 
    1116   close.onmouseover = GoogieSpell.item_onmouseover; 
    1117   close.onmouseout = GoogieSpell.item_onmouseout; 
    1118   var spn_close = AJS.SPAN({'class': 'googie_list_close'}); 
    1119   spn_close.innerHTML = this.lang_close; 
    1120   close.appendChild(spn_close); 
    1121   var me = this; 
    1122   close.onclick = function(e) { 
    1123     me.hideLangWindow(); GoogieSpell.item_onmouseout(e); 
    1124   }; 
    1125   close_row.appendChild(close); 
    1126   list.appendChild(close_row); 
    1127  
    1128   table.appendChild(list); 
    1129   this.language_window.appendChild(table); 
    1130 } 
    1131  
    1132 GoogieSpell.prototype.setCurrentLanguage = function(lan_code) { 
    1133   GOOGIE_CUR_LANG = lan_code; 
    1134  
    1135   //Set cookie 
    1136   var now = new Date(); 
    1137   now.setTime(now.getTime() + 365 * 24 * 60 * 60 * 1000); 
    1138   setCookie('language', lan_code, now); 
    1139 } 
    1140  
    1141 GoogieSpell.prototype.hideLangWindow = function() { 
    1142   this.language_window.style.visibility = "hidden"; 
    1143   this.switch_lan_pic.className = "googie_lang_3d_on"; 
    1144 } 
    1145  
    1146 GoogieSpell.prototype.deHighlightCurSel = function() { 
    1147   this.lang_cur_elm.className = "googie_list_onout"; 
    1148 } 
    1149  
    1150 GoogieSpell.prototype.highlightCurSel = function() { 
    1151   for(var i=0; i < this.lang_elms.length; i++) { 
    1152     if(this.lang_elms[i].googieId == GOOGIE_CUR_LANG) { 
    1153       this.lang_elms[i].className = "googie_list_selected"; 
    1154       this.lang_cur_elm = this.lang_elms[i]; 
    1155     } 
    1156     else { 
    1157       this.lang_elms[i].className = "googie_list_onout"; 
    1158     } 
    1159   } 
    1160 } 
    1161  
    1162 GoogieSpell.prototype.showLangWindow = function(elm, ofst_top, ofst_left) { 
    1163   if(!AJS.isDefined(ofst_top)) 
    1164     ofst_top = 20; 
    1165   if(!AJS.isDefined(ofst_left)) 
    1166     ofst_left = 50; 
    1167  
    1168   this.createLangWindow(); 
    1169   AJS.getBody().appendChild(this.language_window); 
    1170  
    1171   var abs_pos = GoogieSpell.absolutePosition(elm); 
    1172   AJS.showElement(this.language_window); 
    1173   this.language_window.style.top = (abs_pos.y+ofst_top) + "px"; 
    1174   this.language_window.style.left = (abs_pos.x+ofst_left-this.language_window.offsetWidth) + "px"; 
    1175   this.highlightCurSel(); 
    1176   this.language_window.style.visibility = "visible"; 
    1177 } 
    1178  
    1179 GoogieSpell.prototype.flashNoSpellingErrorState = function() { 
    1180   this.setStateChanged("no_error_found"); 
    1181   var me = this; 
    1182   AJS.hideElement(this.switch_lan_pic); 
    1183   this.gselm.innerHTML = this.lang_no_error_found; 
    1184   this.gselm.className = "googie_check_spelling_ok"; 
    1185   this.gselm.style.textDecoration = "none"; 
    1186   this.gselm.style.cursor = "default"; 
    1187   var fu = function() { 
    1188     AJS.removeElement(me.gselm); 
    1189     me.checkSpellingState(); 
    1190   }; 
    1191   setTimeout(fu, 1000); 
    1192 } 
    1193  
    1194 GoogieSpell.prototype.resumeEditingState = function() { 
    1195   this.setStateChanged("resume_editing"); 
    1196   var me = this; 
    1197   AJS.hideElement(me.switch_lan_pic); 
    1198  
    1199   //Change link text to resume 
    1200   me.gselm.innerHTML = this.lang_rsm_edt; 
    1201   me.gselm.onclick = function(e) { 
    1202     me.resumeEditing(e, me); 
    1203   } 
    1204   me.gselm.className = "googie_check_spelling_ok"; 
    1205   me.edit_layer.scrollTop = me.ta_scroll_top; 
    1206 } 
    1207  
    1208 GoogieSpell.prototype.createChangeLangPic = function() { 
    1209   var switch_lan = AJS.A({'class': 'googie_lang_3d_on', 'style': 'padding-left: 6px;'}, AJS.IMG({'src': this.img_dir + 'change_lang.gif', 'alt': "Change language"})); 
    1210   switch_lan.onmouseover = function() { 
    1211     if(this.className != "googie_lang_3d_click") 
    1212       this.className = "googie_lang_3d_on"; 
    1213   } 
    1214  
    1215   var me = this; 
    1216   switch_lan.onclick = function() { 
    1217     if(this.className == "googie_lang_3d_click") { 
    1218       me.hideLangWindow(); 
    1219     } 
    1220     else { 
    1221       me.showLangWindow(switch_lan); 
    1222       this.className = "googie_lang_3d_click"; 
    1223     } 
    1224   } 
    1225   return switch_lan; 
    1226 } 
    1227  
    1228 GoogieSpell.prototype.createSpellDiv = function() { 
    1229   var chk_spell = AJS.SPAN({'class': 'googie_check_spelling_link'}); 
    1230   chk_spell.innerHTML = this.lang_chck_spell; 
    1231   var spell_img = null; 
    1232   if(this.show_spell_img) 
    1233     spell_img = AJS.IMG({'src': this.img_dir + "spellc.gif"}); 
    1234   return AJS.SPAN(spell_img, " ", chk_spell); 
    1235 } 
    1236  
    1237 GoogieSpell.prototype.checkSpellingState = function() { 
    1238   this.setStateChanged("check_spelling"); 
    1239   var me = this; 
    1240   if(this.show_change_lang_pic) 
    1241     this.switch_lan_pic = this.createChangeLangPic(); 
    1242   else 
    1243     this.switch_lan_pic = AJS.SPAN(); 
    1244  
    1245   var span_chck = this.createSpellDiv(); 
    1246   span_chck.onclick = function() { 
    1247     me.spellCheck(span_chck); 
    1248   } 
    1249   AJS.appendChildNodes(this.spell_container, span_chck, " ", this.switch_lan_pic); 
    1250   // modified by roundcube 
    1251   this.check_link = span_chck; 
    1252 } 
    1253  
    1254 GoogieSpell.prototype.setLanguages = function(lang_dict) { 
    1255   this.lang_to_word = lang_dict; 
    1256   this.langlist_codes = AJS.keys(lang_dict); 
    1257 } 
    1258  
    1259 GoogieSpell.prototype.decorateTextarea = function(id, /*optional*/spell_container_id, force_width) { 
    1260   var me = this; 
    1261  
    1262   if(typeof(id) == "string") 
    1263     this.text_area = AJS.getElement(id); 
    1264   else 
    1265     this.text_area = id; 
    1266  
    1267   var r_width; 
    1268  
    1269   if(this.text_area != null) { 
    1270     if(AJS.isDefined(spell_container_id)) { 
    1271       if(typeof(spell_container_id) == "string") 
    1272         this.spell_container = AJS.getElement(spell_container_id); 
    1273       else 
    1274         this.spell_container = spell_container_id; 
    1275     } 
    1276     else { 
    1277       var table = AJS.TABLE(); 
    1278       var tbody = AJS.TBODY(); 
    1279       var tr = AJS.TR(); 
    1280       if(AJS.isDefined(force_width)) { 
    1281         r_width = force_width; 
    1282       } 
    1283       else { 
    1284         r_width = this.text_area.offsetWidth + "px"; 
    1285       } 
    1286  
    1287       var spell_container = AJS.TD(); 
    1288       this.spell_container = spell_container; 
    1289  
    1290       tr.appendChild(spell_container); 
    1291  
    1292       tbody.appendChild(tr); 
    1293       table.appendChild(tbody); 
    1294  
    1295       AJS.insertBefore(table, this.text_area); 
    1296  
    1297       //Set width 
    1298       table.style.width = '100%';  // modified by roundcube (old: r_width) 
    1299       spell_container.style.width = r_width; 
    1300       spell_container.style.textAlign = "right"; 
    1301     } 
    1302  
    1303     this.checkSpellingState(); 
    1304   } 
    1305   else { 
    1306     alert("Text area not found"); 
    1307   } 
    1308 } 
     2551GoogieSpell.prototype.createFocusLink = function(name) { 
     2552    return AJS.A({'href': 'javascript:;', name: name}); 
     2553} 
  • skins/default/googiespell.css

    r2c63370 rffa6c10  
    6363} 
    6464 
    65 .googie_check_spelling_link {   
     65.googie_resume_editing, 
     66.googie_check_spelling_link { 
    6667  color: #CC0000; 
    6768  font-size: 11px; 
     
    7071} 
    7172 
     73.googie_resume_editing:hover, 
    7274.googie_check_spelling_link:hover { 
    7375  text-decoration: underline; 
     76} 
     77 
     78.googie_resume_editing { 
     79  color: green; 
    7480} 
    7581 
Note: See TracChangeset for help on using the changeset viewer.