Changeset 4482


Ignore:
Timestamp:
24/11/08 09:47:36 (11 years ago)
Author:
cbyrom
Message:

Replace 'viewItems' controller and flesh out selected Items controller - to allow user to plot results out in Google Earth and conTerra +
add wmc client section to config file to allow this to be specified as
an alternative service + tidy up some logging and unused code + update
prototype version to avoid IE problems.

Location:
MILK/trunk/milk_server
Files:
3 added
9 edited

Legend:

Unmodified
Added
Removed
  • MILK/trunk/milk_server/milk_server/config/ndgMiddleware.py

    r4477 r4482  
    4444        self.globals.atomEditorEnabled = cf.get('ATOM_EDITOR','enabled') 
    4545        self.globals.debugAtomEditor = cf.get('ATOM_EDITOR','debug') 
     46 
     47        self.globals.wmcClientURL = cf.get('WMC_CLIENT','url') 
    4648        
    4749        self.globals.disclaimer=cf.get('default','disclaimer') 
  • MILK/trunk/milk_server/milk_server/config/routing.py

    r4476 r4482  
    4444    map.connect('retrieve/:uri', controller = 'retrieve') 
    4545    map.connect('selectedItems',controller='selectedItems',action='index') 
     46    map.connect('viewItems',controller='viewItems',action='index') 
    4647    map.connect('addSelectedItem/:entryid/:divid', 
    4748                controller='selectedItems',action='addSelectedItem') 
  • MILK/trunk/milk_server/milk_server/controllers/discovery.py

    r4476 r4482  
    3939        
    4040        # see if this is a discovery search or a more complicated search 
    41         if 'searchTarget' not in self.inputs: self.inputs['searchTarget']='Discovery' 
     41        if 'searchTarget' not in self.inputs:  
     42            self.inputs['searchTarget']='Discovery' 
    4243         
    4344        #the following need to be defined 
  • MILK/trunk/milk_server/milk_server/controllers/retrieve.py

    r4468 r4482  
    177177                 
    178178            elif viewFormat == ndgObject.NDGA0_DOC_TYPE: 
    179                 #renderTemplate = 'csml' 
    180179                renderTemplate = 'content' 
    181180                name=self.uri.localID 
  • MILK/trunk/milk_server/milk_server/controllers/selectedItems.py

    r4476 r4482  
    1313from milk_server.lib.base import * 
    1414from paste.request import parse_querystring 
    15 from milk_server.models import selectedItem 
    16 import copy 
    17 import urllib 
    18  
    19 import logging 
    20 log = logging.getLogger(__name__) 
     15from milk_server.models.selectedItem import SelectedItem 
     16import copy, urllib, logging 
    2117 
    2218class SelecteditemsController(BaseController): 
     
    2622        Default controller method to handle the initial requests to the page 
    2723        """ 
    28         import pdb 
    29         pdb.set_trace() 
    3024        # Get the inputs passed via the URL 
    3125        self.inputs=dict(parse_querystring(request.environ)) 
     
    5751                wmcURLs = [] 
    5852                for item in session['selectedItems']: 
    59                     log.debug('SelectedItem: kmlList = %s, wmcList = %s' % ( 
     53                    logging.debug('SelectedItem: kmlList = %s, wmcList = %s' % ( 
    6054                        `item.kmlList`, `item.wmcList`)) 
    6155 
     
    9387             
    9488        return render('selectedItems') 
     89 
    9590     
    9691    def getSelectedItems(self): 
     
    134129        session.save() 
    135130         
     131 
     132    def getConTerraDoc(wmcURLs): 
     133        """ 
     134        Construct an aggregated XML file and display this as a temporary webpage; this will automatically be 
     135        POSTED so that the data is sent via POST to the Con Terra mapClient to visualise (as required by this client) 
     136        @param wmcURLS: An array of URLs pointing to WMC docs to visualise 
     137        """ 
     138        # firstly, retrieve each WMC doc 
     139        wmcDocs = [] 
     140        for wmcURL in wmcURLs: 
     141            wmcDocs.append(WMC(wmcURL)) 
     142         
     143        # now create a dictionary of WMS/layer from these docs - to avoid duplicate layers 
     144        c.wms = {} 
     145        for wmcDoc in wmcDocs: 
     146            for layer in wmcDoc.layers: 
     147                if layer.wmsURL not in c.wms: 
     148                    c.wms[layer.wmsURL] = [layer.name] 
     149                else: 
     150                    if layer.name not in c.wms[layer.wmsURL]: 
     151                        c.wms[layer.wmsURL].append(layer.name) 
     152         
     153        # now create the form to post this data 
     154        logging.info('Creating form doc with data to post to Con Terra mapClient') 
     155        c.redirectToConterra = True; 
     156        response.write(render('selectedItems')) 
    136157     
    137158    def addSelectedItem(self,entryid,divid): 
     
    148169        title = request.params['title'] 
    149170 
    150         log.debug('addSelectedItem(%s, %s, %s, %s, %s)' % ( 
     171        logging.debug('addSelectedItem(%s, %s, %s, %s, %s)' % ( 
    151172            `entryid`, `kmlurl`, `wmcurl`, `title`, `divid`)) 
    152173 
    153         item = selectedItem.SelectedItem(entryid, title, kmlurl, wmcurl) 
    154  
    155         log.debug('SelectedItem: kmlList = %s, wmcList = %s' % ( 
     174        item = SelectedItem(entryid, title, kmlurl, wmcurl) 
     175 
     176        logging.debug('SelectedItem: kmlList = %s, wmcList = %s' % ( 
    156177            `item.kmlList`, `item.wmcList`)) 
    157178         
  • MILK/trunk/milk_server/milk_server/lib/base.py

    r4477 r4482  
    3838 
    3939import logging 
    40 logger = logging.getLogger(__name__) 
    4140 
    4241# Configure 
     
    5049        # the action or route vars here 
    5150 
    52         logger.debug("BaseController.__call__ ...")         
     51        logging.debug("BaseController.__call__ ...")         
    5352                 
    5453        #organise the information needed by pagetabs ...  
     
    6261        if 'lastViewed' in session: c.pageTabs.append(('Details',session['lastViewed'])) 
    6362 
    64         if g.atomEditorEnabled: c.pageTabs.append(('Atom Editor', session.get('currentEditor') or \ 
     63        if g.atomEditorEnabled: c.pageTabs.append(('Editor', session.get('currentEditor') or \ 
    6564                                                       h.url_for(controller='atom_editor/listatom',action='atomHome'))) 
    6665         
  • MILK/trunk/milk_server/milk_server/public/js/ndgJavascript.js

    r4474 r4482  
    2727  vis.display = (vis.display==''||vis.display=='block')?'none':'block'; 
    2828} 
     29 
     30 
     31/** 
     32 * Set all checkboxes in a table to be the same state as the checkbox passed in 
     33 * @param abox: the 'select all' checkbox - NB, the other checkboxes will be set to the same 
     34 *                              state as this one 
     35 * @param tableID: the ID of the parent table containing the checkboxes 
     36 */  
     37function selectAll(abox, tableID)  
     38{ 
     39        var table = document.getElementById(tableID); 
     40    var cboxes = table.getElementsByTagName('input'); 
     41        var l = cboxes.length; 
     42 
     43        for (var i = 0; i < l; i++) 
     44        { 
     45        var n = cboxes[i]; 
     46        if ('checkbox' == n.type && n != abox) 
     47        { 
     48                        n.checked = abox.checked; 
     49        } 
     50    } 
     51} 
    2952  
  • MILK/trunk/milk_server/milk_server/public/js/prototype.js

    r3538 r4482  
    1 /*  Prototype JavaScript framework, version 1.5.1_rc1 
    2  *  (c) 2005-2007 Sam Stephenson 
     1/*  Prototype JavaScript framework, version 1.6.0.3 
     2 *  (c) 2005-2008 Sam Stephenson 
    33 * 
    44 *  Prototype is freely distributable under the terms of an MIT-style license. 
    55 *  For details, see the Prototype web site: http://www.prototypejs.org/ 
    66 * 
    7 /*--------------------------------------------------------------------------*/ 
     7 *--------------------------------------------------------------------------*/ 
    88 
    99var Prototype = { 
    10   Version: '1.5.1_rc1', 
     10  Version: '1.6.0.3', 
    1111 
    1212  Browser: { 
    13     IE:     !!(window.attachEvent && !window.opera), 
    14     Opera:  !!window.opera, 
     13    IE:     !!(window.attachEvent && 
     14      navigator.userAgent.indexOf('Opera') === -1), 
     15    Opera:  navigator.userAgent.indexOf('Opera') > -1, 
    1516    WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1, 
    16     Gecko:  navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1 
    17   }, 
     17    Gecko:  navigator.userAgent.indexOf('Gecko') > -1 && 
     18      navigator.userAgent.indexOf('KHTML') === -1, 
     19    MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/) 
     20  }, 
     21 
    1822  BrowserFeatures: { 
    1923    XPath: !!document.evaluate, 
     24    SelectorsAPI: !!document.querySelector, 
    2025    ElementExtensions: !!window.HTMLElement, 
    2126    SpecificElementExtensions: 
    22       (document.createElement('div').__proto__ !== 
    23        document.createElement('form').__proto__) 
    24   }, 
    25  
    26   ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)', 
    27   emptyFunction: function() {}, 
     27      document.createElement('div')['__proto__'] && 
     28      document.createElement('div')['__proto__'] !== 
     29        document.createElement('form')['__proto__'] 
     30  }, 
     31 
     32  ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>', 
     33  JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/, 
     34 
     35  emptyFunction: function() { }, 
    2836  K: function(x) { return x } 
    29 } 
    30  
     37}; 
     38 
     39if (Prototype.Browser.MobileSafari) 
     40  Prototype.BrowserFeatures.SpecificElementExtensions = false; 
     41 
     42 
     43/* Based on Alex Arnell's inheritance implementation. */ 
    3144var Class = { 
    3245  create: function() { 
    33     return function() { 
     46    var parent = null, properties = $A(arguments); 
     47    if (Object.isFunction(properties[0])) 
     48      parent = properties.shift(); 
     49 
     50    function klass() { 
    3451      this.initialize.apply(this, arguments); 
    3552    } 
    36   } 
    37 } 
    38  
    39 var Abstract = new Object(); 
     53 
     54    Object.extend(klass, Class.Methods); 
     55    klass.superclass = parent; 
     56    klass.subclasses = []; 
     57 
     58    if (parent) { 
     59      var subclass = function() { }; 
     60      subclass.prototype = parent.prototype; 
     61      klass.prototype = new subclass; 
     62      parent.subclasses.push(klass); 
     63    } 
     64 
     65    for (var i = 0; i < properties.length; i++) 
     66      klass.addMethods(properties[i]); 
     67 
     68    if (!klass.prototype.initialize) 
     69      klass.prototype.initialize = Prototype.emptyFunction; 
     70 
     71    klass.prototype.constructor = klass; 
     72 
     73    return klass; 
     74  } 
     75}; 
     76 
     77Class.Methods = { 
     78  addMethods: function(source) { 
     79    var ancestor   = this.superclass && this.superclass.prototype; 
     80    var properties = Object.keys(source); 
     81 
     82    if (!Object.keys({ toString: true }).length) 
     83      properties.push("toString", "valueOf"); 
     84 
     85    for (var i = 0, length = properties.length; i < length; i++) { 
     86      var property = properties[i], value = source[property]; 
     87      if (ancestor && Object.isFunction(value) && 
     88          value.argumentNames().first() == "$super") { 
     89        var method = value; 
     90        value = (function(m) { 
     91          return function() { return ancestor[m].apply(this, arguments) }; 
     92        })(property).wrap(method); 
     93 
     94        value.valueOf = method.valueOf.bind(method); 
     95        value.toString = method.toString.bind(method); 
     96      } 
     97      this.prototype[property] = value; 
     98    } 
     99 
     100    return this; 
     101  } 
     102}; 
     103 
     104var Abstract = { }; 
    40105 
    41106Object.extend = function(destination, source) { 
    42   for (var property in source) { 
     107  for (var property in source) 
    43108    destination[property] = source[property]; 
    44   } 
    45109  return destination; 
    46 } 
     110}; 
    47111 
    48112Object.extend(Object, { 
    49113  inspect: function(object) { 
    50114    try { 
    51       if (object === undefined) return 'undefined'; 
     115      if (Object.isUndefined(object)) return 'undefined'; 
    52116      if (object === null) return 'null'; 
    53       return object.inspect ? object.inspect() : object.toString(); 
     117      return object.inspect ? object.inspect() : String(object); 
    54118    } catch (e) { 
    55119      if (e instanceof RangeError) return '...'; 
     
    60124  toJSON: function(object) { 
    61125    var type = typeof object; 
    62     switch(type) { 
     126    switch (type) { 
    63127      case 'undefined': 
    64128      case 'function': 
     
    66130      case 'boolean': return object.toString(); 
    67131    } 
     132 
    68133    if (object === null) return 'null'; 
    69134    if (object.toJSON) return object.toJSON(); 
    70     if (object.ownerDocument === document) return; 
     135    if (Object.isElement(object)) return; 
     136 
    71137    var results = []; 
    72138    for (var property in object) { 
    73139      var value = Object.toJSON(object[property]); 
    74       if (value !== undefined) 
    75         results.push(property.toJSON() + ':' + value); 
    76     } 
    77     return '{' + results.join(',') + '}'; 
     140      if (!Object.isUndefined(value)) 
     141        results.push(property.toJSON() + ': ' + value); 
     142    } 
     143 
     144    return '{' + results.join(', ') + '}'; 
     145  }, 
     146 
     147  toQueryString: function(object) { 
     148    return $H(object).toQueryString(); 
     149  }, 
     150 
     151  toHTML: function(object) { 
     152    return object && object.toHTML ? object.toHTML() : String.interpret(object); 
    78153  }, 
    79154 
     
    93168 
    94169  clone: function(object) { 
    95     return Object.extend({}, object); 
     170    return Object.extend({ }, object); 
     171  }, 
     172 
     173  isElement: function(object) { 
     174    return !!(object && object.nodeType == 1); 
     175  }, 
     176 
     177  isArray: function(object) { 
     178    return object != null && typeof object == "object" && 
     179      'splice' in object && 'join' in object; 
     180  }, 
     181 
     182  isHash: function(object) { 
     183    return object instanceof Hash; 
     184  }, 
     185 
     186  isFunction: function(object) { 
     187    return typeof object == "function"; 
     188  }, 
     189 
     190  isString: function(object) { 
     191    return typeof object == "string"; 
     192  }, 
     193 
     194  isNumber: function(object) { 
     195    return typeof object == "number"; 
     196  }, 
     197 
     198  isUndefined: function(object) { 
     199    return typeof object == "undefined"; 
    96200  } 
    97201}); 
    98202 
    99 Function.prototype.bind = function() { 
    100   var __method = this, args = $A(arguments), object = args.shift(); 
    101   return function() { 
    102     return __method.apply(object, args.concat($A(arguments))); 
    103   } 
    104 } 
    105  
    106 Function.prototype.bindAsEventListener = function(object) { 
    107   var __method = this, args = $A(arguments), object = args.shift(); 
    108   return function(event) { 
    109     return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments))); 
    110   } 
    111 } 
    112  
    113 Object.extend(Number.prototype, { 
    114   toColorPart: function() { 
    115     return this.toPaddedString(2, 16); 
    116   }, 
    117  
    118   succ: function() { 
    119     return this + 1; 
    120   }, 
    121  
    122   times: function(iterator) { 
    123     $R(0, this, true).each(iterator); 
    124     return this; 
    125   }, 
    126  
    127   toPaddedString: function(length, radix) { 
    128     var string = this.toString(radix || 10); 
    129     return '0'.times(length - string.length) + string; 
    130   }, 
    131  
    132   toJSON: function() { 
    133     return isFinite(this) ? this.toString() : 'null'; 
     203Object.extend(Function.prototype, { 
     204  argumentNames: function() { 
     205    var names = this.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1] 
     206      .replace(/\s+/g, '').split(','); 
     207    return names.length == 1 && !names[0] ? [] : names; 
     208  }, 
     209 
     210  bind: function() { 
     211    if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this; 
     212    var __method = this, args = $A(arguments), object = args.shift(); 
     213    return function() { 
     214      return __method.apply(object, args.concat($A(arguments))); 
     215    } 
     216  }, 
     217 
     218  bindAsEventListener: function() { 
     219    var __method = this, args = $A(arguments), object = args.shift(); 
     220    return function(event) { 
     221      return __method.apply(object, [event || window.event].concat(args)); 
     222    } 
     223  }, 
     224 
     225  curry: function() { 
     226    if (!arguments.length) return this; 
     227    var __method = this, args = $A(arguments); 
     228    return function() { 
     229      return __method.apply(this, args.concat($A(arguments))); 
     230    } 
     231  }, 
     232 
     233  delay: function() { 
     234    var __method = this, args = $A(arguments), timeout = args.shift() * 1000; 
     235    return window.setTimeout(function() { 
     236      return __method.apply(__method, args); 
     237    }, timeout); 
     238  }, 
     239 
     240  defer: function() { 
     241    var args = [0.01].concat($A(arguments)); 
     242    return this.delay.apply(this, args); 
     243  }, 
     244 
     245  wrap: function(wrapper) { 
     246    var __method = this; 
     247    return function() { 
     248      return wrapper.apply(this, [__method.bind(this)].concat($A(arguments))); 
     249    } 
     250  }, 
     251 
     252  methodize: function() { 
     253    if (this._methodized) return this._methodized; 
     254    var __method = this; 
     255    return this._methodized = function() { 
     256      return __method.apply(null, [this].concat($A(arguments))); 
     257    }; 
    134258  } 
    135259}); 
    136260 
    137261Date.prototype.toJSON = function() { 
    138   return '"' + this.getFullYear() + '-' + 
    139     (this.getMonth() + 1).toPaddedString(2) + '-' + 
    140     this.getDate().toPaddedString(2) + 'T' + 
    141     this.getHours().toPaddedString(2) + ':' + 
    142     this.getMinutes().toPaddedString(2) + ':' + 
    143     this.getSeconds().toPaddedString(2) + '"'; 
     262  return '"' + this.getUTCFullYear() + '-' + 
     263    (this.getUTCMonth() + 1).toPaddedString(2) + '-' + 
     264    this.getUTCDate().toPaddedString(2) + 'T' + 
     265    this.getUTCHours().toPaddedString(2) + ':' + 
     266    this.getUTCMinutes().toPaddedString(2) + ':' + 
     267    this.getUTCSeconds().toPaddedString(2) + 'Z"'; 
    144268}; 
    145269 
     
    153277        returnValue = lambda(); 
    154278        break; 
    155       } catch (e) {} 
     279      } catch (e) { } 
    156280    } 
    157281 
    158282    return returnValue; 
    159283  } 
    160 } 
     284}; 
     285 
     286RegExp.prototype.match = RegExp.prototype.test; 
     287 
     288RegExp.escape = function(str) { 
     289  return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'); 
     290}; 
    161291 
    162292/*--------------------------------------------------------------------------*/ 
    163293 
    164 var PeriodicalExecuter = Class.create(); 
    165 PeriodicalExecuter.prototype = { 
     294var PeriodicalExecuter = Class.create({ 
    166295  initialize: function(callback, frequency) { 
    167296    this.callback = callback; 
     
    176305  }, 
    177306 
     307  execute: function() { 
     308    this.callback(this); 
     309  }, 
     310 
    178311  stop: function() { 
    179312    if (!this.timer) return; 
     
    186319      try { 
    187320        this.currentlyExecuting = true; 
    188         this.callback(this); 
     321        this.execute(); 
    189322      } finally { 
    190323        this.currentlyExecuting = false; 
     
    192325    } 
    193326  } 
    194 } 
     327}); 
    195328Object.extend(String, { 
    196329  interpret: function(value) { 
     
    226359  sub: function(pattern, replacement, count) { 
    227360    replacement = this.gsub.prepareReplacement(replacement); 
    228     count = count === undefined ? 1 : count; 
     361    count = Object.isUndefined(count) ? 1 : count; 
    229362 
    230363    return this.gsub(pattern, function(match) { 
     
    236369  scan: function(pattern, iterator) { 
    237370    this.gsub(pattern, iterator); 
    238     return this; 
     371    return String(this); 
    239372  }, 
    240373 
    241374  truncate: function(length, truncation) { 
    242375    length = length || 30; 
    243     truncation = truncation === undefined ? '...' : truncation; 
     376    truncation = Object.isUndefined(truncation) ? '...' : truncation; 
    244377    return this.length > length ? 
    245       this.slice(0, length - truncation.length) + truncation : this; 
     378      this.slice(0, length - truncation.length) + truncation : String(this); 
    246379  }, 
    247380 
     
    277410 
    278411  unescapeHTML: function() { 
    279     var div = document.createElement('div'); 
     412    var div = new Element('div'); 
    280413    div.innerHTML = this.stripTags(); 
    281414    return div.childNodes[0] ? (div.childNodes.length > 1 ? 
     
    286419  toQueryParams: function(separator) { 
    287420    var match = this.strip().match(/([^?#]*)(#.*)?$/); 
    288     if (!match) return {}; 
    289  
    290     return match[1].split(separator || '&').inject({}, function(hash, pair) { 
     421    if (!match) return { }; 
     422 
     423    return match[1].split(separator || '&').inject({ }, function(hash, pair) { 
    291424      if ((pair = pair.split('='))[0]) { 
    292         var name = decodeURIComponent(pair[0]); 
    293         var value = pair[1] ? decodeURIComponent(pair[1]) : undefined; 
    294  
    295         if (hash[name] !== undefined) { 
    296           if (hash[name].constructor != Array) 
    297             hash[name] = [hash[name]]; 
    298           if (value) hash[name].push(value); 
     425        var key = decodeURIComponent(pair.shift()); 
     426        var value = pair.length > 1 ? pair.join('=') : pair[0]; 
     427        if (value != undefined) value = decodeURIComponent(value); 
     428 
     429        if (key in hash) { 
     430          if (!Object.isArray(hash[key])) hash[key] = [hash[key]]; 
     431          hash[key].push(value); 
    299432        } 
    300         else hash[name] = value; 
     433        else hash[key] = value; 
    301434      } 
    302435      return hash; 
     
    314447 
    315448  times: function(count) { 
    316     var result = ''; 
    317     for (var i = 0; i < count; i++) result += this; 
    318     return result; 
     449    return count < 1 ? '' : new Array(count + 1).join(this); 
    319450  }, 
    320451 
     
    358489  }, 
    359490 
     491  unfilterJSON: function(filter) { 
     492    return this.sub(filter || Prototype.JSONFilter, '#{1}'); 
     493  }, 
     494 
     495  isJSON: function() { 
     496    var str = this; 
     497    if (str.blank()) return false; 
     498    str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''); 
     499    return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str); 
     500  }, 
     501 
    360502  evalJSON: function(sanitize) { 
     503    var json = this.unfilterJSON(); 
    361504    try { 
    362       if (!sanitize || (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(this))) 
    363         return eval('(' + this + ')'); 
    364     } catch (e) {} 
    365     throw new SyntaxError('Badly formated JSON string: ' + this.inspect()); 
     505      if (!sanitize || json.isJSON()) return eval('(' + json + ')'); 
     506    } catch (e) { } 
     507    throw new SyntaxError('Badly formed JSON string: ' + this.inspect()); 
    366508  }, 
    367509 
     
    371513 
    372514  startsWith: function(pattern) { 
    373     return this.indexOf(pattern) == 0; 
     515    return this.indexOf(pattern) === 0; 
    374516  }, 
    375517 
    376518  endsWith: function(pattern) { 
    377     return this.lastIndexOf(pattern) == (this.length - pattern.length); 
     519    var d = this.length - pattern.length; 
     520    return d >= 0 && this.lastIndexOf(pattern) === d; 
    378521  }, 
    379522 
     
    384527  blank: function() { 
    385528    return /^\s*$/.test(this); 
     529  }, 
     530 
     531  interpolate: function(object, pattern) { 
     532    return new Template(this, pattern).evaluate(object); 
    386533  } 
    387534}); 
    388535 
     536if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, { 
     537  escapeHTML: function() { 
     538    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); 
     539  }, 
     540  unescapeHTML: function() { 
     541    return this.stripTags().replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>'); 
     542  } 
     543}); 
     544 
    389545String.prototype.gsub.prepareReplacement = function(replacement) { 
    390   if (typeof replacement == 'function') return replacement; 
     546  if (Object.isFunction(replacement)) return replacement; 
    391547  var template = new Template(replacement); 
    392548  return function(match) { return template.evaluate(match) }; 
    393 } 
     549}; 
    394550 
    395551String.prototype.parseQuery = String.prototype.toQueryParams; 
     
    400556}); 
    401557 
    402 with (String.prototype.escapeHTML) div.appendChild(text); 
    403  
    404 var Template = Class.create(); 
    405 Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; 
    406 Template.prototype = { 
     558String.prototype.escapeHTML.div.appendChild(String.prototype.escapeHTML.text); 
     559 
     560var Template = Class.create({ 
    407561  initialize: function(template, pattern) { 
    408562    this.template = template.toString(); 
    409     this.pattern  = pattern || Template.Pattern; 
     563    this.pattern = pattern || Template.Pattern; 
    410564  }, 
    411565 
    412566  evaluate: function(object) { 
     567    if (Object.isFunction(object.toTemplateReplacements)) 
     568      object = object.toTemplateReplacements(); 
     569 
    413570    return this.template.gsub(this.pattern, function(match) { 
    414       var before = match[1]; 
     571      if (object == null) return ''; 
     572 
     573      var before = match[1] || ''; 
    415574      if (before == '\\') return match[2]; 
    416       return before + String.interpret(object[match[3]]); 
     575 
     576      var ctx = object, expr = match[3]; 
     577      var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/; 
     578      match = pattern.exec(expr); 
     579      if (match == null) return before; 
     580 
     581      while (match != null) { 
     582        var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1]; 
     583        ctx = ctx[comp]; 
     584        if (null == ctx || '' == match[3]) break; 
     585        expr = expr.substring('[' == match[3] ? match[1].length : match[0].length); 
     586        match = pattern.exec(expr); 
     587      } 
     588 
     589      return before + String.interpret(ctx); 
    417590    }); 
    418591  } 
    419 } 
    420  
    421 var $break    = new Object(); 
    422 var $continue = new Object(); 
     592}); 
     593Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; 
     594 
     595var $break = { }; 
    423596 
    424597var Enumerable = { 
    425   each: function(iterator) { 
     598  each: function(iterator, context) { 
    426599    var index = 0; 
    427600    try { 
    428601      this._each(function(value) { 
    429         iterator(value, index++); 
     602        iterator.call(context, value, index++); 
    430603      }); 
    431604    } catch (e) { 
     
    435608  }, 
    436609 
    437   eachSlice: function(number, iterator) { 
     610  eachSlice: function(number, iterator, context) { 
    438611    var index = -number, slices = [], array = this.toArray(); 
     612    if (number < 1) return array; 
    439613    while ((index += number) < array.length) 
    440614      slices.push(array.slice(index, index+number)); 
    441     return slices.map(iterator); 
    442   }, 
    443  
    444   all: function(iterator) { 
     615    return slices.collect(iterator, context); 
     616  }, 
     617 
     618  all: function(iterator, context) { 
     619    iterator = iterator || Prototype.K; 
    445620    var result = true; 
    446621    this.each(function(value, index) { 
    447       result = result && !!(iterator || Prototype.K)(value, index); 
     622      result = result && !!iterator.call(context, value, index); 
    448623      if (!result) throw $break; 
    449624    }); 
     
    451626  }, 
    452627 
    453   any: function(iterator) { 
     628  any: function(iterator, context) { 
     629    iterator = iterator || Prototype.K; 
    454630    var result = false; 
    455631    this.each(function(value, index) { 
    456       if (result = !!(iterator || Prototype.K)(value, index)) 
     632      if (result = !!iterator.call(context, value, index)) 
    457633        throw $break; 
    458634    }); 
     
    460636  }, 
    461637 
    462   collect: function(iterator) { 
     638  collect: function(iterator, context) { 
     639    iterator = iterator || Prototype.K; 
    463640    var results = []; 
    464641    this.each(function(value, index) { 
    465       results.push((iterator || Prototype.K)(value, index)); 
     642      results.push(iterator.call(context, value, index)); 
    466643    }); 
    467644    return results; 
    468645  }, 
    469646 
    470   detect: function(iterator) { 
     647  detect: function(iterator, context) { 
    471648    var result; 
    472649    this.each(function(value, index) { 
    473       if (iterator(value, index)) { 
     650      if (iterator.call(context, value, index)) { 
    474651        result = value; 
    475652        throw $break; 
     
    479656  }, 
    480657 
    481   findAll: function(iterator) { 
     658  findAll: function(iterator, context) { 
    482659    var results = []; 
    483660    this.each(function(value, index) { 
    484       if (iterator(value, index)) 
     661      if (iterator.call(context, value, index)) 
    485662        results.push(value); 
    486663    }); 
     
    488665  }, 
    489666 
    490   grep: function(pattern, iterator) { 
     667  grep: function(filter, iterator, context) { 
     668    iterator = iterator || Prototype.K; 
    491669    var results = []; 
     670 
     671    if (Object.isString(filter)) 
     672      filter = new RegExp(filter); 
     673 
    492674    this.each(function(value, index) { 
    493       var stringValue = value.toString(); 
    494       if (stringValue.match(pattern)) 
    495         results.push((iterator || Prototype.K)(value, index)); 
    496     }) 
     675      if (filter.match(value)) 
     676        results.push(iterator.call(context, value, index)); 
     677    }); 
    497678    return results; 
    498679  }, 
    499680 
    500681  include: function(object) { 
     682    if (Object.isFunction(this.indexOf)) 
     683      if (this.indexOf(object) != -1) return true; 
     684 
    501685    var found = false; 
    502686    this.each(function(value) { 
     
    510694 
    511695  inGroupsOf: function(number, fillWith) { 
    512     fillWith = fillWith === undefined ? null : fillWith; 
     696    fillWith = Object.isUndefined(fillWith) ? null : fillWith; 
    513697    return this.eachSlice(number, function(slice) { 
    514698      while(slice.length < number) slice.push(fillWith); 
     
    517701  }, 
    518702 
    519   inject: function(memo, iterator) { 
     703  inject: function(memo, iterator, context) { 
    520704    this.each(function(value, index) { 
    521       memo = iterator(memo, value, index); 
     705      memo = iterator.call(context, memo, value, index); 
    522706    }); 
    523707    return memo; 
     
    531715  }, 
    532716 
    533   max: function(iterator) { 
     717  max: function(iterator, context) { 
     718    iterator = iterator || Prototype.K; 
    534719    var result; 
    535720    this.each(function(value, index) { 
    536       value = (iterator || Prototype.K)(value, index); 
    537       if (result == undefined || value >= result) 
     721      value = iterator.call(context, value, index); 
     722      if (result == null || value >= result) 
    538723        result = value; 
    539724    }); 
     
    541726  }, 
    542727 
    543   min: function(iterator) { 
     728  min: function(iterator, context) { 
     729    iterator = iterator || Prototype.K; 
    544730    var result; 
    545731    this.each(function(value, index) { 
    546       value = (iterator || Prototype.K)(value, index); 
    547       if (result == undefined || value < result) 
     732      value = iterator.call(context, value, index); 
     733      if (result == null || value < result) 
    548734        result = value; 
    549735    }); 
     
    551737  }, 
    552738 
    553   partition: function(iterator) { 
     739  partition: function(iterator, context) { 
     740    iterator = iterator || Prototype.K; 
    554741    var trues = [], falses = []; 
    555742    this.each(function(value, index) { 
    556       ((iterator || Prototype.K)(value, index) ? 
     743      (iterator.call(context, value, index) ? 
    557744        trues : falses).push(value); 
    558745    }); 
     
    562749  pluck: function(property) { 
    563750    var results = []; 
    564     this.each(function(value, index) { 
     751    this.each(function(value) { 
    565752      results.push(value[property]); 
    566753    }); 
     
    568755  }, 
    569756 
    570   reject: function(iterator) { 
     757  reject: function(iterator, context) { 
    571758    var results = []; 
    572759    this.each(function(value, index) { 
    573       if (!iterator(value, index)) 
     760      if (!iterator.call(context, value, index)) 
    574761        results.push(value); 
    575762    }); 
     
    577764  }, 
    578765 
    579   sortBy: function(iterator) { 
     766  sortBy: function(iterator, context) { 
    580767    return this.map(function(value, index) { 
    581       return {value: value, criteria: iterator(value, index)}; 
     768      return { 
     769        value: value, 
     770        criteria: iterator.call(context, value, index) 
     771      }; 
    582772    }).sort(function(left, right) { 
    583773      var a = left.criteria, b = right.criteria; 
     
    592782  zip: function() { 
    593783    var iterator = Prototype.K, args = $A(arguments); 
    594     if (typeof args.last() == 'function') 
     784    if (Object.isFunction(args.last())) 
    595785      iterator = args.pop(); 
    596786 
     
    608798    return '#<Enumerable:' + this.toArray().inspect() + '>'; 
    609799  } 
    610 } 
     800}; 
    611801 
    612802Object.extend(Enumerable, { 
     
    614804  find:    Enumerable.detect, 
    615805  select:  Enumerable.findAll, 
     806  filter:  Enumerable.findAll, 
    616807  member:  Enumerable.include, 
    617   entries: Enumerable.toArray 
     808  entries: Enumerable.toArray, 
     809  every:   Enumerable.all, 
     810  some:    Enumerable.any 
    618811}); 
    619 var $A = Array.from = function(iterable) { 
     812function $A(iterable) { 
    620813  if (!iterable) return []; 
    621   if (iterable.toArray) { 
    622     return iterable.toArray(); 
    623   } else { 
    624     var results = []; 
    625     for (var i = 0, length = iterable.length; i < length; i++) 
    626       results.push(iterable[i]); 
     814  if (iterable.toArray) return iterable.toArray(); 
     815  var length = iterable.length || 0, results = new Array(length); 
     816  while (length--) results[length] = iterable[length]; 
     817  return results; 
     818} 
     819 
     820if (Prototype.Browser.WebKit) { 
     821  $A = function(iterable) { 
     822    if (!iterable) return []; 
     823    // In Safari, only use the `toArray` method if it's not a NodeList. 
     824    // A NodeList is a function, has an function `item` property, and a numeric 
     825    // `length` property. Adapted from Google Doctype. 
     826    if (!(typeof iterable === 'function' && typeof iterable.length === 
     827        'number' && typeof iterable.item === 'function') && iterable.toArray) 
     828      return iterable.toArray(); 
     829    var length = iterable.length || 0, results = new Array(length); 
     830    while (length--) results[length] = iterable[length]; 
    627831    return results; 
    628   } 
     832  }; 
    629833} 
    630834 
     835Array.from = $A; 
     836 
    631837Object.extend(Array.prototype, Enumerable); 
    632838 
    633 if (!Array.prototype._reverse) 
    634   Array.prototype._reverse = Array.prototype.reverse; 
     839if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse; 
    635840 
    636841Object.extend(Array.prototype, { 
     
    661866  flatten: function() { 
    662867    return this.inject([], function(array, value) { 
    663       return array.concat(value && value.constructor == Array ? 
     868      return array.concat(Object.isArray(value) ? 
    664869        value.flatten() : [value]); 
    665870    }); 
     
    671876      return !values.include(value); 
    672877    }); 
    673   }, 
    674  
    675   indexOf: function(object) { 
    676     for (var i = 0, length = this.length; i < length; i++) 
    677       if (this[i] == object) return i; 
    678     return -1; 
    679878  }, 
    680879 
     
    695894  }, 
    696895 
     896  intersect: function(array) { 
     897    return this.uniq().findAll(function(item) { 
     898      return array.detect(function(value) { return item === value }); 
     899    }); 
     900  }, 
     901 
    697902  clone: function() { 
    698903    return [].concat(this); 
     
    711916    this.each(function(object) { 
    712917      var value = Object.toJSON(object); 
    713       if (value !== undefined) results.push(value); 
     918      if (!Object.isUndefined(value)) results.push(value); 
    714919    }); 
    715     return '[' + results.join(',') + ']'; 
     920    return '[' + results.join(', ') + ']'; 
    716921  } 
    717922}); 
    718923 
     924// use native browser JS 1.6 implementation if available 
     925if (Object.isFunction(Array.prototype.forEach)) 
     926  Array.prototype._each = Array.prototype.forEach; 
     927 
     928if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) { 
     929  i || (i = 0); 
     930  var length = this.length; 
     931  if (i < 0) i = length + i; 
     932  for (; i < length; i++) 
     933    if (this[i] === item) return i; 
     934  return -1; 
     935}; 
     936 
     937if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) { 
     938  i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1; 
     939  var n = this.slice(0, i).reverse().indexOf(item); 
     940  return (n < 0) ? n : i - n - 1; 
     941}; 
     942 
    719943Array.prototype.toArray = Array.prototype.clone; 
    720944 
    721945function $w(string) { 
     946  if (!Object.isString(string)) return []; 
    722947  string = string.strip(); 
    723948  return string ? string.split(/\s+/) : []; 
     
    729954    for (var i = 0, length = this.length; i < length; i++) array.push(this[i]); 
    730955    for (var i = 0, length = arguments.length; i < length; i++) { 
    731       if (arguments[i].constructor == Array) { 
     956      if (Object.isArray(arguments[i])) { 
    732957        for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++) 
    733958          array.push(arguments[i][j]); 
     
    737962    } 
    738963    return array; 
    739   } 
     964  }; 
    740965} 
    741 var Hash = function(object) { 
    742   if (object instanceof Hash) this.merge(object); 
    743   else Object.extend(this, object || {}); 
    744 }; 
    745  
    746 Object.extend(Hash, { 
    747   toQueryString: function(obj) { 
    748     var parts = []; 
    749     parts.add = arguments.callee.addPair; 
    750  
    751     this.prototype._each.call(obj, function(pair) { 
    752       if (!pair.key) return; 
    753       var value = pair.value; 
    754  
    755       if (value && typeof value == 'object') { 
    756         if (value.constructor == Array) value.each(function(value) { 
    757           parts.add(pair.key, value); 
    758         }); 
    759         return; 
    760       } 
    761       parts.add(pair.key, value); 
    762     }); 
    763  
    764     return parts.join('&'); 
    765   }, 
    766  
    767   toJSON: function(object) { 
    768     var results = []; 
    769     this.prototype._each.call(object, function(pair) { 
    770       var value = Object.toJSON(pair.value); 
    771       if (value !== undefined) results.push(pair.key.toJSON() + ':' + value); 
    772     }); 
    773     return '{' + results.join(',') + '}'; 
     966Object.extend(Number.prototype, { 
     967  toColorPart: function() { 
     968    return this.toPaddedString(2, 16); 
     969  }, 
     970 
     971  succ: function() { 
     972    return this + 1; 
     973  }, 
     974 
     975  times: function(iterator, context) { 
     976    $R(0, this, true).each(iterator, context); 
     977    return this; 
     978  }, 
     979 
     980  toPaddedString: function(length, radix) { 
     981    var string = this.toString(radix || 10); 
     982    return '0'.times(length - string.length) + string; 
     983  }, 
     984 
     985  toJSON: function() { 
     986    return isFinite(this) ? this.toString() : 'null'; 
    774987  } 
    775988}); 
    776989 
    777 Hash.toQueryString.addPair = function(key, value, prefix) { 
    778   if (value == null) return; 
    779   key = encodeURIComponent(key); 
    780   this.push(key + '=' + (value == null ? '' : encodeURIComponent(value))); 
    781 } 
    782  
    783 Object.extend(Hash.prototype, Enumerable); 
    784 Object.extend(Hash.prototype, { 
    785   _each: function(iterator) { 
    786     for (var key in this) { 
    787       var value = this[key]; 
    788       if (value && value == Hash.prototype[key]) continue; 
    789  
    790       var pair = [key, value]; 
    791       pair.key = key; 
    792       pair.value = value; 
    793       iterator(pair); 
    794     } 
    795   }, 
    796  
    797   keys: function() { 
    798     return this.pluck('key'); 
    799   }, 
    800  
    801   values: function() { 
    802     return this.pluck('value'); 
    803   }, 
    804  
    805   merge: function(hash) { 
    806     return $H(hash).inject(this, function(mergedHash, pair) { 
    807       mergedHash[pair.key] = pair.value; 
    808       return mergedHash; 
    809     }); 
    810   }, 
    811  
    812   remove: function() { 
    813     var result; 
    814     for(var i = 0, length = arguments.length; i < length; i++) { 
    815       var value = this[arguments[i]]; 
    816       if (value !== undefined){ 
    817         if (result === undefined) result = value; 
    818         else { 
    819           if (result.constructor != Array) result = [result]; 
    820           result.push(value) 
    821         } 
    822       } 
    823       delete this[arguments[i]]; 
    824     } 
    825     return result; 
    826   }, 
    827  
    828   toQueryString: function() { 
    829     return Hash.toQueryString(this); 
    830   }, 
    831  
    832   inspect: function() { 
    833     return '#<Hash:{' + this.map(function(pair) { 
    834       return pair.map(Object.inspect).join(': '); 
    835     }).join(', ') + '}>'; 
    836   }, 
    837  
    838   toJSON: function() { 
    839     return Hash.toJSON(this); 
    840   } 
     990$w('abs round ceil floor').each(function(method){ 
     991  Number.prototype[method] = Math[method].methodize(); 
    841992}); 
    842  
    843993function $H(object) { 
    844   if (object instanceof Hash) return object; 
    845994  return new Hash(object); 
    846995}; 
    847996 
    848 // Safari iterates over shadowed properties 
    849 if (function() { 
    850   var i = 0, Test = function(value) { this.key = value }; 
    851   Test.prototype.key = 'foo'; 
    852   for (var property in new Test('bar')) i++; 
    853   return i > 1; 
    854 }()) Hash.prototype._each = function(iterator) { 
    855   var cache = []; 
    856   for (var key in this) { 
    857     var value = this[key]; 
    858     if ((value && value == Hash.prototype[key]) || cache.include(key)) continue; 
    859     cache.push(key); 
    860     var pair = [key, value]; 
    861     pair.key = key; 
    862     pair.value = value; 
    863     iterator(pair); 
    864   } 
    865 }; 
    866 ObjectRange = Class.create(); 
    867 Object.extend(ObjectRange.prototype, Enumerable); 
    868 Object.extend(ObjectRange.prototype, { 
     997var Hash = Class.create(Enumerable, (function() { 
     998 
     999  function toQueryPair(key, value) { 
     1000    if (Object.isUndefined(value)) return key; 
     1001    return key + '=' + encodeURIComponent(String.interpret(value)); 
     1002  } 
     1003 
     1004  return { 
     1005    initialize: function(object) { 
     1006      this._object = Object.isHash(object) ? object.toObject() : Object.clone(object); 
     1007    }, 
     1008 
     1009    _each: function(iterator) { 
     1010      for (var key in this._object) { 
     1011        var value = this._object[key], pair = [key, value]; 
     1012        pair.key = key; 
     1013        pair.value = value; 
     1014        iterator(pair); 
     1015      } 
     1016    }, 
     1017 
     1018    set: function(key, value) { 
     1019      return this._object[key] = value; 
     1020    }, 
     1021 
     1022    get: function(key) { 
     1023      // simulating poorly supported hasOwnProperty 
     1024      if (this._object[key] !== Object.prototype[key]) 
     1025        return this._object[key]; 
     1026    }, 
     1027 
     1028    unset: function(key) { 
     1029      var value = this._object[key]; 
     1030      delete this._object[key]; 
     1031      return value; 
     1032    }, 
     1033 
     1034    toObject: function() { 
     1035      return Object.clone(this._object); 
     1036    }, 
     1037 
     1038    keys: function() { 
     1039      return this.pluck('key'); 
     1040    }, 
     1041 
     1042    values: function() { 
     1043      return this.pluck('value'); 
     1044    }, 
     1045 
     1046    index: function(value) { 
     1047      var match = this.detect(function(pair) { 
     1048        return pair.value === value; 
     1049      }); 
     1050      return match && match.key; 
     1051    }, 
     1052 
     1053    merge: function(object) { 
     1054      return this.clone().update(object); 
     1055    }, 
     1056 
     1057    update: function(object) { 
     1058      return new Hash(object).inject(this, function(result, pair) { 
     1059        result.set(pair.key, pair.value); 
     1060        return result; 
     1061      }); 
     1062    }, 
     1063 
     1064    toQueryString: function() { 
     1065      return this.inject([], function(results, pair) { 
     1066        var key = encodeURIComponent(pair.key), values = pair.value; 
     1067 
     1068        if (values && typeof values == 'object') { 
     1069          if (Object.isArray(values)) 
     1070            return results.concat(values.map(toQueryPair.curry(key))); 
     1071        } else results.push(toQueryPair(key, values)); 
     1072        return results; 
     1073      }).join('&'); 
     1074    }, 
     1075 
     1076    inspect: function() { 
     1077      return '#<Hash:{' + this.map(function(pair) { 
     1078        return pair.map(Object.inspect).join(': '); 
     1079      }).join(', ') + '}>'; 
     1080    }, 
     1081 
     1082    toJSON: function() { 
     1083      return Object.toJSON(this.toObject()); 
     1084    }, 
     1085 
     1086    clone: function() { 
     1087      return new Hash(this); 
     1088    } 
     1089  } 
     1090})()); 
     1091 
     1092Hash.prototype.toTemplateReplacements = Hash.prototype.toObject; 
     1093Hash.from = $H; 
     1094var ObjectRange = Class.create(Enumerable, { 
    8691095  initialize: function(start, end, exclusive) { 
    8701096    this.start = start; 
     
    8921118var $R = function(start, end, exclusive) { 
    8931119  return new ObjectRange(start, end, exclusive); 
    894 } 
     1120}; 
    8951121 
    8961122var Ajax = { 
     
    9041130 
    9051131  activeRequestCount: 0 
    906 } 
     1132}; 
    9071133 
    9081134Ajax.Responders = { 
     
    9241150  dispatch: function(callback, request, transport, json) { 
    9251151    this.each(function(responder) { 
    926       if (typeof responder[callback] == 'function') { 
     1152      if (Object.isFunction(responder[callback])) { 
    9271153        try { 
    9281154          responder[callback].apply(responder, [request, transport, json]); 
    929         } catch (e) {} 
     1155        } catch (e) { } 
    9301156      } 
    9311157    }); 
     
    9361162 
    9371163Ajax.Responders.register({ 
    938   onCreate: function() { 
    939     Ajax.activeRequestCount++; 
    940   }, 
    941   onComplete: function() { 
    942     Ajax.activeRequestCount--; 
    943   } 
     1164  onCreate:   function() { Ajax.activeRequestCount++ }, 
     1165  onComplete: function() { Ajax.activeRequestCount-- } 
    9441166}); 
    9451167 
    946 Ajax.Base = function() {}; 
    947 Ajax.Base.prototype = { 
    948   setOptions: function(options) { 
     1168Ajax.Base = Class.create({ 
     1169  initialize: function(options) { 
    9491170    this.options = { 
    9501171      method:       'post', 
     
    9521173      contentType:  'application/x-www-form-urlencoded', 
    9531174      encoding:     'UTF-8', 
    954       parameters:   '' 
    955     } 
    956     Object.extend(this.options, options || {}); 
     1175      parameters:   '', 
     1176      evalJSON:     true, 
     1177      evalJS:       true 
     1178    }; 
     1179    Object.extend(this.options, options || { }); 
    9571180 
    9581181    this.options.method = this.options.method.toLowerCase(); 
    959     if (typeof this.options.parameters == 'string') 
     1182 
     1183    if (Object.isString(this.options.parameters)) 
    9601184      this.options.parameters = this.options.parameters.toQueryParams(); 
    961   } 
    962 } 
    963  
    964 Ajax.Request = Class.create(); 
    965 Ajax.Request.Events = 
    966   ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; 
    967  
    968 Ajax.Request.prototype = Object.extend(new Ajax.Base(), { 
     1185    else if (Object.isHash(this.options.parameters)) 
     1186      this.options.parameters = this.options.parameters.toObject(); 
     1187  } 
     1188}); 
     1189 
     1190Ajax.Request = Class.create(Ajax.Base, { 
    9691191  _complete: false, 
    9701192 
    971   initialize: function(url, options) { 
     1193  initialize: function($super, url, options) { 
     1194    $super(options); 
    9721195    this.transport = Ajax.getTransport(); 
    973     this.setOptions(options); 
    9741196    this.request(url); 
    9751197  }, 
     
    9881210    this.parameters = params; 
    9891211 
    990     if (params = Hash.toQueryString(params)) { 
     1212    if (params = Object.toQueryString(params)) { 
    9911213      // when GET, append parameters to URL 
    9921214      if (this.method == 'get') 
     
    9971219 
    9981220    try { 
    999       Ajax.Responders.dispatch('onCreate', this, this.transport); 
     1221      var response = new Ajax.Response(this); 
     1222      if (this.options.onCreate) this.options.onCreate(response); 
     1223      Ajax.Responders.dispatch('onCreate', this, response); 
    10001224 
    10011225      this.transport.open(this.method.toUpperCase(), this.url, 
    10021226        this.options.asynchronous); 
    10031227 
    1004       if (this.options.asynchronous) 
    1005         setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10); 
     1228      if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1); 
    10061229 
    10071230      this.transport.onreadystatechange = this.onStateChange.bind(this); 
     
    10511274      var extras = this.options.requestHeaders; 
    10521275 
    1053       if (typeof extras.push == 'function') 
     1276      if (Object.isFunction(extras.push)) 
    10541277        for (var i = 0, length = extras.length; i < length; i += 2) 
    10551278          headers[extras[i]] = extras[i+1]; 
     
    10631286 
    10641287  success: function() { 
    1065     return !this.transport.status 
    1066         || (this.transport.status >= 200 && this.transport.status < 300); 
     1288    var status = this.getStatus(); 
     1289    return !status || (status >= 200 && status < 300); 
     1290  }, 
     1291 
     1292  getStatus: function() { 
     1293    try { 
     1294      return this.transport.status || 0; 
     1295    } catch (e) { return 0 } 
    10671296  }, 
    10681297 
    10691298  respondToReadyState: function(readyState) { 
    1070     var state = Ajax.Request.Events[readyState]; 
    1071     var transport = this.transport, json = this.evalJSON(); 
     1299    var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this); 
    10721300 
    10731301    if (state == 'Complete') { 
    10741302      try { 
    10751303        this._complete = true; 
    1076         (this.options['on' + this.transport.status] 
     1304        (this.options['on' + response.status] 
    10771305         || this.options['on' + (this.success() ? 'Success' : 'Failure')] 
    1078          || Prototype.emptyFunction)(transport, json); 
     1306         || Prototype.emptyFunction)(response, response.headerJSON); 
    10791307      } catch (e) { 
    10801308        this.dispatchException(e); 
    10811309      } 
    10821310 
    1083       if ((this.getHeader('Content-type') || 'text/javascript').strip(). 
    1084         match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i)) 
    1085           this.evalResponse(); 
     1311      var contentType = response.getHeader('Content-type'); 
     1312      if (this.options.evalJS == 'force' 
     1313          || (this.options.evalJS && this.isSameOrigin() && contentType 
     1314          && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i))) 
     1315        this.evalResponse(); 
    10861316    } 
    10871317 
    10881318    try { 
    1089       (this.options['on' + state] || Prototype.emptyFunction)(transport, json); 
    1090       Ajax.Responders.dispatch('on' + state, this, transport, json); 
     1319      (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON); 
     1320      Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON); 
    10911321    } catch (e) { 
    10921322      this.dispatchException(e); 
     
    10991329  }, 
    11001330 
     1331  isSameOrigin: function() { 
     1332    var m = this.url.match(/^\s*https?:\/\/[^\/]*/); 
     1333    return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({ 
     1334      protocol: location.protocol, 
     1335      domain: document.domain, 
     1336      port: location.port ? ':' + location.port : '' 
     1337    })); 
     1338  }, 
     1339 
    11011340  getHeader: function(name) { 
    11021341    try { 
    1103       return this.transport.getResponseHeader(name); 
    1104     } catch (e) { return null } 
    1105   }, 
    1106  
    1107   evalJSON: function() { 
    1108     try { 
    1109       var json = this.getHeader('X-JSON'); 
    1110       return json ? eval('(' + json + ')') : null; 
     1342      return this.transport.getResponseHeader(name) || null; 
    11111343    } catch (e) { return null } 
    11121344  }, 
     
    11141346  evalResponse: function() { 
    11151347    try { 
    1116       return eval(this.transport.responseText); 
     1348      return eval((this.transport.responseText || '').unfilterJSON()); 
    11171349    } catch (e) { 
    11181350      this.dispatchException(e); 
     
    11261358}); 
    11271359 
    1128 Ajax.Updater = Class.create(); 
    1129  
    1130 Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), { 
    1131   initialize: function(container, url, options) { 
     1360Ajax.Request.Events = 
     1361  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; 
     1362 
     1363Ajax.Response = Class.create({ 
     1364  initialize: function(request){ 
     1365    this.request = request; 
     1366    var transport  = this.transport  = request.transport, 
     1367        readyState = this.readyState = transport.readyState; 
     1368 
     1369    if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) { 
     1370      this.status       = this.getStatus(); 
     1371      this.statusText   = this.getStatusText(); 
     1372      this.responseText = String.interpret(transport.responseText); 
     1373      this.headerJSON   = this._getHeaderJSON(); 
     1374    } 
     1375 
     1376    if(readyState == 4) { 
     1377      var xml = transport.responseXML; 
     1378      this.responseXML  = Object.isUndefined(xml) ? null : xml; 
     1379      this.responseJSON = this._getResponseJSON(); 
     1380    } 
     1381  }, 
     1382 
     1383  status:      0, 
     1384  statusText: '', 
     1385 
     1386  getStatus: Ajax.Request.prototype.getStatus, 
     1387 
     1388  getStatusText: function() { 
     1389    try { 
     1390      return this.transport.statusText || ''; 
     1391    } catch (e) { return '' } 
     1392  }, 
     1393 
     1394  getHeader: Ajax.Request.prototype.getHeader, 
     1395 
     1396  getAllHeaders: function() { 
     1397    try { 
     1398      return this.getAllResponseHeaders(); 
     1399    } catch (e) { return null } 
     1400  }, 
     1401 
     1402  getResponseHeader: function(name) { 
     1403    return this.transport.getResponseHeader(name); 
     1404  }, 
     1405 
     1406  getAllResponseHeaders: function() { 
     1407    return this.transport.getAllResponseHeaders(); 
     1408  }, 
     1409 
     1410  _getHeaderJSON: function() { 
     1411    var json = this.getHeader('X-JSON'); 
     1412    if (!json) return null; 
     1413    json = decodeURIComponent(escape(json)); 
     1414    try { 
     1415      return json.evalJSON(this.request.options.sanitizeJSON || 
     1416        !this.request.isSameOrigin()); 
     1417    } catch (e) { 
     1418      this.request.dispatchException(e); 
     1419    } 
     1420  }, 
     1421 
     1422  _getResponseJSON: function() { 
     1423    var options = this.request.options; 
     1424    if (!options.evalJSON || (options.evalJSON != 'force' && 
     1425      !(this.getHeader('Content-type') || '').include('application/json')) || 
     1426        this.responseText.blank()) 
     1427          return null; 
     1428    try { 
     1429      return this.responseText.evalJSON(options.sanitizeJSON || 
     1430        !this.request.isSameOrigin()); 
     1431    } catch (e) { 
     1432      this.request.dispatchException(e); 
     1433    } 
     1434  } 
     1435}); 
     1436 
     1437Ajax.Updater = Class.create(Ajax.Request, { 
     1438  initialize: function($super, container, url, options) { 
    11321439    this.container = { 
    11331440      success: (container.success || container), 
    11341441      failure: (container.failure || (container.success ? null : container)) 
    1135     } 
    1136  
    1137     this.transport = Ajax.getTransport(); 
    1138     this.setOptions(options); 
    1139  
    1140     var onComplete = this.options.onComplete || Prototype.emptyFunction; 
    1141     this.options.onComplete = (function(transport, param) { 
    1142       this.updateContent(); 
    1143       onComplete(transport, param); 
     1442    }; 
     1443 
     1444    options = Object.clone(options); 
     1445    var onComplete = options.onComplete; 
     1446    options.onComplete = (function(response, json) { 
     1447      this.updateContent(response.responseText); 
     1448      if (Object.isFunction(onComplete)) onComplete(response, json); 
    11441449    }).bind(this); 
    11451450 
    1146     this.request(url); 
    1147   }, 
    1148  
    1149   updateContent: function() { 
    1150     var receiver = this.container[this.success() ? 'success' : 'failure']; 
    1151     var response = this.transport.responseText; 
    1152  
    1153     if (!this.options.evalScripts) response = response.stripScripts(); 
     1451    $super(url, options); 
     1452  }, 
     1453 
     1454  updateContent: function(responseText) { 
     1455    var receiver = this.container[this.success() ? 'success' : 'failure'], 
     1456        options = this.options; 
     1457 
     1458    if (!options.evalScripts) responseText = responseText.stripScripts(); 
    11541459 
    11551460    if (receiver = $(receiver)) { 
    1156       if (this.options.insertion) 
    1157         new this.options.insertion(receiver, response); 
    1158       else 
    1159         receiver.update(response); 
    1160     } 
    1161  
    1162     if (this.success()) { 
    1163       if (this.onComplete) 
    1164         setTimeout(this.onComplete.bind(this), 10); 
     1461      if (options.insertion) { 
     1462        if (Object.isString(options.insertion)) { 
     1463          var insertion = { }; insertion[options.insertion] = responseText; 
     1464          receiver.insert(insertion); 
     1465        } 
     1466        else options.insertion(receiver, responseText); 
     1467      } 
     1468      else receiver.update(responseText); 
    11651469    } 
    11661470  } 
    11671471}); 
    11681472 
    1169 Ajax.PeriodicalUpdater = Class.create(); 
    1170 Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), { 
    1171   initialize: function(container, url, options) { 
    1172     this.setOptions(options); 
     1473Ajax.PeriodicalUpdater = Class.create(Ajax.Base, { 
     1474  initialize: function($super, container, url, options) { 
     1475    $super(options); 
    11731476    this.onComplete = this.options.onComplete; 
    11741477 
     
    11761479    this.decay = (this.options.decay || 1); 
    11771480 
    1178     this.updater = {}; 
     1481    this.updater = { }; 
    11791482    this.container = container; 
    11801483    this.url = url; 
     
    11941497  }, 
    11951498 
    1196   updateComplete: function(request) { 
     1499  updateComplete: function(response) { 
    11971500    if (this.options.decay) { 
    1198       this.decay = (request.responseText == this.lastText ? 
     1501      this.decay = (response.responseText == this.lastText ? 
    11991502        this.decay * this.options.decay : 1); 
    12001503 
    1201       this.lastText = request.responseText; 
    1202     } 
    1203     this.timer = setTimeout(this.onTimerEvent.bind(this), 
    1204       this.decay * this.frequency * 1000); 
     1504      this.lastText = response.responseText; 
     1505    } 
     1506    this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency); 
    12051507  }, 
    12061508 
     
    12151517    return elements; 
    12161518  } 
    1217   if (typeof element == 'string') 
     1519  if (Object.isString(element)) 
    12181520    element = document.getElementById(element); 
    12191521  return Element.extend(element); 
     
    12261528      null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); 
    12271529    for (var i = 0, length = query.snapshotLength; i < length; i++) 
    1228       results.push(query.snapshotItem(i)); 
     1530      results.push(Element.extend(query.snapshotItem(i))); 
    12291531    return results; 
    12301532  }; 
    1231  
    1232   document.getElementsByClassName = function(className, parentElement) { 
    1233     var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]"; 
    1234     return document._getElementsByXPath(q, parentElement); 
    1235   } 
    1236  
    1237 } else document.getElementsByClassName = function(className, parentElement) { 
    1238   var children = ($(parentElement) || document.body).getElementsByTagName('*'); 
    1239   var elements = [], child; 
    1240   for (var i = 0, length = children.length; i < length; i++) { 
    1241     child = children[i]; 
    1242     if (Element.hasClassName(child, className)) 
    1243       elements.push(Element.extend(child)); 
    1244   } 
    1245   return elements; 
    1246 }; 
     1533} 
    12471534 
    12481535/*--------------------------------------------------------------------------*/ 
    12491536 
    1250 if (!window.Element) var Element = {}; 
    1251  
    1252 Element.extend = function(element) { 
    1253   var F = Prototype.BrowserFeatures; 
    1254   if (!element || !element.tagName || element.nodeType == 3 || 
    1255    element._extended || F.SpecificElementExtensions || element == window) 
    1256     return element; 
    1257  
    1258   var methods = {}, tagName = element.tagName, cache = Element.extend.cache, 
    1259    T = Element.Methods.ByTag; 
    1260  
    1261   // extend methods for all tags (Safari doesn't need this) 
    1262   if (!F.ElementExtensions) { 
    1263     Object.extend(methods, Element.Methods), 
    1264     Object.extend(methods, Element.Methods.Simulated); 
    1265   } 
    1266  
    1267   // extend methods for specific tags 
    1268   if (T[tagName]) Object.extend(methods, T[tagName]); 
    1269  
    1270   for (var property in methods) { 
    1271     var value = methods[property]; 
    1272     if (typeof value == 'function' && !(property in element)) 
    1273       element[property] = cache.findOrStore(value); 
    1274   } 
    1275  
    1276   element._extended = true; 
    1277   return element; 
    1278 }; 
    1279  
    1280 Element.extend.cache = { 
    1281   findOrStore: function(value) { 
    1282     return this[value] = this[value] || function() { 
    1283       return value.apply(null, [this].concat($A(arguments))); 
    1284     } 
    1285   } 
    1286 }; 
     1537if (!window.Node) var Node = { }; 
     1538 
     1539if (!Node.ELEMENT_NODE) { 
     1540  // DOM level 2 ECMAScript Language Binding 
     1541  Object.extend(Node, { 
     1542    ELEMENT_NODE: 1, 
     1543    ATTRIBUTE_NODE: 2, 
     1544    TEXT_NODE: 3, 
     1545    CDATA_SECTION_NODE: 4, 
     1546    ENTITY_REFERENCE_NODE: 5, 
     1547    ENTITY_NODE: 6, 
     1548    PROCESSING_INSTRUCTION_NODE: 7, 
     1549    COMMENT_NODE: 8, 
     1550    DOCUMENT_NODE: 9, 
     1551    DOCUMENT_TYPE_NODE: 10, 
     1552    DOCUMENT_FRAGMENT_NODE: 11, 
     1553    NOTATION_NODE: 12 
     1554  }); 
     1555} 
     1556 
     1557(function() { 
     1558  var element = this.Element; 
     1559  this.Element = function(tagName, attributes) { 
     1560    attributes = attributes || { }; 
     1561    tagName = tagName.toLowerCase(); 
     1562    var cache = Element.cache; 
     1563    if (Prototype.Browser.IE && attributes.name) { 
     1564      tagName = '<' + tagName + ' name="' + attributes.name + '">'; 
     1565      delete attributes.name; 
     1566      return Element.writeAttribute(document.createElement(tagName), attributes); 
     1567    } 
     1568    if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName)); 
     1569    return Element.writeAttribute(cache[tagName].cloneNode(false), attributes); 
     1570  }; 
     1571  Object.extend(this.Element, element || { }); 
     1572  if (element) this.Element.prototype = element.prototype; 
     1573}).call(window); 
     1574 
     1575Element.cache = { }; 
    12871576 
    12881577Element.Methods = { 
     
    12981587 
    12991588  hide: function(element) { 
    1300     $(element).style.display = 'none'; 
     1589    element = $(element); 
     1590    element.style.display = 'none'; 
    13011591    return element; 
    13021592  }, 
    13031593 
    13041594  show: function(element) { 
    1305     $(element).style.display = ''; 
     1595    element = $(element); 
     1596    element.style.display = ''; 
    13061597    return element; 
    13071598  }, 
     
    13131604  }, 
    13141605 
    1315   update: function(element, html) { 
    1316     html = typeof html == 'undefined' ? '' : html.toString(); 
    1317     $(element).innerHTML = html.stripScripts(); 
    1318     setTimeout(function() {html.evalScripts()}, 10); 
     1606  update: function(element, content) { 
     1607    element = $(element); 
     1608    if (content && content.toElement) content = content.toElement(); 
     1609    if (Object.isElement(content)) return element.update().insert(content); 
     1610    content = Object.toHTML(content); 
     1611    element.innerHTML = content.stripScripts(); 
     1612    content.evalScripts.bind(content).defer(); 
    13191613    return element; 
    13201614  }, 
    13211615 
    1322   replace: function(element, html) { 
     1616  replace: function(element, content) { 
    13231617    element = $(element); 
    1324     html = typeof html == 'undefined' ? '' : html.toString(); 
    1325     if (element.outerHTML) { 
    1326       element.outerHTML = html.stripScripts(); 
    1327     } else { 
     1618    if (content && content.toElement) content = content.toElement(); 
     1619    else if (!Object.isElement(content)) { 
     1620      content = Object.toHTML(content); 
    13281621      var range = element.ownerDocument.createRange(); 
    1329       range.selectNodeContents(element); 
    1330       element.parentNode.replaceChild( 
    1331         range.createContextualFragment(html.stripScripts()), element); 
    1332     } 
    1333     setTimeout(function() {html.evalScripts()}, 10); 
     1622      range.selectNode(element); 
     1623      content.evalScripts.bind(content).defer(); 
     1624      content = range.createContextualFragment(content.stripScripts()); 
     1625    } 
     1626    element.parentNode.replaceChild(content, element); 
    13341627    return element; 
     1628  }, 
     1629 
     1630  insert: function(element, insertions) { 
     1631    element = $(element); 
     1632 
     1633    if (Object.isString(insertions) || Object.isNumber(insertions) || 
     1634        Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML))) 
     1635          insertions = {bottom:insertions}; 
     1636 
     1637    var content, insert, tagName, childNodes; 
     1638 
     1639    for (var position in insertions) { 
     1640      content  = insertions[position]; 
     1641      position = position.toLowerCase(); 
     1642      insert = Element._insertionTranslations[position]; 
     1643 
     1644      if (content && content.toElement) content = content.toElement(); 
     1645      if (Object.isElement(content)) { 
     1646        insert(element, content); 
     1647        continue; 
     1648      } 
     1649 
     1650      content = Object.toHTML(content); 
     1651 
     1652      tagName = ((position == 'before' || position == 'after') 
     1653        ? element.parentNode : element).tagName.toUpperCase(); 
     1654 
     1655      childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); 
     1656 
     1657      if (position == 'top' || position == 'after') childNodes.reverse(); 
     1658      childNodes.each(insert.curry(element)); 
     1659 
     1660      content.evalScripts.bind(content).defer(); 
     1661    } 
     1662 
     1663    return element; 
     1664  }, 
     1665 
     1666  wrap: function(element, wrapper, attributes) { 
     1667    element = $(element); 
     1668    if (Object.isElement(wrapper)) 
     1669      $(wrapper).writeAttribute(attributes || { }); 
     1670    else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes); 
     1671    else wrapper = new Element('div', wrapper); 
     1672    if (element.parentNode) 
     1673      element.parentNode.replaceChild(wrapper, element); 
     1674    wrapper.appendChild(element); 
     1675    return wrapper; 
    13351676  }, 
    13361677 
     
    13601701 
    13611702  descendants: function(element) { 
    1362     return $A($(element).getElementsByTagName('*')); 
     1703    return $(element).select("*"); 
     1704  }, 
     1705 
     1706  firstDescendant: function(element) { 
     1707    element = $(element).firstChild; 
     1708    while (element && element.nodeType != 1) element = element.nextSibling; 
     1709    return $(element); 
    13631710  }, 
    13641711 
     
    13841731 
    13851732  match: function(element, selector) { 
    1386     if (typeof selector == 'string') 
     1733    if (Object.isString(selector)) 
    13871734      selector = new Selector(selector); 
    13881735    return selector.match($(element)); 
     
    13901737 
    13911738  up: function(element, expression, index) { 
    1392     var ancestors = $(element).ancestors(); 
    1393     return expression ? Selector.findElement(ancestors, expression, index) : 
    1394       ancestors[index || 0]; 
     1739    element = $(element); 
     1740    if (arguments.length == 1) return $(element.parentNode); 
     1741    var ancestors = element.ancestors(); 
     1742    return Object.isNumber(expression) ? ancestors[expression] : 
     1743      Selector.findElement(ancestors, expression, index); 
    13951744  }, 
    13961745 
    13971746  down: function(element, expression, index) { 
    1398     var descendants = $(element).descendants(); 
    1399     return expression ? Selector.findElement(descendants, expression, index) : 
    1400       descendants[index || 0]; 
     1747    element = $(element); 
     1748    if (arguments.length == 1) return element.firstDescendant(); 
     1749    return Object.isNumber(expression) ? element.descendants()[expression] : 
     1750      Element.select(element, expression)[index || 0]; 
    14011751  }, 
    14021752 
    14031753  previous: function(element, expression, index) { 
    1404     var previousSiblings = $(element).previousSiblings(); 
    1405     return expression ? Selector.findElement(previousSiblings, expression, index) : 
    1406       previousSiblings[index || 0]; 
     1754    element = $(element); 
     1755    if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element)); 
     1756    var previousSiblings = element.previousSiblings(); 
     1757    return Object.isNumber(expression) ? previousSiblings[expression] : 
     1758      Selector.findElement(previousSiblings, expression, index); 
    14071759  }, 
    14081760 
    14091761  next: function(element, expression, index) { 
    1410     var nextSiblings = $(element).nextSiblings(); 
    1411     return expression ? Selector.findElement(nextSiblings, expression, index) : 
    1412       nextSiblings[index || 0]; 
    1413   }, 
    1414  
    1415   getElementsBySelector: function() { 
     1762    element = $(element); 
     1763    if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element)); 
     1764    var nextSiblings = element.nextSiblings(); 
     1765    return Object.isNumber(expression) ? nextSiblings[expression] : 
     1766      Selector.findElement(nextSiblings, expression, index); 
     1767  }, 
     1768 
     1769  select: function() { 
    14161770    var args = $A(arguments), element = $(args.shift()); 
    14171771    return Selector.findChildElements(element, args); 
    14181772  }, 
    14191773 
    1420   getElementsByClassName: function(element, className) { 
    1421     return document.getElementsByClassName(className, element); 
     1774  adjacent: function() { 
     1775    var args = $A(arguments), element = $(args.shift()); 
     1776    return Selector.findChildElements(element.parentNode, args).without(element); 
     1777  }, 
     1778 
     1779  identify: function(element) { 
     1780    element = $(element); 
     1781    var id = element.readAttribute('id'), self = arguments.callee; 
     1782    if (id) return id; 
     1783    do { id = 'anonymous_element_' + self.counter++ } while ($(id)); 
     1784    element.writeAttribute('id', id); 
     1785    return id; 
    14221786  }, 
    14231787 
     
    14251789    element = $(element); 
    14261790    if (Prototype.Browser.IE) { 
    1427       if (!element.attributes) return null; 
    1428       var t = Element._attributeTranslations; 
     1791      var t = Element._attributeTranslations.read; 
    14291792      if (t.values[name]) return t.values[name](element, name); 
    1430       if (t.names[name])  name = t.names[name]; 
    1431       var attribute = element.attributes[name]; 
    1432       return attribute ? attribute.nodeValue : null; 
     1793      if (t.names[name]) name = t.names[name]; 
     1794      if (name.include(':')) { 
     1795        return (!element.attributes || !element.attributes[name]) ? null : 
     1796         element.attributes[name].value; 
     1797      } 
    14331798    } 
    14341799    return element.getAttribute(name); 
     1800  }, 
     1801 
     1802  writeAttribute: function(element, name, value) { 
     1803    element = $(element); 
     1804    var attributes = { }, t = Element._attributeTranslations.write; 
     1805 
     1806    if (typeof name == 'object') attributes = name; 
     1807    else attributes[name] = Object.isUndefined(value) ? true : value; 
     1808 
     1809    for (var attr in attributes) { 
     1810      name = t.names[attr] || attr; 
     1811      value = attributes[attr]; 
     1812      if (t.values[attr]) name = t.values[attr](element, value); 
     1813      if (value === false || value === null) 
     1814        element.removeAttribute(name); 
     1815      else if (value === true) 
     1816        element.setAttribute(name, name); 
     1817      else element.setAttribute(name, value); 
     1818    } 
     1819    return element; 
    14351820  }, 
    14361821 
     
    14501835    if (!(element = $(element))) return; 
    14511836    var elementClassName = element.className; 
    1452     if (elementClassName.length == 0) return false; 
    1453     if (elementClassName == className || 
    1454         elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)"))) 
    1455       return true; 
    1456     return false; 
     1837    return (elementClassName.length > 0 && (elementClassName == className || 
     1838      new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName))); 
    14571839  }, 
    14581840 
    14591841  addClassName: function(element, className) { 
    14601842    if (!(element = $(element))) return; 
    1461     Element.classNames(element).add(className); 
     1843    if (!element.hasClassName(className)) 
     1844      element.className += (element.className ? ' ' : '') + className; 
    14621845    return element; 
    14631846  }, 
     
    14651848  removeClassName: function(element, className) { 
    14661849    if (!(element = $(element))) return; 
    1467     Element.classNames(element).remove(className); 
     1850    element.className = element.className.replace( 
     1851      new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip(); 
    14681852    return element; 
    14691853  }, 
     
    14711855  toggleClassName: function(element, className) { 
    14721856    if (!(element = $(element))) return; 
    1473     Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className); 
    1474     return element; 
    1475   }, 
    1476  
    1477   observe: function() { 
    1478     Event.observe.apply(Event, arguments); 
    1479     return $A(arguments).first(); 
    1480   }, 
    1481  
    1482   stopObserving: function() { 
    1483     Event.stopObserving.apply(Event, arguments); 
    1484     return $A(arguments).first(); 
     1857    return element[element.hasClassName(className) ? 
     1858      'removeClassName' : 'addClassName'](className); 
    14851859  }, 
    14861860 
     
    15041878  descendantOf: function(element, ancestor) { 
    15051879    element = $(element), ancestor = $(ancestor); 
     1880 
     1881    if (element.compareDocumentPosition) 
     1882      return (element.compareDocumentPosition(ancestor) & 8) === 8; 
     1883 
     1884    if (ancestor.contains) 
     1885      return ancestor.contains(element) && ancestor !== element; 
     1886 
    15061887    while (element = element.parentNode) 
    15071888      if (element == ancestor) return true; 
     1889 
    15081890    return false; 
    15091891  }, 
     
    15111893  scrollTo: function(element) { 
    15121894    element = $(element); 
    1513     var pos = Position.cumulativeOffset(element); 
     1895    var pos = element.cumulativeOffset(); 
    15141896    window.scrollTo(pos[0], pos[1]); 
    15151897    return element; 
     
    15201902    style = style == 'float' ? 'cssFloat' : style.camelize(); 
    15211903    var value = element.style[style]; 
    1522     if (!value) { 
     1904    if (!value || value == 'auto') { 
    15231905      var css = document.defaultView.getComputedStyle(element, null); 
    15241906      value = css ? css[style] : null; 
     
    15321914  }, 
    15331915 
    1534   setStyle: function(element, styles, camelized) { 
     1916  setStyle: function(element, styles) { 
    15351917    element = $(element); 
    1536     var elementStyle = element.style; 
    1537  
     1918    var elementStyle = element.style, match; 
     1919    if (Object.isString(styles)) { 
     1920      element.style.cssText += ';' + styles; 
     1921      return styles.include('opacity') ? 
     1922        element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element; 
     1923    } 
    15381924    for (var property in styles) 
    1539       if (property == 'opacity') element.setOpacity(styles[property]) 
     1925      if (property == 'opacity') element.setOpacity(styles[property]); 
    15401926      else 
    15411927        elementStyle[(property == 'float' || property == 'cssFloat') ? 
    1542           (elementStyle.styleFloat === undefined ? 'cssFloat' : 'styleFloat') : 
    1543           (camelized ? property : property.camelize())] = styles[property]; 
     1928          (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') : 
     1929            property] = styles[property]; 
    15441930 
    15451931    return element; 
     
    15551941  getDimensions: function(element) { 
    15561942    element = $(element); 
    1557     var display = $(element).getStyle('display'); 
     1943    var display = element.getStyle('display'); 
    15581944    if (display != 'none' && display != null) // Safari bug 
    15591945      return {width: element.offsetWidth, height: element.offsetHeight}; 
     
    15841970      // Opera returns the offset relative to the positioning context, when an 
    15851971      // element is position relative but top and left have not been defined 
    1586       if (window.opera) { 
     1972      if (Prototype.Browser.Opera) { 
    15871973        element.style.top = 0; 
    15881974        element.style.left = 0; 
     
    16081994    element = $(element); 
    16091995    if (element._overflow) return element; 
    1610     element._overflow = element.style.overflow || 'auto'; 
    1611     if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden') 
     1996    element._overflow = Element.getStyle(element, 'overflow') || 'auto'; 
     1997    if (element._overflow !== 'hidden') 
    16121998      element.style.overflow = 'hidden'; 
    16131999    return element; 
     
    16202006    element._overflow = null; 
    16212007    return element; 
     2008  }, 
     2009 
     2010  cumulativeOffset: function(element) { 
     2011    var valueT = 0, valueL = 0; 
     2012    do { 
     2013      valueT += element.offsetTop  || 0; 
     2014      valueL += element.offsetLeft || 0; 
     2015      element = element.offsetParent; 
     2016    } while (element); 
     2017    return Element._returnOffset(valueL, valueT); 
     2018  }, 
     2019 
     2020  positionedOffset: function(element) { 
     2021    var valueT = 0, valueL = 0; 
     2022    do { 
     2023      valueT += element.offsetTop  || 0; 
     2024      valueL += element.offsetLeft || 0; 
     2025      element = element.offsetParent; 
     2026      if (element) { 
     2027        if (element.tagName.toUpperCase() == 'BODY') break; 
     2028        var p = Element.getStyle(element, 'position'); 
     2029        if (p !== 'static') break; 
     2030      } 
     2031    } while (element); 
     2032    return Element._returnOffset(valueL, valueT); 
     2033  }, 
     2034 
     2035  absolutize: function(element) { 
     2036    element = $(element); 
     2037    if (element.getStyle('position') == 'absolute') return element; 
     2038    // Position.prepare(); // To be done manually by Scripty when it needs it. 
     2039 
     2040    var offsets = element.positionedOffset(); 
     2041    var top     = offsets[1]; 
     2042    var left    = offsets[0]; 
     2043    var width   = element.clientWidth; 
     2044    var height  = element.clientHeight; 
     2045 
     2046    element._originalLeft   = left - parseFloat(element.style.left  || 0); 
     2047    element._originalTop    = top  - parseFloat(element.style.top || 0); 
     2048    element._originalWidth  = element.style.width; 
     2049    element._originalHeight = element.style.height; 
     2050 
     2051    element.style.position = 'absolute'; 
     2052    element.style.top    = top + 'px'; 
     2053    element.style.left   = left + 'px'; 
     2054    element.style.width  = width + 'px'; 
     2055    element.style.height = height + 'px'; 
     2056    return element; 
     2057  }, 
     2058 
     2059  relativize: function(element) { 
     2060    element = $(element); 
     2061    if (element.getStyle('position') == 'relative') return element; 
     2062    // Position.prepare(); // To be done manually by Scripty when it needs it. 
     2063 
     2064    element.style.position = 'relative'; 
     2065    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0); 
     2066    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0); 
     2067 
     2068    element.style.top    = top + 'px'; 
     2069    element.style.left   = left + 'px'; 
     2070    element.style.height = element._originalHeight; 
     2071    element.style.width  = element._originalWidth; 
     2072    return element; 
     2073  }, 
     2074 
     2075  cumulativeScrollOffset: function(element) { 
     2076    var valueT = 0, valueL = 0; 
     2077    do { 
     2078      valueT += element.scrollTop  || 0; 
     2079      valueL += element.scrollLeft || 0; 
     2080      element = element.parentNode; 
     2081    } while (element); 
     2082    return Element._returnOffset(valueL, valueT); 
     2083  }, 
     2084 
     2085  getOffsetParent: function(element) { 
     2086    if (element.offsetParent) return $(element.offsetParent); 
     2087    if (element == document.body) return $(element); 
     2088 
     2089    while ((element = element.parentNode) && element != document.body) 
     2090      if (Element.getStyle(element, 'position') != 'static') 
     2091        return $(element); 
     2092 
     2093    return $(document.body); 
     2094  }, 
     2095 
     2096  viewportOffset: function(forElement) { 
     2097    var valueT = 0, valueL = 0; 
     2098 
     2099    var element = forElement; 
     2100    do { 
     2101      valueT += element.offsetTop  || 0; 
     2102      valueL += element.offsetLeft || 0; 
     2103 
     2104      // Safari fix 
     2105      if (element.offsetParent == document.body && 
     2106        Element.getStyle(element, 'position') == 'absolute') break; 
     2107 
     2108    } while (element = element.offsetParent); 
     2109 
     2110    element = forElement; 
     2111    do { 
     2112      if (!Prototype.Browser.Opera || (element.tagName && (element.tagName.toUpperCase() == 'BODY'))) { 
     2113        valueT -= element.scrollTop  || 0; 
     2114        valueL -= element.scrollLeft || 0; 
     2115      } 
     2116    } while (element = element.parentNode); 
     2117 
     2118    return Element._returnOffset(valueL, valueT); 
     2119  }, 
     2120 
     2121  clonePosition: function(element, source) { 
     2122    var options = Object.extend({ 
     2123      setLeft:    true, 
     2124      setTop:     true, 
     2125      setWidth:   true, 
     2126      setHeight:  true, 
     2127      offsetTop:  0, 
     2128      offsetLeft: 0 
     2129    }, arguments[2] || { }); 
     2130 
     2131    // find page position of source 
     2132    source = $(source); 
     2133    var p = source.viewportOffset(); 
     2134 
     2135    // find coordinate system to use 
     2136    element = $(element); 
     2137    var delta = [0, 0]; 
     2138    var parent = null; 
     2139    // delta [0,0] will do fine with position: fixed elements, 
     2140    // position:absolute needs offsetParent deltas 
     2141    if (Element.getStyle(element, 'position') == 'absolute') { 
     2142      parent = element.getOffsetParent(); 
     2143      delta = parent.viewportOffset(); 
     2144    } 
     2145 
     2146    // correct by body offsets (fixes Safari) 
     2147    if (parent == document.body) { 
     2148      delta[0] -= document.body.offsetLeft; 
     2149      delta[1] -= document.body.offsetTop; 
     2150    } 
     2151 
     2152    // set position 
     2153    if (options.setLeft)   element.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px'; 
     2154    if (options.setTop)    element.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px'; 
     2155    if (options.setWidth)  element.style.width = source.offsetWidth + 'px'; 
     2156    if (options.setHeight) element.style.height = source.offsetHeight + 'px'; 
     2157    return element; 
    16222158  } 
    16232159}; 
    16242160 
    1625 Object.extend(Element.Methods, {childOf: Element.Methods.descendantOf}); 
     2161Element.Methods.identify.counter = 1; 
     2162 
     2163Object.extend(Element.Methods, { 
     2164  getElementsBySelector: Element.Methods.select, 
     2165  childElements: Element.Methods.immediateDescendants 
     2166}); 
     2167 
     2168Element._attributeTranslations = { 
     2169  write: { 
     2170    names: { 
     2171      className: 'class', 
     2172      htmlFor:   'for' 
     2173    }, 
     2174    values: { } 
     2175  } 
     2176}; 
    16262177 
    16272178if (Prototype.Browser.Opera) { 
    1628   Element.Methods._getStyle = Element.Methods.getStyle; 
    1629   Element.Methods.getStyle = function(element, style) { 
    1630     switch(style) { 
    1631       case 'left': 
    1632       case 'top': 
    1633       case 'right': 
    1634       case 'bottom': 
    1635         if (Element._getStyle(element, 'position') == 'static') return null; 
    1636       default: return Element._getStyle(element, style); 
    1637     } 
    1638   }; 
     2179  Element.Methods.getStyle = Element.Methods.getStyle.wrap( 
     2180    function(proceed, element, style) { 
     2181      switch (style) { 
     2182        case 'left': case 'top': case 'right': case 'bottom': 
     2183          if (proceed(element, 'position') === 'static') return null; 
     2184        case 'height': case 'width': 
     2185          // returns '0px' for hidden elements; we want it to return null 
     2186          if (!Element.visible(element)) return null; 
     2187 
     2188          // returns the border-box dimensions rather than the content-box 
     2189          // dimensions, so we subtract padding and borders from the value 
     2190          var dim = parseInt(proceed(element, style), 10); 
     2191 
     2192          if (dim !== element['offset' + style.capitalize()]) 
     2193            return dim + 'px'; 
     2194 
     2195          var properties; 
     2196          if (style === 'height') { 
     2197            properties = ['border-top-width', 'padding-top', 
     2198             'padding-bottom', 'border-bottom-width']; 
     2199          } 
     2200          else { 
     2201            properties = ['border-left-width', 'padding-left', 
     2202             'padding-right', 'border-right-width']; 
     2203          } 
     2204          return properties.inject(dim, function(memo, property) { 
     2205            var val = proceed(element, property); 
     2206            return val === null ? memo : memo - parseInt(val, 10); 
     2207          }) + 'px'; 
     2208        default: return proceed(element, style); 
     2209      } 
     2210    } 
     2211  ); 
     2212 
     2213  Element.Methods.readAttribute = Element.Methods.readAttribute.wrap( 
     2214    function(proceed, element, attribute) { 
     2215      if (attribute === 'title') return element.title; 
     2216      return proceed(element, attribute); 
     2217    } 
     2218  ); 
    16392219} 
     2220 
    16402221else if (Prototype.Browser.IE) { 
     2222  // IE doesn't report offsets correctly for static elements, so we change them 
     2223  // to "relative" to get the values, then change them back. 
     2224  Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap( 
     2225    function(proceed, element) { 
     2226      element = $(element); 
     2227      // IE throws an error if element is not in document 
     2228      try { element.offsetParent } 
     2229      catch(e) { return $(document.body) } 
     2230      var position = element.getStyle('position'); 
     2231      if (position !== 'static') return proceed(element); 
     2232      element.setStyle({ position: 'relative' }); 
     2233      var value = proceed(element); 
     2234      element.setStyle({ position: position }); 
     2235      return value; 
     2236    } 
     2237  ); 
     2238 
     2239  $w('positionedOffset viewportOffset').each(function(method) { 
     2240    Element.Methods[method] = Element.Methods[method].wrap( 
     2241      function(proceed, element) { 
     2242        element = $(element); 
     2243        try { element.offsetParent } 
     2244        catch(e) { return Element._returnOffset(0,0) } 
     2245        var position = element.getStyle('position'); 
     2246        if (position !== 'static') return proceed(element); 
     2247        // Trigger hasLayout on the offset parent so that IE6 reports 
     2248        // accurate offsetTop and offsetLeft values for position: fixed. 
     2249        var offsetParent = element.getOffsetParent(); 
     2250        if (offsetParent && offsetParent.getStyle('position') === 'fixed') 
     2251          offsetParent.setStyle({ zoom: 1 }); 
     2252        element.setStyle({ position: 'relative' }); 
     2253        var value = proceed(element); 
     2254        element.setStyle({ position: position }); 
     2255        return value; 
     2256      } 
     2257    ); 
     2258  }); 
     2259 
     2260  Element.Methods.cumulativeOffset = Element.Methods.cumulativeOffset.wrap( 
     2261    function(proceed, element) { 
     2262      try { element.offsetParent } 
     2263      catch(e) { return Element._returnOffset(0,0) } 
     2264      return proceed(element); 
     2265    } 
     2266  ); 
     2267 
    16412268  Element.Methods.getStyle = function(element, style) { 
    16422269    element = $(element); 
     
    16532280    if (value == 'auto') { 
    16542281      if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none')) 
    1655         return element['offset'+style.capitalize()] + 'px'; 
     2282        return element['offset' + style.capitalize()] + 'px'; 
    16562283      return null; 
    16572284    } 
     
    16602287 
    16612288  Element.Methods.setOpacity = function(element, value) { 
     2289    function stripAlpha(filter){ 
     2290      return filter.replace(/alpha\([^\)]*\)/gi,''); 
     2291    } 
    16622292    element = $(element); 
     2293    var currentStyle = element.currentStyle; 
     2294    if ((currentStyle && !currentStyle.hasLayout) || 
     2295      (!currentStyle && element.style.zoom == 'normal')) 
     2296        element.style.zoom = 1; 
     2297 
    16632298    var filter = element.getStyle('filter'), style = element.style; 
    16642299    if (value == 1 || value === '') { 
    1665       style.filter = filter.replace(/alpha\([^\)]*\)/gi,''); 
     2300      (filter = stripAlpha(filter)) ? 
     2301        style.filter = filter : style.removeAttribute('filter'); 
    16662302      return element; 
    16672303    } else if (value < 0.00001) value = 0; 
    1668     style.filter = filter.replace(/alpha\([^\)]*\)/gi, '') + 
     2304    style.filter = stripAlpha(filter) + 
    16692305      'alpha(opacity=' + (value * 100) + ')'; 
    16702306    return element; 
    16712307  }; 
    16722308 
    1673   // IE is missing .innerHTML support for TABLE-related elements 
    1674   Element.Methods.update = function(element, html) { 
    1675     element = $(element); 
    1676     html = typeof html == 'undefined' ? '' : html.toString(); 
    1677     var tagName = element.tagName.toUpperCase(); 
    1678     if (['THEAD','TBODY','TR','TD'].include(tagName)) { 
    1679       var div = document.createElement('div'); 
    1680       switch (tagName) { 
    1681         case 'THEAD': 
    1682         case 'TBODY': 
    1683           div.innerHTML = '<table><tbody>' +  html.stripScripts() + '</tbody></table>'; 
    1684           depth = 2; 
    1685           break; 
    1686         case 'TR': 
    1687           div.innerHTML = '<table><tbody><tr>' +  html.stripScripts() + '</tr></tbody></table>'; 
    1688           depth = 3; 
    1689           break; 
    1690         case 'TD': 
    1691           div.innerHTML = '<table><tbody><tr><td>' +  html.stripScripts() + '</td></tr></tbody></table>'; 
    1692           depth = 4; 
    1693       } 
    1694       $A(element.childNodes).each(function(node) { element.removeChild(node) }); 
    1695       depth.times(function() { div = div.firstChild }); 
    1696       $A(div.childNodes).each(function(node) { element.appendChild(node) }); 
    1697     } else { 
    1698       element.innerHTML = html.stripScripts(); 
    1699     } 
    1700     setTimeout(function() { html.evalScripts() }, 10); 
    1701     return element; 
    1702   } 
     2309  Element._attributeTranslations = { 
     2310    read: { 
     2311      names: { 
     2312        'class': 'className', 
     2313        'for':   'htmlFor' 
     2314      }, 
     2315      values: { 
     2316        _getAttr: function(element, attribute) { 
     2317          return element.getAttribute(attribute, 2); 
     2318        }, 
     2319        _getAttrNode: function(element, attribute) { 
     2320          var node = element.getAttributeNode(attribute); 
     2321          return node ? node.value : ""; 
     2322        }, 
     2323        _getEv: function(element, attribute) { 
     2324          attribute = element.getAttribute(attribute); 
     2325          return attribute ? attribute.toString().slice(23, -2) : null; 
     2326        }, 
     2327        _flag: function(element, attribute) { 
     2328          return $(element).hasAttribute(attribute) ? attribute : null; 
     2329        }, 
     2330        style: function(element) { 
     2331          return element.style.cssText.toLowerCase(); 
     2332        }, 
     2333        title: function(element) { 
     2334          return element.title; 
     2335        } 
     2336      } 
     2337    } 
     2338  }; 
     2339 
     2340  Element._attributeTranslations.write = { 
     2341    names: Object.extend({ 
     2342      cellpadding: 'cellPadding', 
     2343      cellspacing: 'cellSpacing' 
     2344    }, Element._attributeTranslations.read.names), 
     2345    values: { 
     2346      checked: function(element, value) { 
     2347        element.checked = !!value; 
     2348      }, 
     2349 
     2350      style: function(element, value) { 
     2351        element.style.cssText = value ? value : ''; 
     2352      } 
     2353    } 
     2354  }; 
     2355 
     2356  Element._attributeTranslations.has = {}; 
     2357 
     2358  $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' + 
     2359      'encType maxLength readOnly longDesc frameBorder').each(function(attr) { 
     2360    Element._attributeTranslations.write.names[attr.toLowerCase()] = attr; 
     2361    Element._attributeTranslations.has[attr.toLowerCase()] = attr; 
     2362  }); 
     2363 
     2364  (function(v) { 
     2365    Object.extend(v, { 
     2366      href:        v._getAttr, 
     2367      src:         v._getAttr, 
     2368      type:        v._getAttr, 
     2369      action:      v._getAttrNode, 
     2370      disabled:    v._flag, 
     2371      checked:     v._flag, 
     2372      readonly:    v._flag, 
     2373      multiple:    v._flag, 
     2374      onload:      v._getEv, 
     2375      onunload:    v._getEv, 
     2376      onclick:     v._getEv, 
     2377      ondblclick:  v._getEv, 
     2378      onmousedown: v._getEv, 
     2379      onmouseup:   v._getEv, 
     2380      onmouseover: v._getEv, 
     2381      onmousemove: v._getEv, 
     2382      onmouseout:  v._getEv, 
     2383      onfocus:     v._getEv, 
     2384      onblur:      v._getEv, 
     2385      onkeypress:  v._getEv, 
     2386      onkeydown:   v._getEv, 
     2387      onkeyup:     v._getEv, 
     2388      onsubmit:    v._getEv, 
     2389      onreset:     v._getEv, 
     2390      onselect:    v._getEv, 
     2391      onchange:    v._getEv 
     2392    }); 
     2393  })(Element._attributeTranslations.read.values); 
    17032394} 
    1704 else if (Prototype.Browser.Gecko) { 
     2395 
     2396else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) { 
    17052397  Element.Methods.setOpacity = function(element, value) { 
    17062398    element = $(element); 
     
    17112403} 
    17122404 
    1713 Element._attributeTranslations = { 
    1714   names: { 
    1715     colspan:   "colSpan", 
    1716     rowspan:   "rowSpan", 
    1717     valign:    "vAlign", 
    1718     datetime:  "dateTime", 
    1719     accesskey: "accessKey", 
    1720     tabindex:  "tabIndex", 
    1721     enctype:   "encType", 
    1722     maxlength: "maxLength", 
    1723     readonly:  "readOnly", 
    1724     longdesc:  "longDesc" 
    1725   }, 
    1726   values: { 
    1727     _getAttr: function(element, attribute) { 
    1728       return element.getAttribute(attribute, 2); 
    1729     }, 
    1730     _flag: function(element, attribute) { 
    1731       return $(element).hasAttribute(attribute) ? attribute : null; 
    1732     }, 
    1733     style: function(element) { 
    1734       return element.style.cssText.toLowerCase(); 
    1735     }, 
    1736     title: function(element) { 
    1737       var node = element.getAttributeNode('title'); 
    1738       return node.specified ? node.nodeValue : null; 
    1739     } 
    1740   } 
     2405else if (Prototype.Browser.WebKit) { 
     2406  Element.Methods.setOpacity = function(element, value) { 
     2407    element = $(element); 
     2408    element.style.opacity = (value == 1 || value === '') ? '' : 
     2409      (value < 0.00001) ? 0 : value; 
     2410 
     2411    if (value == 1) 
     2412      if(element.tagName.toUpperCase() == 'IMG' && element.width) { 
     2413        element.width++; element.width--; 
     2414      } else try { 
     2415        var n = document.createTextNode(' '); 
     2416        element.appendChild(n); 
     2417        element.removeChild(n); 
     2418      } catch (e) { } 
     2419 
     2420    return element; 
     2421  }; 
     2422 
     2423  // Safari returns margins on body which is incorrect if the child is absolutely 
     2424  // positioned.  For performance reasons, redefine Element#cumulativeOffset for 
     2425  // KHTML/WebKit only. 
     2426  Element.Methods.cumulativeOffset = function(element) { 
     2427    var valueT = 0, valueL = 0; 
     2428    do { 
     2429      valueT += element.offsetTop  || 0; 
     2430      valueL += element.offsetLeft || 0; 
     2431      if (element.offsetParent == document.body) 
     2432        if (Element.getStyle(element, 'position') == 'absolute') break; 
     2433 
     2434      element = element.offsetParent; 
     2435    } while (element); 
     2436 
     2437    return Element._returnOffset(valueL, valueT); 
     2438  }; 
     2439} 
     2440 
     2441if (Prototype.Browser.IE || Prototype.Browser.Opera) { 
     2442  // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements 
     2443  Element.Methods.update = function(element, content) { 
     2444    element = $(element); 
     2445 
     2446    if (content && content.toElement) content = content.toElement(); 
     2447    if (Object.isElement(content)) return element.update().insert(content); 
     2448 
     2449    content = Object.toHTML(content); 
     2450    var tagName = element.tagName.toUpperCase(); 
     2451 
     2452    if (tagName in Element._insertionTranslations.tags) { 
     2453      $A(element.childNodes).each(function(node) { element.removeChild(node) }); 
     2454      Element._getContentFromAnonymousElement(tagName, content.stripScripts()) 
     2455        .each(function(node) { element.appendChild(node) }); 
     2456    } 
     2457    else element.innerHTML = content.stripScripts(); 
     2458 
     2459    content.evalScripts.bind(content).defer(); 
     2460    return element; 
     2461  }; 
     2462} 
     2463 
     2464if ('outerHTML' in document.createElement('div')) { 
     2465  Element.Methods.replace = function(element, content) { 
     2466    element = $(element); 
     2467 
     2468    if (content && content.toElement) content = content.toElement(); 
     2469    if (Object.isElement(content)) { 
     2470      element.parentNode.replaceChild(content, element); 
     2471      return element; 
     2472    } 
     2473 
     2474    content = Object.toHTML(content); 
     2475    var parent = element.parentNode, tagName = parent.tagName.toUpperCase(); 
     2476 
     2477    if (Element._insertionTranslations.tags[tagName]) { 
     2478      var nextSibling = element.next(); 
     2479      var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); 
     2480      parent.removeChild(element); 
     2481      if (nextSibling) 
     2482        fragments.each(function(node) { parent.insertBefore(node, nextSibling) }); 
     2483      else 
     2484        fragments.each(function(node) { parent.appendChild(node) }); 
     2485    } 
     2486    else element.outerHTML = content.stripScripts(); 
     2487 
     2488    content.evalScripts.bind(content).defer(); 
     2489    return element; 
     2490  }; 
     2491} 
     2492 
     2493Element._returnOffset = function(l, t) { 
     2494  var result = [l, t]; 
     2495  result.left = l; 
     2496  result.top = t; 
     2497  return result; 
    17412498}; 
    17422499 
     2500Element._getContentFromAnonymousElement = function(tagName, html) { 
     2501  var div = new Element('div'), t = Element._insertionTranslations.tags[tagName]; 
     2502  if (t) { 
     2503    div.innerHTML = t[0] + html + t[1]; 
     2504    t[2].times(function() { div = div.firstChild }); 
     2505  } else div.innerHTML = html; 
     2506  return $A(div.childNodes); 
     2507}; 
     2508 
     2509Element._insertionTranslations = { 
     2510  before: function(element, node) { 
     2511    element.parentNode.insertBefore(node, element); 
     2512  }, 
     2513  top: function(element, node) { 
     2514    element.insertBefore(node, element.firstChild); 
     2515  }, 
     2516  bottom: function(element, node) { 
     2517    element.appendChild(node); 
     2518  }, 
     2519  after: function(element, node) { 
     2520    element.parentNode.insertBefore(node, element.nextSibling); 
     2521  }, 
     2522  tags: { 
     2523    TABLE:  ['<table>',                '</table>',                   1], 
     2524    TBODY:  ['<table><tbody>',         '</tbody></table>',           2], 
     2525    TR:     ['<table><tbody><tr>',     '</tr></tbody></table>',      3], 
     2526    TD:     ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4], 
     2527    SELECT: ['<select>',               '</select>',                  1] 
     2528  } 
     2529}; 
     2530 
    17432531(function() { 
    1744   Object.extend(this, { 
    1745     href: this._getAttr, 
    1746     src:  this._getAttr, 
    1747     disabled: this._flag, 
    1748     checked:  this._flag, 
    1749     readonly: this._flag, 
    1750     multiple: this._flag 
     2532  Object.extend(this.tags, { 
     2533    THEAD: this.tags.TBODY, 
     2534    TFOOT: this.tags.TBODY, 
     2535    TH:    this.tags.TD 
    17512536  }); 
    1752 }).call(Element._attributeTranslations.values); 
     2537}).call(Element._insertionTranslations); 
    17532538 
    17542539Element.Methods.Simulated = { 
    17552540  hasAttribute: function(element, attribute) { 
    1756     var t = Element._attributeTranslations, node; 
    1757     attribute = t.names[attribute] || attribute; 
    1758     node = $(element).getAttributeNode(attribute); 
    1759     return node && node.specified; 
     2541    attribute = Element._attributeTranslations.has[attribute] || attribute; 
     2542    var node = $(element).getAttributeNode(attribute); 
     2543    return !!(node && node.specified); 
    17602544  } 
    17612545}; 
    17622546 
    1763 Element.Methods.ByTag = {}; 
     2547Element.Methods.ByTag = { }; 
    17642548 
    17652549Object.extend(Element, Element.Methods); 
    17662550 
    17672551if (!Prototype.BrowserFeatures.ElementExtensions && 
    1768  document.createElement('div').__proto__) { 
    1769   window.HTMLElement = {}; 
    1770   window.HTMLElement.prototype = document.createElement('div').__proto__; 
     2552    document.createElement('div')['__proto__']) { 
     2553  window.HTMLElement = { }; 
     2554  window.HTMLElement.prototype = document.createElement('div')['__proto__']; 
    17712555  Prototype.BrowserFeatures.ElementExtensions = true; 
    17722556} 
     2557 
     2558Element.extend = (function() { 
     2559  if (Prototype.BrowserFeatures.SpecificElementExtensions) 
     2560    return Prototype.K; 
     2561 
     2562  var Methods = { }, ByTag = Element.Methods.ByTag; 
     2563 
     2564  var extend = Object.extend(function(element) { 
     2565    if (!element || element._extendedByPrototype || 
     2566        element.nodeType != 1 || element == window) return element; 
     2567 
     2568    var methods = Object.clone(Methods), 
     2569      tagName = element.tagName.toUpperCase(), property, value; 
     2570 
     2571    // extend methods for specific tags 
     2572    if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]); 
     2573 
     2574    for (property in methods) { 
     2575      value = methods[property]; 
     2576      if (Object.isFunction(value) && !(property in element)) 
     2577        element[property] = value.methodize(); 
     2578    } 
     2579 
     2580    element._extendedByPrototype = Prototype.emptyFunction; 
     2581    return element; 
     2582 
     2583  }, { 
     2584    refresh: function() { 
     2585      // extend methods for all tags (Safari doesn't need this) 
     2586      if (!Prototype.BrowserFeatures.ElementExtensions) { 
     2587        Object.extend(Methods, Element.Methods); 
     2588        Object.extend(Methods, Element.Methods.Simulated); 
     2589      } 
     2590    } 
     2591  }); 
     2592 
     2593  extend.refresh(); 
     2594  return extend; 
     2595})(); 
    17732596 
    17742597Element.hasAttribute = function(element, attribute) { 
     
    17792602Element.addMethods = function(methods) { 
    17802603  var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag; 
     2604 
     2605  if (!methods) { 
     2606    Object.extend(Form, Form.Methods); 
     2607    Object.extend(Form.Element, Form.Element.Methods); 
     2608    Object.extend(Element.Methods.ByTag, { 
     2609      "FORM":     Object.clone(Form.Methods), 
     2610      "INPUT":    Object.clone(Form.Element.Methods), 
     2611      "SELECT":   Object.clone(Form.Element.Methods), 
     2612      "TEXTAREA": Object.clone(Form.Element.Methods) 
     2613    }); 
     2614  } 
     2615 
    17812616  if (arguments.length == 2) { 
    17822617    var tagName = methods; 
     
    17842619  } 
    17852620 
    1786   if (!tagName) Object.extend(Element.Methods, methods || {}); 
     2621  if (!tagName) Object.extend(Element.Methods, methods || { }); 
    17872622  else { 
    1788     if (tagName.constructor == Array) tagName.each(extend); 
     2623    if (Object.isArray(tagName)) tagName.each(extend); 
    17892624    else extend(tagName); 
    17902625  } 
     
    17932628    tagName = tagName.toUpperCase(); 
    17942629    if (!Element.Methods.ByTag[tagName]) 
    1795       Element.Methods.ByTag[tagName] = {}; 
     2630      Element.Methods.ByTag[tagName] = { }; 
    17962631    Object.extend(Element.Methods.ByTag[tagName], methods); 
    17972632  } 
     
    17992634  function copy(methods, destination, onlyIfAbsent) { 
    18002635    onlyIfAbsent = onlyIfAbsent || false; 
    1801     var cache = Element.extend.cache; 
    18022636    for (var property in methods) { 
    18032637      var value = methods[property]; 
     2638      if (!Object.isFunction(value)) continue; 
    18042639      if (!onlyIfAbsent || !(property in destination)) 
    1805         destination[property] = cache.findOrStore(value); 
     2640        destination[property] = value.methodize(); 
    18062641    } 
    18072642  } 
     
    18272662    if (window[klass]) return window[klass]; 
    18282663 
    1829     window[klass] = {}; 
    1830     window[klass].prototype = document.createElement(tagName).__proto__; 
     2664    window[klass] = { }; 
     2665    window[klass].prototype = document.createElement(tagName)['__proto__']; 
    18312666    return window[klass]; 
    18322667  } 
     
    18402675    for (var tag in Element.Methods.ByTag) { 
    18412676      var klass = findDOMClass(tag); 
    1842       if (typeof klass == "undefined") continue; 
     2677      if (Object.isUndefined(klass)) continue; 
    18432678      copy(T[tag], klass.prototype); 
    18442679    } 
    18452680  } 
     2681 
     2682  Object.extend(Element, Element.Methods); 
     2683  delete Element.ByTag; 
     2684 
     2685  if (Element.extend.refresh) Element.extend.refresh(); 
     2686  Element.cache = { }; 
    18462687}; 
    18472688 
    1848 var Toggle = { display: Element.toggle }; 
    1849  
    1850 /*--------------------------------------------------------------------------*/ 
    1851  
    1852 Abstract.Insertion = function(adjacency) { 
    1853   this.adjacency = adjacency; 
    1854 } 
    1855  
    1856 Abstract.Insertion.prototype = { 
    1857   initialize: function(element, content) { 
    1858     this.element = $(element); 
    1859     this.content = content.stripScripts(); 
    1860  
    1861     if (this.adjacency && this.element.insertAdjacentHTML) { 
    1862       try { 
    1863         this.element.insertAdjacentHTML(this.adjacency, this.content); 
    1864       } catch (e) { 
    1865         var tagName = this.element.tagName.toUpperCase(); 
    1866         if (['TBODY', 'TR'].include(tagName)) { 
    1867           this.insertContent(this.contentFromAnonymousTable()); 
    1868         } else { 
    1869           throw e; 
    1870         } 
    1871       } 
    1872     } else { 
    1873       this.range = this.element.ownerDocument.createRange(); 
    1874       if (this.initializeRange) this.initializeRange(); 
    1875       this.insertContent([this.range.createContextualFragment(this.content)]); 
    1876     } 
    1877  
    1878     setTimeout(function() {content.evalScripts()}, 10); 
    1879   }, 
    1880  
    1881   contentFromAnonymousTable: function() { 
    1882     var div = document.createElement('div'); 
    1883     div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>'; 
    1884     return $A(div.childNodes[0].childNodes[0].childNodes); 
    1885   } 
    1886 } 
    1887  
    1888 var Insertion = new Object(); 
    1889  
    1890 Insertion.Before = Class.create(); 
    1891 Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), { 
    1892   initializeRange: function() { 
    1893     this.range.setStartBefore(this.element); 
    1894   }, 
    1895  
    1896   insertContent: function(fragments) { 
    1897     fragments.each((function(fragment) { 
    1898       this.element.parentNode.insertBefore(fragment, this.element); 
    1899     }).bind(this)); 
    1900   } 
    1901 }); 
    1902  
    1903 Insertion.Top = Class.create(); 
    1904 Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), { 
    1905   initializeRange: function() { 
    1906     this.range.selectNodeContents(this.element); 
    1907     this.range.collapse(true); 
    1908   }, 
    1909  
    1910   insertContent: function(fragments) { 
    1911     fragments.reverse(false).each((function(fragment) { 
    1912       this.element.insertBefore(fragment, this.element.firstChild); 
    1913     }).bind(this)); 
    1914   } 
    1915 }); 
    1916  
    1917 Insertion.Bottom = Class.create(); 
    1918 Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), { 
    1919   initializeRange: function() { 
    1920     this.range.selectNodeContents(this.element); 
    1921     this.range.collapse(this.element); 
    1922   }, 
    1923  
    1924   insertContent: function(fragments) { 
    1925     fragments.each((function(fragment) { 
    1926       this.element.appendChild(fragment); 
    1927     }).bind(this)); 
    1928   } 
    1929 }); 
    1930  
    1931 Insertion.After = Class.create(); 
    1932 Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), { 
    1933   initializeRange: function() { 
    1934     this.range.setStartAfter(this.element); 
    1935   }, 
    1936  
    1937   insertContent: function(fragments) { 
    1938     fragments.each((function(fragment) { 
    1939       this.element.parentNode.insertBefore(fragment, 
    1940         this.element.nextSibling); 
    1941     }).bind(this)); 
    1942   } 
    1943 }); 
    1944  
    1945 /*--------------------------------------------------------------------------*/ 
    1946  
    1947 Element.ClassNames = Class.create(); 
    1948 Element.ClassNames.prototype = { 
    1949   initialize: function(element) { 
    1950     this.element = $(element); 
    1951   }, 
    1952  
    1953   _each: function(iterator) { 
    1954     this.element.className.split(/\s+/).select(function(name) { 
    1955       return name.length > 0; 
    1956     })._each(iterator); 
    1957   }, 
    1958  
    1959   set: function(className) { 
    1960     this.element.className = className; 
    1961   }, 
    1962  
    1963   add: function(classNameToAdd) { 
    1964     if (this.include(classNameToAdd)) return; 
    1965     this.set($A(this).concat(classNameToAdd).join(' ')); 
    1966   }, 
    1967  
    1968   remove: function(classNameToRemove) { 
    1969     if (!this.include(classNameToRemove)) return; 
    1970     this.set($A(this).without(classNameToRemove).join(' ')); 
    1971   }, 
    1972  
    1973   toString: function() { 
    1974     return $A(this).join(' '); 
     2689document.viewport = { 
     2690  getDimensions: function() { 
     2691    var dimensions = { }, B = Prototype.Browser; 
     2692    $w('width height').each(function(d) { 
     2693      var D = d.capitalize(); 
     2694      if (B.WebKit && !document.evaluate) { 
     2695        // Safari <3.0 needs self.innerWidth/Height 
     2696        dimensions[d] = self['inner' + D]; 
     2697      } else if (B.Opera && parseFloat(window.opera.version()) < 9.5) { 
     2698        // Opera <9.5 needs document.body.clientWidth/Height 
     2699        dimensions[d] = document.body['client' + D] 
     2700      } else { 
     2701        dimensions[d] = document.documentElement['client' + D]; 
     2702      } 
     2703    }); 
     2704    return dimensions; 
     2705  }, 
     2706 
     2707  getWidth: function() { 
     2708    return this.getDimensions().width; 
     2709  }, 
     2710 
     2711  getHeight: function() { 
     2712    return this.getDimensions().height; 
     2713  }, 
     2714 
     2715  getScrollOffsets: function() { 
     2716    return Element._returnOffset( 
     2717      window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft, 
     2718      window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop); 
    19752719  } 
    19762720}; 
    1977  
    1978 Object.extend(Element.ClassNames.prototype, Enumerable); 
    1979 var Selector = Class.create(); 
    1980  
    1981 Selector.prototype = { 
     2721/* Portions of the Selector class are derived from Jack Slocum's DomQuery, 
     2722 * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style 
     2723 * license.  Please see http://www.yui-ext.com/ for more information. */ 
     2724 
     2725var Selector = Class.create({ 
    19822726  initialize: function(expression) { 
    19832727    this.expression = expression.strip(); 
    1984     this.compileMatcher(); 
     2728 
     2729    if (this.shouldUseSelectorsAPI()) { 
     2730      this.mode = 'selectorsAPI'; 
     2731    } else if (this.shouldUseXPath()) { 
     2732      this.mode = 'xpath'; 
     2733      this.compileXPathMatcher(); 
     2734    } else { 
     2735      this.mode = "normal"; 
     2736      this.compileMatcher(); 
     2737    } 
     2738 
     2739  }, 
     2740 
     2741  shouldUseXPath: function() { 
     2742    if (!Prototype.BrowserFeatures.XPath) return false; 
     2743 
     2744    var e = this.expression; 
     2745 
     2746    // Safari 3 chokes on :*-of-type and :empty 
     2747    if (Prototype.Browser.WebKit && 
     2748     (e.include("-of-type") || e.include(":empty"))) 
     2749      return false; 
     2750 
     2751    // XPath can't do namespaced attributes, nor can it read 
     2752    // the "checked" property from DOM nodes 
     2753    if ((/(\[[\w-]*?:|:checked)/).test(e)) 
     2754      return false; 
     2755 
     2756    return true; 
     2757  }, 
     2758 
     2759  shouldUseSelectorsAPI: function() { 
     2760    if (!Prototype.BrowserFeatures.SelectorsAPI) return false; 
     2761 
     2762    if (!Selector._div) Selector._div = new Element('div'); 
     2763 
     2764    // Make sure the browser treats the selector as valid. Test on an 
     2765    // isolated element to minimize cost of this check. 
     2766    try { 
     2767      Selector._div.querySelector(this.expression); 
     2768    } catch(e) { 
     2769      return false; 
     2770    } 
     2771 
     2772    return true; 
    19852773  }, 
    19862774 
    19872775  compileMatcher: function() { 
    1988     // Selectors with namespaced attributes can't use the XPath version 
    1989     if (Prototype.BrowserFeatures.XPath && !(/\[[\w-]*?:/).test(this.expression)) 
    1990       return this.compileXPathMatcher(); 
    1991  
    19922776    var e = this.expression, ps = Selector.patterns, h = Selector.handlers, 
    19932777        c = Selector.criteria, le, p, m; 
    19942778 
    19952779    if (Selector._cache[e]) { 
    1996       this.matcher = Selector._cache[e]; return; 
    1997     } 
     2780      this.matcher = Selector._cache[e]; 
     2781      return; 
     2782    } 
     2783 
    19982784    this.matcher = ["this.matcher = function(root) {", 
    19992785                    "var r = root, h = Selector.handlers, c = false, n;"]; 
     
    20042790        p = ps[i]; 
    20052791        if (m = e.match(p)) { 
    2006           this.matcher.push(typeof c[i] == 'function' ? c[i](m) : 
    2007               new Template(c[i]).evaluate(m)); 
     2792          this.matcher.push(Object.isFunction(c[i]) ? c[i](m) : 
     2793            new Template(c[i]).evaluate(m)); 
    20082794          e = e.replace(m[0], ''); 
    20092795          break; 
     
    20192805  compileXPathMatcher: function() { 
    20202806    var e = this.expression, ps = Selector.patterns, 
    2021         x = Selector.xpath, le, p, m; 
     2807        x = Selector.xpath, le, m; 
    20222808 
    20232809    if (Selector._cache[e]) { 
     
    20302816      for (var i in ps) { 
    20312817        if (m = e.match(ps[i])) { 
    2032           this.matcher.push(typeof x[i] == 'function' ? x[i](m) : 
     2818          this.matcher.push(Object.isFunction(x[i]) ? x[i](m) : 
    20332819            new Template(x[i]).evaluate(m)); 
    20342820          e = e.replace(m[0], ''); 
     
    20442830  findElements: function(root) { 
    20452831    root = root || document; 
    2046     if (this.xpath) return document._getElementsByXPath(this.xpath, root); 
    2047     return this.matcher(root); 
     2832    var e = this.expression, results; 
     2833 
     2834    switch (this.mode) { 
     2835      case 'selectorsAPI': 
     2836        // querySelectorAll queries document-wide, then filters to descendants 
     2837        // of the context element. That's not what we want. 
     2838        // Add an explicit context to the selector if necessary. 
     2839        if (root !== document) { 
     2840          var oldId = root.id, id = $(root).identify(); 
     2841          e = "#" + id + " " + e; 
     2842        } 
     2843 
     2844        results = $A(root.querySelectorAll(e)).map(Element.extend); 
     2845        root.id = oldId; 
     2846 
     2847        return results; 
     2848      case 'xpath': 
     2849        return document._getElementsByXPath(this.xpath, root); 
     2850      default: 
     2851       return this.matcher(root); 
     2852    } 
    20482853  }, 
    20492854 
    20502855  match: function(element) { 
    2051     return this.findElements(document).include(element); 
     2856    this.tokens = []; 
     2857 
     2858    var e = this.expression, ps = Selector.patterns, as = Selector.assertions; 
     2859    var le, p, m; 
     2860 
     2861    while (e && le !== e && (/\S/).test(e)) { 
     2862      le = e; 
     2863      for (var i in ps) { 
     2864        p = ps[i]; 
     2865        if (m = e.match(p)) { 
     2866          // use the Selector.assertions methods unless the selector 
     2867          // is too complex. 
     2868          if (as[i]) { 
     2869            this.tokens.push([i, Object.clone(m)]); 
     2870            e = e.replace(m[0], ''); 
     2871          } else { 
     2872            // reluctantly do a document-wide search 
     2873            // and look for a match in the array 
     2874            return this.findElements(document).include(element); 
     2875          } 
     2876        } 
     2877      } 
     2878    } 
     2879 
     2880    var match = true, name, matches; 
     2881    for (var i = 0, token; token = this.tokens[i]; i++) { 
     2882      name = token[0], matches = token[1]; 
     2883      if (!Selector.assertions[name](element, matches)) { 
     2884        match = false; break; 
     2885      } 
     2886    } 
     2887 
     2888    return match; 
    20522889  }, 
    20532890 
     
    20592896    return "#<Selector:" + this.expression.inspect() + ">"; 
    20602897  } 
    2061 }; 
     2898}); 
    20622899 
    20632900Object.extend(Selector, { 
    2064   _cache: {}, 
     2901  _cache: { }, 
    20652902 
    20662903  xpath: { 
     
    20762913    className:    "[contains(concat(' ', @class, ' '), ' #{1} ')]", 
    20772914    id:           "[@id='#{1}']", 
    2078     attrPresence: "[@#{1}]", 
     2915    attrPresence: function(m) { 
     2916      m[1] = m[1].toLowerCase(); 
     2917      return new Template("[@#{1}]").evaluate(m); 
     2918    }, 
    20792919    attr: function(m) { 
     2920      m[1] = m[1].toLowerCase(); 
    20802921      m[3] = m[5] || m[6]; 
    20812922      return new Template(Selector.xpath.operators[m[2]]).evaluate(m); 
     
    20842925      var h = Selector.xpath.pseudos[m[1]]; 
    20852926      if (!h) return ''; 
    2086       if (typeof h === 'function') return h(m); 
     2927      if (Object.isFunction(h)) return h(m); 
    20872928      return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m); 
    20882929    }, 
     
    21002941      'last-child':  '[not(following-sibling::*)]', 
    21012942      'only-child':  '[not(preceding-sibling::* or following-sibling::*)]', 
    2102       'empty':       "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]", 
     2943      'empty':       "[count(*) = 0 and (count(text()) = 0)]", 
    21032944      'checked':     "[@checked]", 
    2104       'disabled':    "[@disabled]", 
    2105       'enabled':     "[not(@disabled)]", 
     2945      'disabled':    "[(@disabled) and (@type!='hidden')]", 
     2946      'enabled':     "[not(@disabled) and (@type!='hidden')]", 
    21062947      'not': function(m) { 
    2107         if (!m[6]) return ''; 
    2108         var p = Selector.patterns, x = Selector.xpath; 
    2109         for (var i in p) { 
    2110           if (mm = m[6].match(p[i])) { 
    2111             var ss = typeof x[i] == 'function' ? x[i](mm) : new Template(x[i]).evaluate(mm); 
    2112             m[6] = ss.substring(1, ss.length - 1); 
    2113             break; 
     2948        var e = m[6], p = Selector.patterns, 
     2949            x = Selector.xpath, le, v; 
     2950 
     2951        var exclusion = []; 
     2952        while (e && le != e && (/\S/).test(e)) { 
     2953          le = e; 
     2954          for (var i in p) { 
     2955            if (m = e.match(p[i])) { 
     2956              v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m); 
     2957              exclusion.push("(" + v.substring(1, v.length - 1) + ")"); 
     2958              e = e.replace(m[0], ''); 
     2959              break; 
     2960            } 
    21142961          } 
    21152962        } 
    2116         return "[not(" + m[6] + ")]"; 
     2963        return "[not(" + exclusion.join(" and ") + ")]"; 
    21172964      }, 
    21182965      'nth-child':      function(m) { 
     
    21372984        var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m); 
    21382985      }, 
    2139       nth: function(predicate, m) { 
    2140         var mm, formula = m[6]; 
     2986      nth: function(fragment, m) { 
     2987        var mm, formula = m[6], predicate; 
    21412988        if (formula == 'even') formula = '2n+0'; 
    21422989        if (formula == 'odd')  formula = '2n+1'; 
    21432990        if (mm = formula.match(/^(\d+)$/)) // digit only 
    2144           predicate += "= " + mm[1]; 
    2145         if (mm = formula.match(/^(\d+)?n(\+(\d+))?/)) { // an+b 
     2991          return '[' + fragment + "= " + mm[1] + ']'; 
     2992        if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b 
     2993          if (mm[1] == "-") mm[1] = -1; 
    21462994          var a = mm[1] ? Number(mm[1]) : 1; 
    2147           var b = mm[3] ? Number(mm[3]) : 0; 
    2148           predicate += "mod " + a + " = " + b; 
     2995          var b = mm[2] ? Number(mm[2]) : 0; 
     2996          predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " + 
     2997          "((#{fragment} - #{b}) div #{a} >= 0)]"; 
     2998          return new Template(predicate).evaluate({ 
     2999            fragment: fragment, a: a, b: b }); 
    21493000        } 
    2150         return "[" + predicate + "]"; 
    21513001      } 
    21523002    } 
     
    21543004 
    21553005  criteria: { 
    2156     tagName:      'n = h.tagName(n, r, "#{1}", c);   c = false;', 
    2157     className:    'n = h.className(n, r, "#{1}", c); c = false;', 
    2158     id:           'n = h.id(n, r, "#{1}", c);        c = false;', 
    2159     attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;', 
     3006    tagName:      'n = h.tagName(n, r, "#{1}", c);      c = false;', 
     3007    className:    'n = h.className(n, r, "#{1}", c);    c = false;', 
     3008    id:           'n = h.id(n, r, "#{1}", c);           c = false;', 
     3009    attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;', 
    21603010    attr: function(m) { 
    2161       m[3] = m[5] || m[6]; 
    2162       return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m); 
    2163     }, 
    2164     pseudo:       'n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;', 
     3011      m[3] = (m[5] || m[6]); 
     3012      return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m); 
     3013    }, 
     3014    pseudo: function(m) { 
     3015      if (m[6]) m[6] = m[6].replace(/"/g, '\\"'); 
     3016      return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m); 
     3017    }, 
    21653018    descendant:   'c = "descendant";', 
    21663019    child:        'c = "child";', 
     
    21813034    id:           /^#([\w\-\*]+)(\b|$)/, 
    21823035    className:    /^\.([\w\-\*]+)(\b|$)/, 
    2183     pseudo:       /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$)/, 
    2184     attrPresence: /^\[([\w]+)\]/, 
    2185     attr:         /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\]]*?)\4|([^'"][^\]]*?)))?\]/ 
     3036    pseudo: 
     3037/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/, 
     3038    attrPresence: /^\[((?:[\w]+:)?[\w]+)\]/, 
     3039    attr:         /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/ 
     3040  }, 
     3041 
     3042  // for Selector.match and Element#match 
     3043  assertions: { 
     3044    tagName: function(element, matches) { 
     3045      return matches[1].toUpperCase() == element.tagName.toUpperCase(); 
     3046    }, 
     3047 
     3048    className: function(element, matches) { 
     3049      return Element.hasClassName(element, matches[1]); 
     3050    }, 
     3051 
     3052    id: function(element, matches) { 
     3053      return element.id === matches[1]; 
     3054    }, 
     3055 
     3056    attrPresence: function(element, matches) { 
     3057      return Element.hasAttribute(element, matches[1]); 
     3058    }, 
     3059 
     3060    attr: function(element, matches) { 
     3061      var nodeValue = Element.readAttribute(element, matches[1]); 
     3062      return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]); 
     3063    } 
    21863064  }, 
    21873065 
     
    21973075    // marks an array of nodes for counting 
    21983076    mark: function(nodes) { 
     3077      var _true = Prototype.emptyFunction; 
    21993078      for (var i = 0, node; node = nodes[i]; i++) 
    2200         node._counted = true; 
     3079        node._countedByPrototype = _true; 
    22013080      return nodes; 
    22023081    }, 
     
    22043083    unmark: function(nodes) { 
    22053084      for (var i = 0, node; node = nodes[i]; i++) 
    2206         node._counted = undefined; 
     3085        node._countedByPrototype = undefined; 
    22073086      return nodes; 
    22083087    }, 
     
    22123091    // rather than nth-child 
    22133092    index: function(parentNode, reverse, ofType) { 
    2214       parentNode._counted = true; 
     3093      parentNode._countedByPrototype = Prototype.emptyFunction; 
    22153094      if (reverse) { 
    22163095        for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) { 
    2217           node = nodes[i]; 
    2218           if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++; 
     3096          var node = nodes[i]; 
     3097          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++; 
    22193098        } 
    22203099      } else { 
    22213100        for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++) 
    2222           if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++; 
     3101          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++; 
    22233102      } 
    22243103    }, 
     
    22273106    unique: function(nodes) { 
    22283107      if (nodes.length == 0) return nodes; 
    2229       var results = [nodes[0]], n; 
    2230       nodes[0]._counted = true; 
    2231       for (var i = 0, l = nodes.length; i < l; i++) { 
    2232         n = nodes[i]; 
    2233         if (!n._counted) { 
    2234           n._counted = true; 
     3108      var results = [], n; 
     3109      for (var i = 0, l = nodes.length; i < l; i++) 
     3110        if (!(n = nodes[i])._countedByPrototype) { 
     3111          n._countedByPrototype = Prototype.emptyFunction; 
    22353112          results.push(Element.extend(n)); 
    22363113        } 
    2237       } 
    22383114      return Selector.handlers.unmark(results); 
    22393115    }, 
     
    22433119      var h = Selector.handlers; 
    22443120      for (var i = 0, results = [], node; node = nodes[i]; i++) 
    2245         h.concat(results, Element.descendants(node)); 
     3121        h.concat(results, node.getElementsByTagName('*')); 
    22463122      return results; 
    22473123    }, 
     
    22493125    child: function(nodes) { 
    22503126      var h = Selector.handlers; 
    2251       for (var i = 0, results = [], node; node = nodes[i]; i++) 
    2252         h.concat(results, Element.immediateDescendants(node)); 
     3127      for (var i = 0, results = [], node; node = nodes[i]; i++) { 
     3128        for (var j = 0, child; child = node.childNodes[j]; j++) 
     3129          if (child.nodeType == 1 && child.tagName != '!') results.push(child); 
     3130      } 
    22533131      return results; 
    22543132    }, 
     
    22713149    nextElementSibling: function(node) { 
    22723150      while (node = node.nextSibling) 
    2273               if (node.nodeType == 1) return node; 
     3151        if (node.nodeType == 1) return node; 
    22743152      return null; 
    22753153    }, 
     
    22833161    // TOKEN FUNCTIONS 
    22843162    tagName: function(nodes, root, tagName, combinator) { 
    2285       tagName = tagName.toUpperCase(); 
     3163      var uTagName = tagName.toUpperCase(); 
    22863164      var results = [], h = Selector.handlers; 
    22873165      if (nodes) { 
     
    22963174        } 
    22973175        for (var i = 0, node; node = nodes[i]; i++) 
    2298           if (node.tagName.toUpperCase() == tagName) results.push(node); 
     3176          if (node.tagName.toUpperCase() === uTagName) results.push(node); 
    22993177        return results; 
    23003178      } else return root.getElementsByTagName(tagName); 
     
    23033181    id: function(nodes, root, id, combinator) { 
    23043182      var targetNode = $(id), h = Selector.handlers; 
    2305       if (!nodes && root == document) return targetNode ? [targetNode] : []; 
     3183      if (!targetNode) return []; 
     3184      if (!nodes && root == document) return [targetNode]; 
    23063185      if (nodes) { 
    23073186        if (combinator) { 
     
    23423221    }, 
    23433222 
    2344     attrPresence: function(nodes, root, attr) { 
     3223    attrPresence: function(nodes, root, attr, combinator) { 
     3224      if (!nodes) nodes = root.getElementsByTagName("*"); 
     3225      if (nodes && combinator) nodes = this[combinator](nodes); 
    23453226      var results = []; 
    23463227      for (var i = 0, node; node = nodes[i]; i++) 
     
    23493230    }, 
    23503231 
    2351     attr: function(nodes, root, attr, value, operator) { 
     3232    attr: function(nodes, root, attr, value, operator, combinator) { 
     3233      if (!nodes) nodes = root.getElementsByTagName("*"); 
     3234      if (nodes && combinator) nodes = this[combinator](nodes); 
    23523235      var handler = Selector.operators[operator], results = []; 
    23533236      for (var i = 0, node; node = nodes[i]; i++) { 
     
    23603243 
    23613244    pseudo: function(nodes, name, value, root, combinator) { 
    2362       if (combinator) nodes = this[combinator](nodes); 
     3245      if (nodes && combinator) nodes = this[combinator](nodes); 
     3246      if (!nodes) nodes = root.getElementsByTagName("*"); 
    23633247      return Selector.pseudos[name](nodes, value, root); 
    23643248    } 
     
    24103294    }, 
    24113295 
     3296    // handles the an+b logic 
     3297    getIndices: function(a, b, total) { 
     3298      if (a == 0) return b > 0 ? [b] : []; 
     3299      return $R(1, total).inject([], function(memo, i) { 
     3300        if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i); 
     3301        return memo; 
     3302      }); 
     3303    }, 
     3304 
    24123305    // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type 
    24133306    nth: function(nodes, formula, root, reverse, ofType) { 
     3307      if (nodes.length == 0) return []; 
    24143308      if (formula == 'even') formula = '2n+0'; 
    24153309      if (formula == 'odd')  formula = '2n+1'; 
     
    24173311      h.mark(nodes); 
    24183312      for (var i = 0, node; node = nodes[i]; i++) { 
    2419         if (!node.parentNode._counted) { 
     3313        if (!node.parentNode._countedByPrototype) { 
    24203314          h.index(node.parentNode, reverse, ofType); 
    24213315          indexed.push(node.parentNode); 
     
    24263320        for (var i = 0, node; node = nodes[i]; i++) 
    24273321          if (node.nodeIndex == formula) results.push(node); 
    2428       } else if (m = formula.match(/^(\d+)?n(\+(\d+))?$/)) { // an+b 
     3322      } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b 
     3323        if (m[1] == "-") m[1] = -1; 
    24293324        var a = m[1] ? Number(m[1]) : 1; 
    2430         var b = m[3] ? Number(m[3]) : 0; 
    2431         for (var i = 0, node; node = nodes[i]; i++) 
    2432           if (node.nodeIndex % a == b) results.push(node); 
     3325        var b = m[2] ? Number(m[2]) : 0; 
     3326        var indices = Selector.pseudos.getIndices(a, b, nodes.length); 
     3327        for (var i = 0, node, l = indices.length; node = nodes[i]; i++) { 
     3328          for (var j = 0; j < l; j++) 
     3329            if (node.nodeIndex == indices[j]) results.push(node); 
     3330        } 
    24333331      } 
    24343332      h.unmark(nodes); 
     
    24403338      for (var i = 0, results = [], node; node = nodes[i]; i++) { 
    24413339        // IE treats comments as element nodes 
    2442         if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue; 
     3340        if (node.tagName == '!' || node.firstChild) continue; 
    24433341        results.push(node); 
    24443342      } 
     
    24473345 
    24483346    'not': function(nodes, selector, root) { 
    2449       var h = Selector.handlers, exclusions = $A(nodes), selectorType, m; 
    2450       for (var i in Selector.patterns) { 
    2451         if (m = selector.match(Selector.patterns[i])) { 
    2452           selectorType = i; break; 
    2453         } 
    2454       } 
    2455       switch(selectorType) { 
    2456         case 'className': case 'tagName': case 'id': // fallthroughs 
    2457         case 'attrPresence': exclusions = h[selectorType](exclusions, root, m[1], false); break; 
    2458         case 'attr': m[3] = m[5] || m[6]; exclusions = h.attr(exclusions, root, m[1], m[3], m[2]); break; 
    2459         case 'pseudo': exclusions = h.pseudo(exclusions, m[1], m[6], root, false); break; 
    2460         // only 'simple selectors' (one token) allowed in a :not clause 
    2461         default: throw 'Illegal selector in :not clause.'; 
    2462       } 
     3347      var h = Selector.handlers, selectorType, m; 
     3348      var exclusions = new Selector(selector).findElements(root); 
    24633349      h.mark(exclusions); 
    24643350      for (var i = 0, results = [], node; node = nodes[i]; i++) 
    2465         if (!node._counted) results.push(node); 
     3351        if (!node._countedByPrototype) results.push(node); 
    24663352      h.unmark(exclusions); 
    24673353      return results; 
     
    24703356    'enabled': function(nodes, value, root) { 
    24713357      for (var i = 0, results = [], node; node = nodes[i]; i++) 
    2472         if (!node.disabled) results.push(node); 
     3358        if (!node.disabled && (!node.type || node.type !== 'hidden')) 
     3359          results.push(node); 
    24733360      return results; 
    24743361    }, 
     
    24903377    '=':  function(nv, v) { return nv == v; }, 
    24913378    '!=': function(nv, v) { return nv != v; }, 
    2492     '^=': function(nv, v) { return nv.startsWith(v); }, 
     3379    '^=': function(nv, v) { return nv == v || nv && nv.startsWith(v); }, 
     3380    '$=': function(nv, v) { return nv == v || nv && nv.endsWith(v); }, 
     3381    '*=': function(nv, v) { return nv == v || nv && nv.include(v); }, 
    24933382    '$=': function(nv, v) { return nv.endsWith(v); }, 
    24943383    '*=': function(nv, v) { return nv.include(v); }, 
    24953384    '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); }, 
    2496     '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); } 
     3385    '|=': function(nv, v) { return ('-' + (nv || "").toUpperCase() + 
     3386     '-').include('-' + (v || "").toUpperCase() + '-'); } 
     3387  }, 
     3388 
     3389  split: function(expression) { 
     3390    var expressions = []; 
     3391    expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) { 
     3392      expressions.push(m[1].strip()); 
     3393    }); 
     3394    return expressions; 
    24973395  }, 
    24983396 
    24993397  matchElements: function(elements, expression) { 
    2500     var matches = new Selector(expression).findElements(), h = Selector.handlers; 
     3398    var matches = $$(expression), h = Selector.handlers; 
    25013399    h.mark(matches); 
    25023400    for (var i = 0, results = [], element; element = elements[i]; i++) 
    2503       if (element._counted) results.push(element); 
     3401      if (element._countedByPrototype) results.push(element); 
    25043402    h.unmark(matches); 
    25053403    return results; 
     
    25073405 
    25083406  findElement: function(elements, expression, index) { 
    2509     if (typeof expression == 'number') { 
     3407    if (Object.isNumber(expression)) { 
    25103408      index = expression; expression = false; 
    25113409    } 
     
    25143412 
    25153413  findChildElements: function(element, expressions) { 
    2516     var exprs = expressions.join(','), expressions = []; 
    2517     exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) { 
    2518       expressions.push(m[1].strip()); 
    2519     }); 
     3414    expressions = Selector.split(expressions.join(',')); 
    25203415    var results = [], h = Selector.handlers; 
    25213416    for (var i = 0, l = expressions.length, selector; i < l; i++) { 
     
    25263421  } 
    25273422}); 
     3423 
     3424if (Prototype.Browser.IE) { 
     3425  Object.extend(Selector.handlers, { 
     3426    // IE returns comment nodes on getElementsByTagName("*"). 
     3427    // Filter them out. 
     3428    concat: function(a, b) { 
     3429      for (var i = 0, node; node = b[i]; i++) 
     3430        if (node.tagName !== "!") a.push(node); 
     3431      return a; 
     3432    }, 
     3433 
     3434    // IE improperly serializes _countedByPrototype in (inner|outer)HTML. 
     3435    unmark: function(nodes) { 
     3436      for (var i = 0, node; node = nodes[i]; i++) 
     3437        node.removeAttribute('_countedByPrototype'); 
     3438      return nodes; 
     3439    } 
     3440  }); 
     3441} 
    25283442 
    25293443function $$() { 
     
    25363450  }, 
    25373451 
    2538   serializeElements: function(elements, getHash) { 
    2539     var data = elements.inject({}, function(result, element) { 
     3452  serializeElements: function(elements, options) { 
     3453    if (typeof options != 'object') options = { hash: !!options }; 
     3454    else if (Object.isUndefined(options.hash)) options.hash = true; 
     3455    var key, value, submitted = false, submit = options.submit; 
     3456 
     3457    var data = elements.inject({ }, function(result, element) { 
    25403458      if (!element.disabled && element.name) { 
    2541         var key = element.name, value = $(element).getValue(); 
    2542         if (value != null) { 
    2543                 if (key in result) { 
    2544             if (result[key].constructor != Array) result[key] = [result[key]]; 
     3459        key = element.name; value = $(element).getValue(); 
     3460        if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted && 
     3461            submit !== false && (!submit || key == submit) && (submitted = true)))) { 
     3462          if (key in result) { 
     3463            // a key is already present; construct an array of values 
     3464            if (!Object.isArray(result[key])) result[key] = [result[key]]; 
    25453465            result[key].push(value); 
    25463466          } 
     
    25513471    }); 
    25523472 
    2553     return getHash ? data : Hash.toQueryString(data); 
     3473    return options.hash ? data : Object.toQueryString(data); 
    25543474  } 
    25553475}; 
    25563476 
    25573477Form.Methods = { 
    2558   serialize: function(form, getHash) { 
    2559     return Form.serializeElements(Form.getElements(form), getHash); 
     3478  serialize: function(form, options) { 
     3479    return Form.serializeElements(Form.getElements(form), options); 
    25603480  }, 
    25613481 
     
    25883508  disable: function(form) { 
    25893509    form = $(form); 
    2590     form.getElements().each(function(element) { 
    2591       element.blur(); 
    2592       element.disabled = 'true'; 
    2593     }); 
     3510    Form.getElements(form).invoke('disable'); 
    25943511    return form; 
    25953512  }, 
     
    25973514  enable: function(form) { 
    25983515    form = $(form); 
    2599     form.getElements().each(function(element) { 
    2600       element.disabled = ''; 
     3516    Form.getElements(form).invoke('enable'); 
     3517    return form; 
     3518  }, 
     3519 
     3520  findFirstElement: function(form) { 
     3521    var elements = $(form).getElements().findAll(function(element) { 
     3522      return 'hidden' != element.type && !element.disabled; 
    26013523    }); 
    2602     return form; 
    2603   }, 
    2604  
    2605   findFirstElement: function(form) { 
    2606     return $(form).getElements().find(function(element) { 
    2607       return element.type != 'hidden' && !element.disabled && 
    2608         ['input', 'select', 'textarea'].include(element.tagName.toLowerCase()); 
     3524    var firstByIndex = elements.findAll(function(element) { 
     3525      return element.hasAttribute('tabIndex') && element.tabIndex >= 0; 
     3526    }).sortBy(function(element) { return element.tabIndex }).first(); 
     3527 
     3528    return firstByIndex ? firstByIndex : elements.find(function(element) { 
     3529      return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase()); 
    26093530    }); 
    26103531  }, 
     
    26173538 
    26183539  request: function(form, options) { 
    2619     form = $(form), options = Object.clone(options || {}); 
    2620  
    2621     var params = options.parameters; 
     3540    form = $(form), options = Object.clone(options || { }); 
     3541 
     3542    var params = options.parameters, action = form.readAttribute('action') || ''; 
     3543    if (action.blank()) action = window.location.href; 
    26223544    options.parameters = form.serialize(true); 
    26233545 
    26243546    if (params) { 
    2625       if (typeof params == 'string') params = params.toQueryParams(); 
     3547      if (Object.isString(params)) params = params.toQueryParams(); 
    26263548      Object.extend(options.parameters, params); 
    26273549    } 
     
    26303552      options.method = form.method; 
    26313553 
    2632     return new Ajax.Request(form.action, options); 
    2633   } 
    2634 } 
    2635  
    2636 Object.extend(Form, Form.Methods); 
     3554    return new Ajax.Request(action, options); 
     3555  } 
     3556}; 
    26373557 
    26383558/*--------------------------------------------------------------------------*/ 
     
    26483568    return element; 
    26493569  } 
    2650 } 
     3570}; 
    26513571 
    26523572Form.Element.Methods = { 
     
    26563576      var value = element.getValue(); 
    26573577      if (value != undefined) { 
    2658         var pair = {}; 
     3578        var pair = { }; 
    26593579        pair[element.name] = value; 
    2660         return Hash.toQueryString(pair); 
     3580        return Object.toQueryString(pair); 
    26613581      } 
    26623582    } 
     
    26683588    var method = element.tagName.toLowerCase(); 
    26693589    return Form.Element.Serializers[method](element); 
     3590  }, 
     3591 
     3592  setValue: function(element, value) { 
     3593    element = $(element); 
     3594    var method = element.tagName.toLowerCase(); 
     3595    Form.Element.Serializers[method](element, value); 
     3596    return element; 
    26703597  }, 
    26713598 
     
    26843611      element.focus(); 
    26853612      if (element.select && (element.tagName.toLowerCase() != 'input' || 
    2686         !['button', 'reset', 'submit'].include(element.type))) 
     3613          !['button', 'reset', 'submit'].include(element.type))) 
    26873614        element.select(); 
    2688     } catch (e) {} 
     3615    } catch (e) { } 
    26893616    return element; 
    26903617  }, 
     
    26923619  disable: function(element) { 
    26933620    element = $(element); 
    2694     element.blur(); 
    26953621    element.disabled = true; 
    26963622    return element; 
     
    27023628    return element; 
    27033629  } 
    2704 } 
    2705  
    2706 Object.extend(Form.Element, Form.Element.Methods); 
    2707 Object.extend(Element.Methods.ByTag, { 
    2708   "FORM":     Object.clone(Form.Methods), 
    2709   "INPUT":    Object.clone(Form.Element.Methods), 
    2710   "SELECT":   Object.clone(Form.Element.Methods), 
    2711   "TEXTAREA": Object.clone(Form.Element.Methods) 
    2712 }); 
     3630}; 
    27133631 
    27143632/*--------------------------------------------------------------------------*/ 
    27153633 
    27163634var Field = Form.Element; 
    2717 var $F = Form.Element.getValue; 
     3635var $F = Form.Element.Methods.getValue; 
    27183636 
    27193637/*--------------------------------------------------------------------------*/ 
    27203638 
    27213639Form.Element.Serializers = { 
    2722   input: function(element) { 
     3640  input: function(element, value) { 
    27233641    switch (element.type.toLowerCase()) { 
    27243642      case 'checkbox': 
    27253643      case 'radio': 
    2726         return Form.Element.Serializers.inputSelector(element); 
     3644        return Form.Element.Serializers.inputSelector(element, value); 
    27273645      default: 
    2728         return Form.Element.Serializers.textarea(element); 
    2729     } 
    2730   }, 
    2731  
    2732   inputSelector: function(element) { 
    2733     return element.checked ? element.value : null; 
    2734   }, 
    2735  
    2736   textarea: function(element) { 
    2737     return element.value; 
    2738   }, 
    2739  
    2740   select: function(element) { 
    2741     return this[element.type == 'select-one' ? 
    2742       'selectOne' : 'selectMany'](element); 
     3646        return Form.Element.Serializers.textarea(element, value); 
     3647    } 
     3648  }, 
     3649 
     3650  inputSelector: function(element, value) { 
     3651    if (Object.isUndefined(value)) return element.checked ? element.value : null; 
     3652    else element.checked = !!value; 
     3653  }, 
     3654 
     3655  textarea: function(element, value) { 
     3656    if (Object.isUndefined(value)) return element.value; 
     3657    else element.value = value; 
     3658  }, 
     3659 
     3660  select: function(element, value) { 
     3661    if (Object.isUndefined(value)) 
     3662      return this[element.type == 'select-one' ? 
     3663        'selectOne' : 'selectMany'](element); 
     3664    else { 
     3665      var opt, currentValue, single = !Object.isArray(value); 
     3666      for (var i = 0, length = element.length; i < length; i++) { 
     3667        opt = element.options[i]; 
     3668        currentValue = this.optionValue(opt); 
     3669        if (single) { 
     3670          if (currentValue == value) { 
     3671            opt.selected = true; 
     3672            return; 
     3673          } 
     3674        } 
     3675        else opt.selected = value.include(currentValue); 
     3676      } 
     3677    } 
    27433678  }, 
    27443679 
     
    27633698    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text; 
    27643699  } 
    2765 } 
     3700}; 
    27663701 
    27673702/*--------------------------------------------------------------------------*/ 
    27683703 
    2769 Abstract.TimedObserver = function() {} 
    2770 Abstract.TimedObserver.prototype = { 
    2771   initialize: function(element, frequency, callback) { 
    2772     this.frequency = frequency; 
     3704Abstract.TimedObserver = Class.create(PeriodicalExecuter, { 
     3705  initialize: function($super, element, frequency, callback) { 
     3706    $super(callback, frequency); 
    27733707    this.element   = $(element); 
    2774     this.callback  = callback; 
    2775  
    27763708    this.lastValue = this.getValue(); 
    2777     this.registerCallback(); 
    2778   }, 
    2779  
    2780   registerCallback: function() { 
    2781     setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); 
    2782   }, 
    2783  
    2784   onTimerEvent: function() { 
     3709  }, 
     3710 
     3711  execute: function() { 
    27853712    var value = this.getValue(); 
    2786     var changed = ('string' == typeof this.lastValue && 'string' == typeof value 
    2787       ? this.lastValue != value : String(this.lastValue) != String(value)); 
    2788     if (changed) { 
     3713    if (Object.isString(this.lastValue) && Object.isString(value) ? 
     3714        this.lastValue != value : String(this.lastValue) != String(value)) { 
    27893715      this.callback(this.element, value); 
    27903716      this.lastValue = value; 
    27913717    } 
    27923718  } 
    2793 } 
    2794  
    2795 Form.Element.Observer = Class.create(); 
    2796 Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { 
     3719}); 
     3720 
     3721Form.Element.Observer = Class.create(Abstract.TimedObserver, { 
    27973722  getValue: function() { 
    27983723    return Form.Element.getValue(this.element); 
     
    28003725}); 
    28013726 
    2802 Form.Observer = Class.create(); 
    2803 Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { 
     3727Form.Observer = Class.create(Abstract.TimedObserver, { 
    28043728  getValue: function() { 
    28053729    return Form.serialize(this.element); 
     
    28093733/*--------------------------------------------------------------------------*/ 
    28103734 
    2811 Abstract.EventObserver = function() {} 
    2812 Abstract.EventObserver.prototype = { 
     3735Abstract.EventObserver = Class.create({ 
    28133736  initialize: function(element, callback) { 
    28143737    this.element  = $(element); 
     
    28313754 
    28323755  registerFormCallbacks: function() { 
    2833     Form.getElements(this.element).each(this.registerCallback.bind(this)); 
     3756    Form.getElements(this.element).each(this.registerCallback, this); 
    28343757  }, 
    28353758 
     
    28473770    } 
    28483771  } 
    2849 } 
    2850  
    2851 Form.Element.EventObserver = Class.create(); 
    2852 Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { 
     3772}); 
     3773 
     3774Form.Element.EventObserver = Class.create(Abstract.EventObserver, { 
    28533775  getValue: function() { 
    28543776    return Form.Element.getValue(this.element); 
     
    28563778}); 
    28573779 
    2858 Form.EventObserver = Class.create(); 
    2859 Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { 
     3780Form.EventObserver = Class.create(Abstract.EventObserver, { 
    28603781  getValue: function() { 
    28613782    return Form.serialize(this.element); 
    28623783  } 
    28633784}); 
    2864 if (!window.Event) { 
    2865   var Event = new Object(); 
    2866 } 
     3785if (!window.Event) var Event = { }; 
    28673786 
    28683787Object.extend(Event, { 
     
    28803799  KEY_PAGEUP:   33, 
    28813800  KEY_PAGEDOWN: 34, 
    2882  
    2883   element: function(event) { 
    2884     return event.target || event.srcElement; 
    2885   }, 
    2886  
    2887   isLeftClick: function(event) { 
    2888     return (((event.which) && (event.which == 1)) || 
    2889             ((event.button) && (event.button == 1))); 
    2890   }, 
    2891  
    2892   pointerX: function(event) { 
    2893     return event.pageX || (event.clientX + 
    2894       (document.documentElement.scrollLeft || document.body.scrollLeft)); 
    2895   }, 
    2896  
    2897   pointerY: function(event) { 
    2898     return event.pageY || (event.clientY + 
    2899       (document.documentElement.scrollTop || document.body.scrollTop)); 
    2900   }, 
    2901  
    2902   stop: function(event) { 
    2903     if (event.preventDefault) { 
     3801  KEY_INSERT:   45, 
     3802 
     3803  cache: { }, 
     3804 
     3805  relatedTarget: function(event) { 
     3806    var element; 
     3807    switch(event.type) { 
     3808      case 'mouseover': element = event.fromElement; break; 
     3809      case 'mouseout':  element = event.toElement;   break; 
     3810      default: return null; 
     3811    } 
     3812    return Element.extend(element); 
     3813  } 
     3814}); 
     3815 
     3816Event.Methods = (function() { 
     3817  var isButton; 
     3818 
     3819  if (Prototype.Browser.IE) { 
     3820    var buttonMap = { 0: 1, 1: 4, 2: 2 }; 
     3821    isButton = function(event, code) { 
     3822      return event.button == buttonMap[code]; 
     3823    }; 
     3824 
     3825  } else if (Prototype.Browser.WebKit) { 
     3826    isButton = function(event, code) { 
     3827      switch (code) { 
     3828        case 0: return event.which == 1 && !event.metaKey; 
     3829        case 1: return event.which == 1 && event.metaKey; 
     3830        default: return false; 
     3831      } 
     3832    }; 
     3833 
     3834  } else { 
     3835    isButton = function(event, code) { 
     3836      return event.which ? (event.which === code + 1) : (event.button === code); 
     3837    }; 
     3838  } 
     3839 
     3840  return { 
     3841    isLeftClick:   function(event) { return isButton(event, 0) }, 
     3842    isMiddleClick: function(event) { return isButton(event, 1) }, 
     3843    isRightClick:  function(event) { return isButton(event, 2) }, 
     3844 
     3845    element: function(event) { 
     3846      event = Event.extend(event); 
     3847 
     3848      var node          = event.target, 
     3849          type          = event.type, 
     3850          currentTarget = event.currentTarget; 
     3851 
     3852      if (currentTarget && currentTarget.tagName) { 
     3853        // Firefox screws up the "click" event when moving between radio buttons 
     3854        // via arrow keys. It also screws up the "load" and "error" events on images, 
     3855        // reporting the document as the target instead of the original image. 
     3856        if (type === 'load' || type === 'error' || 
     3857          (type === 'click' && currentTarget.tagName.toLowerCase() === 'input' 
     3858            && currentTarget.type === 'radio')) 
     3859              node = currentTarget; 
     3860      } 
     3861      if (node.nodeType == Node.TEXT_NODE) node = node.parentNode; 
     3862      return Element.extend(node); 
     3863    }, 
     3864 
     3865    findElement: function(event, expression) { 
     3866      var element = Event.element(event); 
     3867      if (!expression) return element; 
     3868      var elements = [element].concat(element.ancestors()); 
     3869      return Selector.findElement(elements, expression, 0); 
     3870    }, 
     3871 
     3872    pointer: function(event) { 
     3873      var docElement = document.documentElement, 
     3874      body = document.body || { scrollLeft: 0, scrollTop: 0 }; 
     3875      return { 
     3876        x: event.pageX || (event.clientX + 
     3877          (docElement.scrollLeft || body.scrollLeft) - 
     3878          (docElement.clientLeft || 0)), 
     3879        y: event.pageY || (event.clientY + 
     3880          (docElement.scrollTop || body.scrollTop) - 
     3881          (docElement.clientTop || 0)) 
     3882      }; 
     3883    }, 
     3884 
     3885    pointerX: function(event) { return Event.pointer(event).x }, 
     3886    pointerY: function(event) { return Event.pointer(event).y }, 
     3887 
     3888    stop: function(event) { 
     3889      Event.extend(event); 
    29043890      event.preventDefault(); 
    29053891      event.stopPropagation(); 
     3892      event.stopped = true; 
     3893    } 
     3894  }; 
     3895})(); 
     3896 
     3897Event.extend = (function() { 
     3898  var methods = Object.keys(Event.Methods).inject({ }, function(m, name) { 
     3899    m[name] = Event.Methods[name].methodize(); 
     3900    return m; 
     3901  }); 
     3902 
     3903  if (Prototype.Browser.IE) { 
     3904    Object.extend(methods, { 
     3905      stopPropagation: function() { this.cancelBubble = true }, 
     3906      preventDefault:  function() { this.returnValue = false }, 
     3907      inspect: function() { return "[object Event]" } 
     3908    }); 
     3909 
     3910    return function(event) { 
     3911      if (!event) return false; 
     3912      if (event._extendedByPrototype) return event; 
     3913 
     3914      event._extendedByPrototype = Prototype.emptyFunction; 
     3915      var pointer = Event.pointer(event); 
     3916      Object.extend(event, { 
     3917        target: event.srcElement, 
     3918        relatedTarget: Event.relatedTarget(event), 
     3919        pageX:  pointer.x, 
     3920        pageY:  pointer.y 
     3921      }); 
     3922      return Object.extend(event, methods); 
     3923    }; 
     3924 
     3925  } else { 
     3926    Event.prototype = Event.prototype || document.createEvent("HTMLEvents")['__proto__']; 
     3927    Object.extend(Event.prototype, methods); 
     3928    return Prototype.K; 
     3929  } 
     3930})(); 
     3931 
     3932Object.extend(Event, (function() { 
     3933  var cache = Event.cache; 
     3934 
     3935  function getEventID(element) { 
     3936    if (element._prototypeEventID) return element._prototypeEventID[0]; 
     3937    arguments.callee.id = arguments.callee.id || 1; 
     3938    return element._prototypeEventID = [++arguments.callee.id]; 
     3939  } 
     3940 
     3941  function getDOMEventName(eventName) { 
     3942    if (eventName && eventName.include(':')) return "dataavailable"; 
     3943    return eventName; 
     3944  } 
     3945 
     3946  function getCacheForID(id) { 
     3947    return cache[id] = cache[id] || { }; 
     3948  } 
     3949 
     3950  function getWrappersForEventName(id, eventName) { 
     3951    var c = getCacheForID(id); 
     3952    return c[eventName] = c[eventName] || []; 
     3953  } 
     3954 
     3955  function createWrapper(element, eventName, handler) { 
     3956    var id = getEventID(element); 
     3957    var c = getWrappersForEventName(id, eventName); 
     3958    if (c.pluck("handler").include(handler)) return false; 
     3959 
     3960    var wrapper = function(event) { 
     3961      if (!Event || !Event.extend || 
     3962        (event.eventName && event.eventName != eventName)) 
     3963          return false; 
     3964 
     3965      Event.extend(event); 
     3966      handler.call(element, event); 
     3967    }; 
     3968 
     3969    wrapper.handler = handler; 
     3970    c.push(wrapper); 
     3971    return wrapper; 
     3972  } 
     3973 
     3974  function findWrapper(id, eventName, handler) { 
     3975    var c = getWrappersForEventName(id, eventName); 
     3976    return c.find(function(wrapper) { return wrapper.handler == handler }); 
     3977  } 
     3978 
     3979  function destroyWrapper(id, eventName, handler) { 
     3980    var c = getCacheForID(id); 
     3981    if (!c[eventName]) return false; 
     3982    c[eventName] = c[eventName].without(findWrapper(id, eventName, handler)); 
     3983  } 
     3984 
     3985  function destroyCache() { 
     3986    for (var id in cache) 
     3987      for (var eventName in cache[id]) 
     3988        cache[id][eventName] = null; 
     3989  } 
     3990 
     3991 
     3992  // Internet Explorer needs to remove event handlers on page unload 
     3993  // in order to avoid memory leaks. 
     3994  if (window.attachEvent) { 
     3995    window.attachEvent("onunload", destroyCache); 
     3996  } 
     3997 
     3998  // Safari has a dummy event handler on page unload so that it won't 
     3999  // use its bfcache. Safari <= 3.1 has an issue with restoring the "document" 
     4000  // object when page is returned to via the back button using its bfcache. 
     4001  if (Prototype.Browser.WebKit) { 
     4002    window.addEventListener('unload', Prototype.emptyFunction, false); 
     4003  } 
     4004 
     4005  return { 
     4006    observe: function(element, eventName, handler) { 
     4007      element = $(element); 
     4008      var name = getDOMEventName(eventName); 
     4009 
     4010      var wrapper = createWrapper(element, eventName, handler); 
     4011      if (!wrapper) return element; 
     4012 
     4013      if (element.addEventListener) { 
     4014        element.addEventListener(name, wrapper, false); 
     4015      } else { 
     4016        element.attachEvent("on" + name, wrapper); 
     4017      } 
     4018 
     4019      return element; 
     4020    }, 
     4021 
     4022    stopObserving: function(element, eventName, handler) { 
     4023      element = $(element); 
     4024      var id = getEventID(element), name = getDOMEventName(eventName); 
     4025 
     4026      if (!handler && eventName) { 
     4027        getWrappersForEventName(id, eventName).each(function(wrapper) { 
     4028          element.stopObserving(eventName, wrapper.handler); 
     4029        }); 
     4030        return element; 
     4031 
     4032      } else if (!eventName) { 
     4033        Object.keys(getCacheForID(id)).each(function(eventName) { 
     4034          element.stopObserving(eventName); 
     4035        }); 
     4036        return element; 
     4037      } 
     4038 
     4039      var wrapper = findWrapper(id, eventName, handler); 
     4040      if (!wrapper) return element; 
     4041 
     4042      if (element.removeEventListener) { 
     4043        element.removeEventListener(name, wrapper, false); 
     4044      } else { 
     4045        element.detachEvent("on" + name, wrapper); 
     4046      } 
     4047 
     4048      destroyWrapper(id, eventName, handler); 
     4049 
     4050      return element; 
     4051    }, 
     4052 
     4053    fire: function(element, eventName, memo) { 
     4054      element = $(element); 
     4055      if (element == document && document.createEvent && !element.dispatchEvent) 
     4056        element = document.documentElement; 
     4057 
     4058      var event; 
     4059      if (document.createEvent) { 
     4060        event = document.createEvent("HTMLEvents"); 
     4061        event.initEvent("dataavailable", true, true); 
     4062      } else { 
     4063        event = document.createEventObject(); 
     4064        event.eventType = "ondataavailable"; 
     4065      } 
     4066 
     4067      event.eventName = eventName; 
     4068      event.memo = memo || { }; 
     4069 
     4070      if (document.createEvent) { 
     4071        element.dispatchEvent(event); 
     4072      } else { 
     4073        element.fireEvent(event.eventType, event); 
     4074      } 
     4075 
     4076      return Event.extend(event); 
     4077    } 
     4078  }; 
     4079})()); 
     4080 
     4081Object.extend(Event, Event.Methods); 
     4082 
     4083Element.addMethods({ 
     4084  fire:          Event.fire, 
     4085  observe:       Event.observe, 
     4086  stopObserving: Event.stopObserving 
     4087}); 
     4088 
     4089Object.extend(document, { 
     4090  fire:          Element.Methods.fire.methodize(), 
     4091  observe:       Element.Methods.observe.methodize(), 
     4092  stopObserving: Element.Methods.stopObserving.methodize(), 
     4093  loaded:        false 
     4094}); 
     4095 
     4096(function() { 
     4097  /* Support for the DOMContentLoaded event is based on work by Dan Webb, 
     4098     Matthias Miller, Dean Edwards and John Resig. */ 
     4099 
     4100  var timer; 
     4101 
     4102  function fireContentLoadedEvent() { 
     4103    if (document.loaded) return; 
     4104    if (timer) window.clearInterval(timer); 
     4105    document.fire("dom:loaded"); 
     4106    document.loaded = true; 
     4107  } 
     4108 
     4109  if (document.addEventListener) { 
     4110    if (Prototype.Browser.WebKit) { 
     4111      timer = window.setInterval(function() { 
     4112        if (/loaded|complete/.test(document.readyState)) 
     4113          fireContentLoadedEvent(); 
     4114      }, 0); 
     4115 
     4116      Event.observe(window, "load", fireContentLoadedEvent); 
     4117 
    29064118    } else { 
    2907       event.returnValue = false; 
    2908       event.cancelBubble = true; 
    2909     } 
    2910   }, 
    2911  
    2912   // find the first node with the given tagName, starting from the 
    2913   // node the event was triggered on; traverses the DOM upwards 
    2914   findElement: function(event, tagName) { 
    2915     var element = Event.element(event); 
    2916     while (element.parentNode && (!element.tagName || 
    2917         (element.tagName.toUpperCase() != tagName.toUpperCase()))) 
    2918       element = element.parentNode; 
    2919     return element; 
    2920   }, 
    2921  
    2922   observers: false, 
    2923  
    2924   _observeAndCache: function(element, name, observer, useCapture) { 
    2925     if (!this.observers) this.observers = []; 
    2926     if (element.addEventListener) { 
    2927       this.observers.push([element, name, observer, useCapture]); 
    2928       element.addEventListener(name, observer, useCapture); 
    2929     } else if (element.attachEvent) { 
    2930       this.observers.push([element, name, observer, useCapture]); 
    2931       element.attachEvent('on' + name, observer); 
    2932     } 
    2933   }, 
    2934  
    2935   unloadCache: function() { 
    2936     if (!Event.observers) return; 
    2937     for (var i = 0, length = Event.observers.length; i < length; i++) { 
    2938       Event.stopObserving.apply(this, Event.observers[i]); 
    2939       Event.observers[i][0] = null; 
    2940     } 
    2941     Event.observers = false; 
    2942   }, 
    2943  
    2944   observe: function(element, name, observer, useCapture) { 
    2945     element = $(element); 
    2946     useCapture = useCapture || false; 
    2947  
    2948     if (name == 'keypress' && 
    2949       (Prototype.Browser.WebKit || element.attachEvent)) 
    2950       name = 'keydown'; 
    2951  
    2952     Event._observeAndCache(element, name, observer, useCapture); 
    2953   }, 
    2954  
    2955   stopObserving: function(element, name, observer, useCapture) { 
    2956     element = $(element); 
    2957     useCapture = useCapture || false; 
    2958  
    2959     if (name == 'keypress' && 
    2960         (Prototype.Browser.WebKit || element.attachEvent)) 
    2961       name = 'keydown'; 
    2962  
    2963     if (element.removeEventListener) { 
    2964       element.removeEventListener(name, observer, useCapture); 
    2965     } else if (element.detachEvent) { 
    2966       try { 
    2967         element.detachEvent('on' + name, observer); 
    2968       } catch (e) {} 
    2969     } 
    2970   } 
    2971 }); 
    2972  
    2973 /* prevent memory leaks in IE */ 
    2974 if (Prototype.Browser.IE) 
    2975   Event.observe(window, 'unload', Event.unloadCache, false); 
     4119      document.addEventListener("DOMContentLoaded", 
     4120        fireContentLoadedEvent, false); 
     4121    } 
     4122 
     4123  } else { 
     4124    document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>"); 
     4125    $("__onDOMContentLoaded").onreadystatechange = function() { 
     4126      if (this.readyState == "complete") { 
     4127        this.onreadystatechange = null; 
     4128        fireContentLoadedEvent(); 
     4129      } 
     4130    }; 
     4131  } 
     4132})(); 
     4133/*------------------------------- DEPRECATED -------------------------------*/ 
     4134 
     4135Hash.toQueryString = Object.toQueryString; 
     4136 
     4137var Toggle = { display: Element.toggle }; 
     4138 
     4139Element.Methods.childOf = Element.Methods.descendantOf; 
     4140 
     4141var Insertion = { 
     4142  Before: function(element, content) { 
     4143    return Element.insert(element, {before:content}); 
     4144  }, 
     4145 
     4146  Top: function(element, content) { 
     4147    return Element.insert(element, {top:content}); 
     4148  }, 
     4149 
     4150  Bottom: function(element, content) { 
     4151    return Element.insert(element, {bottom:content}); 
     4152  }, 
     4153 
     4154  After: function(element, content) { 
     4155    return Element.insert(element, {after:content}); 
     4156  } 
     4157}; 
     4158 
     4159var $continue = new Error('"throw $continue" is deprecated, use "return" instead'); 
     4160 
     4161// This should be moved to script.aculo.us; notice the deprecated methods 
     4162// further below, that map to the newer Element methods. 
    29764163var Position = { 
    29774164  // set to true if needed, warning: firefox performance problems 
     
    29934180  }, 
    29944181 
    2995   realOffset: function(element) { 
    2996     var valueT = 0, valueL = 0; 
    2997     do { 
    2998       valueT += element.scrollTop  || 0; 
    2999       valueL += element.scrollLeft || 0; 
    3000       element = element.parentNode; 
    3001     } while (element); 
    3002     return [valueL, valueT]; 
    3003   }, 
    3004  
    3005   cumulativeOffset: function(element) { 
    3006     var valueT = 0, valueL = 0; 
    3007     do { 
    3008       valueT += element.offsetTop  || 0; 
    3009       valueL += element.offsetLeft || 0; 
    3010       element = element.offsetParent; 
    3011     } while (element); 
    3012     return [valueL, valueT]; 
    3013   }, 
    3014  
    3015   positionedOffset: function(element) { 
    3016     var valueT = 0, valueL = 0; 
    3017     do { 
    3018       valueT += element.offsetTop  || 0; 
    3019       valueL += element.offsetLeft || 0; 
    3020       element = element.offsetParent; 
    3021       if (element) { 
    3022         if(element.tagName=='BODY') break; 
    3023         var p = Element.getStyle(element, 'position'); 
    3024         if (p == 'relative' || p == 'absolute') break; 
    3025       } 
    3026     } while (element); 
    3027     return [valueL, valueT]; 
    3028   }, 
    3029  
    3030   offsetParent: function(element) { 
    3031     if (element.offsetParent) return element.offsetParent; 
    3032     if (element == document.body) return element; 
    3033  
    3034     while ((element = element.parentNode) && element != document.body) 
    3035       if (Element.getStyle(element, 'position') != 'static') 
    3036         return element; 
    3037  
    3038     return document.body; 
    3039   }, 
    3040  
    30414182  // caches x/y coordinate pair to use with overlap 
    30424183  within: function(element, x, y) { 
     
    30454186    this.xcomp = x; 
    30464187    this.ycomp = y; 
    3047     this.offset = this.cumulativeOffset(element); 
     4188    this.offset = Element.cumulativeOffset(element); 
    30484189 
    30494190    return (y >= this.offset[1] && 
     
    30544195 
    30554196  withinIncludingScrolloffsets: function(element, x, y) { 
    3056     var offsetcache = this.realOffset(element); 
     4197    var offsetcache = Element.cumulativeScrollOffset(element); 
    30574198 
    30584199    this.xcomp = x + offsetcache[0] - this.deltaX; 
    30594200    this.ycomp = y + offsetcache[1] - this.deltaY; 
    3060     this.offset = this.cumulativeOffset(element); 
     4201    this.offset = Element.cumulativeOffset(element); 
    30614202 
    30624203    return (this.ycomp >= this.offset[1] && 
     
    30774218  }, 
    30784219 
    3079   page: function(forElement) { 
    3080     var valueT = 0, valueL = 0; 
    3081  
    3082     var element = forElement; 
    3083     do { 
    3084       valueT += element.offsetTop  || 0; 
    3085       valueL += element.offsetLeft || 0; 
    3086  
    3087       // Safari fix 
    3088       if (element.offsetParent == document.body) 
    3089         if (Element.getStyle(element,'position')=='absolute') break; 
    3090  
    3091     } while (element = element.offsetParent); 
    3092  
    3093     element = forElement; 
    3094     do { 
    3095       if (!window.opera || element.tagName=='BODY') { 
    3096         valueT -= element.scrollTop  || 0; 
    3097         valueL -= element.scrollLeft || 0; 
    3098       } 
    3099     } while (element = element.parentNode); 
    3100  
    3101     return [valueL, valueT]; 
    3102   }, 
    3103  
    3104   clone: function(source, target) { 
    3105     var options = Object.extend({ 
    3106       setLeft:    true, 
    3107       setTop:     true, 
    3108       setWidth:   true, 
    3109       setHeight:  true, 
    3110       offsetTop:  0, 
    3111       offsetLeft: 0 
    3112     }, arguments[2] || {}) 
    3113  
    3114     // find page position of source 
    3115     source = $(source); 
    3116     var p = Position.page(source); 
    3117  
    3118     // find coordinate system to use 
    3119     target = $(target); 
    3120     var delta = [0, 0]; 
    3121     var parent = null; 
    3122     // delta [0,0] will do fine with position: fixed elements, 
    3123     // position:absolute needs offsetParent deltas 
    3124     if (Element.getStyle(target,'position') == 'absolute') { 
    3125       parent = Position.offsetParent(target); 
    3126       delta = Position.page(parent); 
    3127     } 
    3128  
    3129     // correct by body offsets (fixes Safari) 
    3130     if (parent == document.body) { 
    3131       delta[0] -= document.body.offsetLeft; 
    3132       delta[1] -= document.body.offsetTop; 
    3133     } 
    3134  
    3135     // set position 
    3136     if(options.setLeft)   target.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px'; 
    3137     if(options.setTop)    target.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px'; 
    3138     if(options.setWidth)  target.style.width = source.offsetWidth + 'px'; 
    3139     if(options.setHeight) target.style.height = source.offsetHeight + 'px'; 
    3140   }, 
     4220  // Deprecation layer -- use newer Element methods now (1.5.2). 
     4221 
     4222  cumulativeOffset: Element.Methods.cumulativeOffset, 
     4223 
     4224  positionedOffset: Element.Methods.positionedOffset, 
    31414225 
    31424226  absolutize: function(element) { 
    3143     element = $(element); 
    3144     if (element.style.position == 'absolute') return; 
    31454227    Position.prepare(); 
    3146  
    3147     var offsets = Position.positionedOffset(element); 
    3148     var top     = offsets[1]; 
    3149     var left    = offsets[0]; 
    3150     var width   = element.clientWidth; 
    3151     var height  = element.clientHeight; 
    3152  
    3153     element._originalLeft   = left - parseFloat(element.style.left  || 0); 
    3154     element._originalTop    = top  - parseFloat(element.style.top || 0); 
    3155     element._originalWidth  = element.style.width; 
    3156     element._originalHeight = element.style.height; 
    3157  
    3158     element.style.position = 'absolute'; 
    3159     element.style.top    = top + 'px'; 
    3160     element.style.left   = left + 'px'; 
    3161     element.style.width  = width + 'px'; 
    3162     element.style.height = height + 'px'; 
     4228    return Element.absolutize(element); 
    31634229  }, 
    31644230 
    31654231  relativize: function(element) { 
    3166     element = $(element); 
    3167     if (element.style.position == 'relative') return; 
    31684232    Position.prepare(); 
    3169  
    3170     element.style.position = 'relative'; 
    3171     var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0); 
    3172     var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0); 
    3173  
    3174     element.style.top    = top + 'px'; 
    3175     element.style.left   = left + 'px'; 
    3176     element.style.height = element._originalHeight; 
    3177     element.style.width  = element._originalWidth; 
    3178   } 
    3179 } 
    3180  
    3181 // Safari returns margins on body which is incorrect if the child is absolutely 
    3182 // positioned.  For performance reasons, redefine Position.cumulativeOffset for 
    3183 // KHTML/WebKit only. 
    3184 if (Prototype.Browser.WebKit) { 
    3185   Position.cumulativeOffset = function(element) { 
    3186     var valueT = 0, valueL = 0; 
    3187     do { 
    3188       valueT += element.offsetTop  || 0; 
    3189       valueL += element.offsetLeft || 0; 
    3190       if (element.offsetParent == document.body) 
    3191         if (Element.getStyle(element, 'position') == 'absolute') break; 
    3192  
    3193       element = element.offsetParent; 
    3194     } while (element); 
    3195  
    3196     return [valueL, valueT]; 
    3197   } 
    3198 } 
     4233    return Element.relativize(element); 
     4234  }, 
     4235 
     4236  realOffset: Element.Methods.cumulativeScrollOffset, 
     4237 
     4238  offsetParent: Element.Methods.getOffsetParent, 
     4239 
     4240  page: Element.Methods.viewportOffset, 
     4241 
     4242  clone: function(source, target, options) { 
     4243    options = options || { }; 
     4244    return Element.clonePosition(target, source, options); 
     4245  } 
     4246}; 
     4247 
     4248/*--------------------------------------------------------------------------*/ 
     4249 
     4250if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){ 
     4251  function iter(name) { 
     4252    return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]"; 
     4253  } 
     4254 
     4255  instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ? 
     4256  function(element, className) { 
     4257    className = className.toString().strip(); 
     4258    var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className); 
     4259    return cond ? document._getElementsByXPath('.//*' + cond, element) : []; 
     4260  } : function(element, className) { 
     4261    className = className.toString().strip(); 
     4262    var elements = [], classNames = (/\s/.test(className) ? $w(className) : null); 
     4263    if (!classNames && !className) return elements; 
     4264 
     4265    var nodes = $(element).getElementsByTagName('*'); 
     4266    className = ' ' + className + ' '; 
     4267 
     4268    for (var i = 0, child, cn; child = nodes[i]; i++) { 
     4269      if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) || 
     4270          (classNames && classNames.all(function(name) { 
     4271            return !name.toString().blank() && cn.include(' ' + name + ' '); 
     4272          })))) 
     4273        elements.push(Element.extend(child)); 
     4274    } 
     4275    return elements; 
     4276  }; 
     4277 
     4278  return function(className, parentElement) { 
     4279    return $(parentElement || document.body).getElementsByClassName(className); 
     4280  }; 
     4281}(Element.Methods); 
     4282 
     4283/*--------------------------------------------------------------------------*/ 
     4284 
     4285Element.ClassNames = Class.create(); 
     4286Element.ClassNames.prototype = { 
     4287  initialize: function(element) { 
     4288    this.element = $(element); 
     4289  }, 
     4290 
     4291  _each: function(iterator) { 
     4292    this.element.className.split(/\s+/).select(function(name) { 
     4293      return name.length > 0; 
     4294    })._each(iterator); 
     4295  }, 
     4296 
     4297  set: function(className) { 
     4298    this.element.className = className; 
     4299  }, 
     4300 
     4301  add: function(classNameToAdd) { 
     4302    if (this.include(classNameToAdd)) return; 
     4303    this.set($A(this).concat(classNameToAdd).join(' ')); 
     4304  }, 
     4305 
     4306  remove: function(classNameToRemove) { 
     4307    if (!this.include(classNameToRemove)) return; 
     4308    this.set($A(this).without(classNameToRemove).join(' ')); 
     4309  }, 
     4310 
     4311  toString: function() { 
     4312    return $A(this).join(' '); 
     4313  } 
     4314}; 
     4315 
     4316Object.extend(Element.ClassNames.prototype, Enumerable); 
     4317 
     4318/*--------------------------------------------------------------------------*/ 
    31994319 
    32004320Element.addMethods(); 
  • MILK/trunk/milk_server/ndgDiscovery.config

    r4477 r4482  
    7878debug: True              
    7979 
     80[WMC_CLIENT] 
     81# specify url for the client; if not specified it will not be possible to view WMS data for  
     82# discovered records 
     83url= 
     84 
    8085[NDG_A_SERVICE] 
    8186badc.nerc.ac.uk: http://glue.badc.rl.ac.uk/cgi-bin/dxui 
Note: See TracChangeset for help on using the changeset viewer.