Changeset 2107 for trunk


Ignore:
Timestamp:
Sep 28, 2007, 12:46:17 AM (17 years ago)
Author:
rvelices
Message:
  • admin, comments and tags pages include page_header later in the code (as in picture and index) allowing plugins to change the header until the very end
  • fix in admin.php : picture_modify requires cache invalidation
  • fix in site_update.php : some echo func calls changed to $template->output .= ...
  • upgraded prototype.js to latest version 1.5.1.1
Location:
trunk
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/admin.php

    r2065 r2107  
    7575$page['page_banner'] = '<h1>'.l10n('PhpWebGallery Administration').'</h1>';
    7676$page['body_id'] = 'theAdminPage';
    77 include(PHPWG_ROOT_PATH.'include/page_header.php');
    7877
    7978$template->set_filenames(array('admin' => 'admin.tpl'));
     
    116115}
    117116
    118 // required before plugin page inclusion
     117//---------------------------------------------------------------- plugin menus
    119118$plugin_menu_links = array(
    120119    array(
     
    125124$plugin_menu_links = trigger_event('get_admin_plugin_menu_links',
    126125  $plugin_menu_links );
    127 
     126foreach ($plugin_menu_links as $menu_item)
     127{
     128  $template->assign_block_vars('plugin_menu.menu_item', $menu_item);
     129}
    128130
    129131include(PHPWG_ROOT_PATH.'admin/'.$page['page'].'.php');
    130132
    131133//------------------------------------------------------------- content display
    132 foreach ($plugin_menu_links as $menu_item)
    133 {
    134   $template->assign_block_vars('plugin_menu.menu_item', $menu_item);
    135 }
    136134
    137135// +-----------------------------------------------------------------------+
     
    155153}
    156154
     155include(PHPWG_ROOT_PATH.'include/page_header.php');
    157156$template->parse('admin');
    158157
     
    172171        'cat_perm',     // ?only POST
    173172        'element_set',  // ?only POST; associate/dissociate
     173        'picture_modify', // ?only POST; associate/dissociate
    174174        'user_list',    // ?only POST; group assoc
    175175        'user_perm',
  • trunk/admin/site_update.php

    r2038 r2107  
    232232  // retrieve sub-directories fulldirs from the site reader
    233233  $fs_fulldirs = $site_reader->get_full_directories($basedir);
    234   //print_r( $fs_fulldirs ); echo "<br>";
    235234
    236235  // get_full_directories doesn't include the base directory, so if it's a
     
    353352  }
    354353
    355   echo '<!-- scanning dirs : ';
    356   echo get_elapsed_time($start, get_moment());
    357   echo ' -->'."\n";
     354  $template->output .= '<!-- scanning dirs : '
     355    . get_elapsed_time($start, get_moment())
     356    . ' -->'."\n";
    358357}
    359358// +-----------------------------------------------------------------------+
     
    367366
    368367  $fs = $site_reader->get_elements($basedir);
    369   //print_r($fs); echo "<br>";
    370   echo '<!-- get_elements: '.get_elapsed_time($start, get_moment())." -->\n";
     368  $template->output .= '<!-- get_elements: '
     369    . get_elapsed_time($start, get_moment())
     370    . " -->\n";
    371371
    372372  $cat_ids = array_diff(array_keys($db_categories), $to_delete);
     
    572572  }
    573573
    574   echo '<!-- scanning files : ';
    575   echo get_elapsed_time($start_files, get_moment());
    576   echo ' -->'."\n";
     574  $template->output .= '<!-- scanning files : '
     575    . get_elapsed_time($start_files, get_moment())
     576    . ' -->'."\n";
    577577
    578578  // retrieving informations given by uploaders
     
    644644    $start = get_moment();
    645645    update_category('all');
    646     echo '<!-- update_category(all) : ';
    647     echo get_elapsed_time($start,get_moment());
    648     echo ' -->'."\n";
     646    $template->output .= '<!-- update_category(all) : '
     647      . get_elapsed_time($start,get_moment())
     648      . ' -->'."\n";
    649649    $start = get_moment();
    650650    ordering();
    651651    update_global_rank();
    652     echo '<!-- ordering categories : ';
    653     echo get_elapsed_time($start, get_moment());
    654     echo ' -->'."\n";
     652    $template->output .= '<!-- ordering categories : '
     653      . get_elapsed_time($start, get_moment())
     654      . ' -->'."\n";
    655655  }
    656656
     
    671671                          $opts['recursive'],
    672672                          false);
    673     echo '<!-- get_filelist : ';
    674     echo get_elapsed_time($start, get_moment());
    675     echo ' -->'."\n";
     673    $template->output .= '<!-- get_filelist : '
     674      . get_elapsed_time($start, get_moment())
     675      . ' -->'."\n";
    676676    $start = get_moment();
    677677
     
    717717        );
    718718    }
    719     echo '<!-- update files : ';
    720     echo get_elapsed_time($start,get_moment());
    721     echo ' -->'."\n";
     719    $template->output .= '<!-- update files : '
     720      . get_elapsed_time($start,get_moment())
     721      . ' -->'."\n";
    722722  }// end if sync files
    723723}
     
    773773                        $opts['only_new']);
    774774
    775   echo '<!-- get_filelist : ';
    776   echo get_elapsed_time($start, get_moment());
    777   echo ' -->'."\n";
     775  $template->output .= '<!-- get_filelist : '
     776    . get_elapsed_time($start, get_moment())
     777    . ' -->'."\n";
    778778
    779779  $start = get_moment();
     
    806806    }
    807807  }
    808  
     808
    809809  foreach ( $files as $id=>$file )
    810810  {
     
    813813      in_array($id, $has_high_images)
    814814      );
    815    
     815
    816816    if ( is_array($data) )
    817817    {
     
    849849    if (count($datas) > 0)
    850850    {
    851       // echo '<pre>', print_r($datas); echo '</pre>';
    852      
    853851      mass_updates(
    854852        IMAGES_TABLE,
     
    872870  }
    873871
    874   echo '<!-- metadata update : ';
    875   echo get_elapsed_time($start, get_moment());
    876   echo ' -->'."\n";
     872  $template->output .= '<!-- metadata update : '
     873    . get_elapsed_time($start, get_moment())
     874    . ' -->'."\n";
    877875
    878876  $template->assign_block_vars(
  • trunk/comments.php

    r2030 r2107  
    55// | Copyright (C) 2003-2007 PhpWebGallery Team - http://phpwebgallery.net |
    66// +-----------------------------------------------------------------------+
    7 // | branch        : BSF (Best So Far)
    87// | file          : $Id$
    98// | last update   : $Date$
     
    176175$title= l10n('title_comments');
    177176$page['body_id'] = 'theCommentsPage';
    178 include(PHPWG_ROOT_PATH.'include/page_header.php');
    179177
    180178$template->set_filenames(array('comments'=>'comments.tpl'));
     
    443441// |                           html code display                           |
    444442// +-----------------------------------------------------------------------+
    445 $template->assign_block_vars('title',array());
     443include(PHPWG_ROOT_PATH.'include/page_header.php');
    446444$template->parse('comments');
    447445include(PHPWG_ROOT_PATH.'include/page_tail.php');
  • trunk/tags.php

    r1912 r2107  
    6060$title= l10n('Tags');
    6161$page['body_id'] = 'theTagsPage';
    62 include(PHPWG_ROOT_PATH.'include/page_header.php');
    6362
    6463$template->set_filenames(array('tags'=>'tags.tpl'));
     
    106105}
    107106
    108 // +-----------------------------------------------------------------------+
    109 // |                           html code display                           |
    110 // +-----------------------------------------------------------------------+
    111 
    112 $template->assign_block_vars('title',array());
     107include(PHPWG_ROOT_PATH.'include/page_header.php');
    113108$template->parse('tags');
    114109include(PHPWG_ROOT_PATH.'include/page_tail.php');
  • trunk/template/yoga/theme/dark/theme.css

    r2101 r2107  
    118118
    119119.virtual_cat { background: #3f3f3f !important; }
    120 #mbMenu #quicksearch > p { text-align: left; }
  • trunk/tools/prototype.js

    r1912 r2107  
    1 /*  Prototype JavaScript framework, version 1.5.0
     1/*  Prototype JavaScript framework, version 1.5.1.1
    22 *  (c) 2005-2007 Sam Stephenson
    33 *
    44 *  Prototype is freely distributable under the terms of an MIT-style license.
    5  *  For details, see the Prototype web site: http://prototype.conio.net/
     5 *  For details, see the Prototype web site: http://www.prototypejs.org/
    66 *
    77/*--------------------------------------------------------------------------*/
    88
    99var Prototype = {
    10   Version: '1.5.0',
    11   BrowserFeatures: {
    12     XPath: !!document.evaluate
    13   },
    14 
    15   ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
    16   emptyFunction: function() {},
    17   K: function(x) { return x }
     10        Version: '1.5.1.1',
     11
     12        Browser: {
     13                IE:     !!(window.attachEvent && !window.opera),
     14                Opera:  !!window.opera,
     15                WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
     16                Gecko:  navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1
     17        },
     18
     19        BrowserFeatures: {
     20                XPath: !!document.evaluate,
     21                ElementExtensions: !!window.HTMLElement,
     22                SpecificElementExtensions:
     23                        (document.createElement('div').__proto__ !==
     24                         document.createElement('form').__proto__)
     25        },
     26
     27        ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
     28        JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
     29
     30        emptyFunction: function() { },
     31        K: function(x) { return x }
    1832}
    1933
    2034var Class = {
    21   create: function() {
    22     return function() {
    23       this.initialize.apply(this, arguments);
    24     }
    25   }
     35        create: function() {
     36                return function() {
     37                        this.initialize.apply(this, arguments);
     38                }
     39        }
    2640}
    2741
     
    2943
    3044Object.extend = function(destination, source) {
    31   for (var property in source) {
    32     destination[property] = source[property];
    33   }
    34   return destination;
     45        for (var property in source) {
     46                destination[property] = source[property];
     47        }
     48        return destination;
    3549}
    3650
    3751Object.extend(Object, {
    38   inspect: function(object) {
    39     try {
    40       if (object === undefined) return 'undefined';
    41       if (object === null) return 'null';
    42       return object.inspect ? object.inspect() : object.toString();
    43     } catch (e) {
    44       if (e instanceof RangeError) return '...';
    45       throw e;
    46     }
    47   },
    48 
    49   keys: function(object) {
    50     var keys = [];
    51     for (var property in object)
    52       keys.push(property);
    53     return keys;
    54   },
    55 
    56   values: function(object) {
    57     var values = [];
    58     for (var property in object)
    59       values.push(object[property]);
    60     return values;
    61   },
    62 
    63   clone: function(object) {
    64     return Object.extend({}, object);
    65   }
     52        inspect: function(object) {
     53                try {
     54                        if (object === undefined) return 'undefined';
     55                        if (object === null) return 'null';
     56                        return object.inspect ? object.inspect() : object.toString();
     57                } catch (e) {
     58                        if (e instanceof RangeError) return '...';
     59                        throw e;
     60                }
     61        },
     62
     63        toJSON: function(object) {
     64                var type = typeof object;
     65                switch(type) {
     66                        case 'undefined':
     67                        case 'function':
     68                        case 'unknown': return;
     69                        case 'boolean': return object.toString();
     70                }
     71                if (object === null) return 'null';
     72                if (object.toJSON) return object.toJSON();
     73                if (object.ownerDocument === document) return;
     74                var results = [];
     75                for (var property in object) {
     76                        var value = Object.toJSON(object[property]);
     77                        if (value !== undefined)
     78                                results.push(property.toJSON() + ': ' + value);
     79                }
     80                return '{' + results.join(', ') + '}';
     81        },
     82
     83        keys: function(object) {
     84                var keys = [];
     85                for (var property in object)
     86                        keys.push(property);
     87                return keys;
     88        },
     89
     90        values: function(object) {
     91                var values = [];
     92                for (var property in object)
     93                        values.push(object[property]);
     94                return values;
     95        },
     96
     97        clone: function(object) {
     98                return Object.extend({}, object);
     99        }
    66100});
    67101
    68102Function.prototype.bind = function() {
    69   var __method = this, args = $A(arguments), object = args.shift();
    70   return function() {
    71     return __method.apply(object, args.concat($A(arguments)));
    72   }
     103        var __method = this, args = $A(arguments), object = args.shift();
     104        return function() {
     105                return __method.apply(object, args.concat($A(arguments)));
     106        }
    73107}
    74108
    75109Function.prototype.bindAsEventListener = function(object) {
    76   var __method = this, args = $A(arguments), object = args.shift();
    77   return function(event) {
    78     return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments)));
    79   }
     110        var __method = this, args = $A(arguments), object = args.shift();
     111        return function(event) {
     112                return __method.apply(object, [event || window.event].concat(args));
     113        }
    80114}
    81115
    82116Object.extend(Number.prototype, {
    83   toColorPart: function() {
    84     var digits = this.toString(16);
    85     if (this < 16) return '0' + digits;
    86     return digits;
    87   },
    88 
    89   succ: function() {
    90     return this + 1;
    91   },
    92 
    93   times: function(iterator) {
    94     $R(0, this, true).each(iterator);
    95     return this;
    96   }
     117        toColorPart: function() {
     118                return this.toPaddedString(2, 16);
     119        },
     120
     121        succ: function() {
     122                return this + 1;
     123        },
     124
     125        times: function(iterator) {
     126                $R(0, this, true).each(iterator);
     127                return this;
     128        },
     129
     130        toPaddedString: function(length, radix) {
     131                var string = this.toString(radix || 10);
     132                return '0'.times(length - string.length) + string;
     133        },
     134
     135        toJSON: function() {
     136                return isFinite(this) ? this.toString() : 'null';
     137        }
    97138});
    98139
     140Date.prototype.toJSON = function() {
     141        return '"' + this.getFullYear() + '-' +
     142                (this.getMonth() + 1).toPaddedString(2) + '-' +
     143                this.getDate().toPaddedString(2) + 'T' +
     144                this.getHours().toPaddedString(2) + ':' +
     145                this.getMinutes().toPaddedString(2) + ':' +
     146                this.getSeconds().toPaddedString(2) + '"';
     147};
     148
    99149var Try = {
    100   these: function() {
    101     var returnValue;
    102 
    103     for (var i = 0, length = arguments.length; i < length; i++) {
    104       var lambda = arguments[i];
    105       try {
    106         returnValue = lambda();
    107         break;
    108       } catch (e) {}
    109     }
    110 
    111     return returnValue;
    112   }
     150        these: function() {
     151                var returnValue;
     152
     153                for (var i = 0, length = arguments.length; i < length; i++) {
     154                        var lambda = arguments[i];
     155                        try {
     156                                returnValue = lambda();
     157                                break;
     158                        } catch (e) {}
     159                }
     160
     161                return returnValue;
     162        }
    113163}
    114164
     
    117167var PeriodicalExecuter = Class.create();
    118168PeriodicalExecuter.prototype = {
    119   initialize: function(callback, frequency) {
    120     this.callback = callback;
    121     this.frequency = frequency;
    122     this.currentlyExecuting = false;
    123 
    124     this.registerCallback();
    125   },
    126 
    127   registerCallback: function() {
    128     this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
    129   },
    130 
    131   stop: function() {
    132     if (!this.timer) return;
    133     clearInterval(this.timer);
    134     this.timer = null;
    135   },
    136 
    137   onTimerEvent: function() {
    138     if (!this.currentlyExecuting) {
    139       try {
    140         this.currentlyExecuting = true;
    141         this.callback(this);
    142       } finally {
    143         this.currentlyExecuting = false;
    144       }
    145     }
    146   }
    147 }
    148 String.interpret = function(value){
    149   return value == null ? '' : String(value);
    150 }
     169        initialize: function(callback, frequency) {
     170                this.callback = callback;
     171                this.frequency = frequency;
     172                this.currentlyExecuting = false;
     173
     174                this.registerCallback();
     175        },
     176
     177        registerCallback: function() {
     178                this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
     179        },
     180
     181        stop: function() {
     182                if (!this.timer) return;
     183                clearInterval(this.timer);
     184                this.timer = null;
     185        },
     186
     187        onTimerEvent: function() {
     188                if (!this.currentlyExecuting) {
     189                        try {
     190                                this.currentlyExecuting = true;
     191                                this.callback(this);
     192                        } finally {
     193                                this.currentlyExecuting = false;
     194                        }
     195                }
     196        }
     197}
     198Object.extend(String, {
     199        interpret: function(value) {
     200                return value == null ? '' : String(value);
     201        },
     202        specialChar: {
     203                '\b': '\\b',
     204                '\t': '\\t',
     205                '\n': '\\n',
     206                '\f': '\\f',
     207                '\r': '\\r',
     208                '\\': '\\\\'
     209        }
     210});
    151211
    152212Object.extend(String.prototype, {
    153   gsub: function(pattern, replacement) {
    154     var result = '', source = this, match;
    155     replacement = arguments.callee.prepareReplacement(replacement);
    156 
    157     while (source.length > 0) {
    158       if (match = source.match(pattern)) {
    159         result += source.slice(0, match.index);
    160         result += String.interpret(replacement(match));
    161         source  = source.slice(match.index + match[0].length);
    162       } else {
    163         result += source, source = '';
    164       }
    165     }
    166     return result;
    167   },
    168 
    169   sub: function(pattern, replacement, count) {
    170     replacement = this.gsub.prepareReplacement(replacement);
    171     count = count === undefined ? 1 : count;
    172 
    173     return this.gsub(pattern, function(match) {
    174       if (--count < 0) return match[0];
    175       return replacement(match);
    176     });
    177   },
    178 
    179   scan: function(pattern, iterator) {
    180     this.gsub(pattern, iterator);
    181     return this;
    182   },
    183 
    184   truncate: function(length, truncation) {
    185     length = length || 30;
    186     truncation = truncation === undefined ? '...' : truncation;
    187     return this.length > length ?
    188       this.slice(0, length - truncation.length) + truncation : this;
    189   },
    190 
    191   strip: function() {
    192     return this.replace(/^\s+/, '').replace(/\s+$/, '');
    193   },
    194 
    195   stripTags: function() {
    196     return this.replace(/<\/?[^>]+>/gi, '');
    197   },
    198 
    199   stripScripts: function() {
    200     return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
    201   },
    202 
    203   extractScripts: function() {
    204     var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
    205     var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
    206     return (this.match(matchAll) || []).map(function(scriptTag) {
    207       return (scriptTag.match(matchOne) || ['', ''])[1];
    208     });
    209   },
    210 
    211   evalScripts: function() {
    212     return this.extractScripts().map(function(script) { return eval(script) });
    213   },
    214 
    215   escapeHTML: function() {
    216     var div = document.createElement('div');
    217     var text = document.createTextNode(this);
    218     div.appendChild(text);
    219     return div.innerHTML;
    220   },
    221 
    222   unescapeHTML: function() {
    223     var div = document.createElement('div');
    224     div.innerHTML = this.stripTags();
    225     return div.childNodes[0] ? (div.childNodes.length > 1 ?
    226       $A(div.childNodes).inject('',function(memo,node){ return memo+node.nodeValue }) :
    227       div.childNodes[0].nodeValue) : '';
    228   },
    229 
    230   toQueryParams: function(separator) {
    231     var match = this.strip().match(/([^?#]*)(#.*)?$/);
    232     if (!match) return {};
    233 
    234     return match[1].split(separator || '&').inject({}, function(hash, pair) {
    235       if ((pair = pair.split('='))[0]) {
    236         var name = decodeURIComponent(pair[0]);
    237         var value = pair[1] ? decodeURIComponent(pair[1]) : undefined;
    238 
    239         if (hash[name] !== undefined) {
    240           if (hash[name].constructor != Array)
    241             hash[name] = [hash[name]];
    242           if (value) hash[name].push(value);
    243         }
    244         else hash[name] = value;
    245       }
    246       return hash;
    247     });
    248   },
    249 
    250   toArray: function() {
    251     return this.split('');
    252   },
    253 
    254   succ: function() {
    255     return this.slice(0, this.length - 1) +
    256       String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
    257   },
    258 
    259   camelize: function() {
    260     var parts = this.split('-'), len = parts.length;
    261     if (len == 1) return parts[0];
    262 
    263     var camelized = this.charAt(0) == '-'
    264       ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
    265       : parts[0];
    266 
    267     for (var i = 1; i < len; i++)
    268       camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
    269 
    270     return camelized;
    271   },
    272 
    273   capitalize: function(){
    274     return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
    275   },
    276 
    277   underscore: function() {
    278     return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
    279   },
    280 
    281   dasherize: function() {
    282     return this.gsub(/_/,'-');
    283   },
    284 
    285   inspect: function(useDoubleQuotes) {
    286     var escapedString = this.replace(/\\/g, '\\\\');
    287     if (useDoubleQuotes)
    288       return '"' + escapedString.replace(/"/g, '\\"') + '"';
    289     else
    290       return "'" + escapedString.replace(/'/g, '\\\'') + "'";
    291   }
     213        gsub: function(pattern, replacement) {
     214                var result = '', source = this, match;
     215                replacement = arguments.callee.prepareReplacement(replacement);
     216
     217                while (source.length > 0) {
     218                        if (match = source.match(pattern)) {
     219                                result += source.slice(0, match.index);
     220                                result += String.interpret(replacement(match));
     221                                source  = source.slice(match.index + match[0].length);
     222                        } else {
     223                                result += source, source = '';
     224                        }
     225                }
     226                return result;
     227        },
     228
     229        sub: function(pattern, replacement, count) {
     230                replacement = this.gsub.prepareReplacement(replacement);
     231                count = count === undefined ? 1 : count;
     232
     233                return this.gsub(pattern, function(match) {
     234                        if (--count < 0) return match[0];
     235                        return replacement(match);
     236                });
     237        },
     238
     239        scan: function(pattern, iterator) {
     240                this.gsub(pattern, iterator);
     241                return this;
     242        },
     243
     244        truncate: function(length, truncation) {
     245                length = length || 30;
     246                truncation = truncation === undefined ? '...' : truncation;
     247                return this.length > length ?
     248                        this.slice(0, length - truncation.length) + truncation : this;
     249        },
     250
     251        strip: function() {
     252                return this.replace(/^\s+/, '').replace(/\s+$/, '');
     253        },
     254
     255        stripTags: function() {
     256                return this.replace(/<\/?[^>]+>/gi, '');
     257        },
     258
     259        stripScripts: function() {
     260                return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
     261        },
     262
     263        extractScripts: function() {
     264                var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
     265                var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
     266                return (this.match(matchAll) || []).map(function(scriptTag) {
     267                        return (scriptTag.match(matchOne) || ['', ''])[1];
     268                });
     269        },
     270
     271        evalScripts: function() {
     272                return this.extractScripts().map(function(script) { return eval(script) });
     273        },
     274
     275        escapeHTML: function() {
     276                var self = arguments.callee;
     277                self.text.data = this;
     278                return self.div.innerHTML;
     279        },
     280
     281        unescapeHTML: function() {
     282                var div = document.createElement('div');
     283                div.innerHTML = this.stripTags();
     284                return div.childNodes[0] ? (div.childNodes.length > 1 ?
     285                        $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
     286                        div.childNodes[0].nodeValue) : '';
     287        },
     288
     289        toQueryParams: function(separator) {
     290                var match = this.strip().match(/([^?#]*)(#.*)?$/);
     291                if (!match) return {};
     292
     293                return match[1].split(separator || '&').inject({}, function(hash, pair) {
     294                        if ((pair = pair.split('='))[0]) {
     295                                var key = decodeURIComponent(pair.shift());
     296                                var value = pair.length > 1 ? pair.join('=') : pair[0];
     297                                if (value != undefined) value = decodeURIComponent(value);
     298
     299                                if (key in hash) {
     300                                        if (hash[key].constructor != Array) hash[key] = [hash[key]];
     301                                        hash[key].push(value);
     302                                }
     303                                else hash[key] = value;
     304                        }
     305                        return hash;
     306                });
     307        },
     308
     309        toArray: function() {
     310                return this.split('');
     311        },
     312
     313        succ: function() {
     314                return this.slice(0, this.length - 1) +
     315                        String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
     316        },
     317
     318        times: function(count) {
     319                var result = '';
     320                for (var i = 0; i < count; i++) result += this;
     321                return result;
     322        },
     323
     324        camelize: function() {
     325                var parts = this.split('-'), len = parts.length;
     326                if (len == 1) return parts[0];
     327
     328                var camelized = this.charAt(0) == '-'
     329                        ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
     330                        : parts[0];
     331
     332                for (var i = 1; i < len; i++)
     333                        camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
     334
     335                return camelized;
     336        },
     337
     338        capitalize: function() {
     339                return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
     340        },
     341
     342        underscore: function() {
     343                return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
     344        },
     345
     346        dasherize: function() {
     347                return this.gsub(/_/,'-');
     348        },
     349
     350        inspect: function(useDoubleQuotes) {
     351                var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
     352                        var character = String.specialChar[match[0]];
     353                        return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
     354                });
     355                if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
     356                return "'" + escapedString.replace(/'/g, '\\\'') + "'";
     357        },
     358
     359        toJSON: function() {
     360                return this.inspect(true);
     361        },
     362
     363        unfilterJSON: function(filter) {
     364                return this.sub(filter || Prototype.JSONFilter, '#{1}');
     365        },
     366
     367        isJSON: function() {
     368                var str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
     369                return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
     370        },
     371
     372        evalJSON: function(sanitize) {
     373                var json = this.unfilterJSON();
     374                try {
     375                        if (!sanitize || json.isJSON()) return eval('(' + json + ')');
     376                } catch (e) { }
     377                throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
     378        },
     379
     380        include: function(pattern) {
     381                return this.indexOf(pattern) > -1;
     382        },
     383
     384        startsWith: function(pattern) {
     385                return this.indexOf(pattern) === 0;
     386        },
     387
     388        endsWith: function(pattern) {
     389                var d = this.length - pattern.length;
     390                return d >= 0 && this.lastIndexOf(pattern) === d;
     391        },
     392
     393        empty: function() {
     394                return this == '';
     395        },
     396
     397        blank: function() {
     398                return /^\s*$/.test(this);
     399        }
    292400});
    293401
     402if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
     403        escapeHTML: function() {
     404                return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
     405        },
     406        unescapeHTML: function() {
     407                return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
     408        }
     409});
     410
    294411String.prototype.gsub.prepareReplacement = function(replacement) {
    295   if (typeof replacement == 'function') return replacement;
    296   var template = new Template(replacement);
    297   return function(match) { return template.evaluate(match) };
     412        if (typeof replacement == 'function') return replacement;
     413        var template = new Template(replacement);
     414        return function(match) { return template.evaluate(match) };
    298415}
    299416
    300417String.prototype.parseQuery = String.prototype.toQueryParams;
     418
     419Object.extend(String.prototype.escapeHTML, {
     420        div:  document.createElement('div'),
     421        text: document.createTextNode('')
     422});
     423
     424with (String.prototype.escapeHTML) div.appendChild(text);
    301425
    302426var Template = Class.create();
    303427Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
    304428Template.prototype = {
    305   initialize: function(template, pattern) {
    306     this.template = template.toString();
    307     this.pattern  = pattern || Template.Pattern;
    308   },
    309 
    310   evaluate: function(object) {
    311     return this.template.gsub(this.pattern, function(match) {
    312       var before = match[1];
    313       if (before == '\\') return match[2];
    314       return before + String.interpret(object[match[3]]);
    315     });
    316   }
    317 }
    318 
    319 var $break    = new Object();
    320 var $continue = new Object();
     429        initialize: function(template, pattern) {
     430                this.template = template.toString();
     431                this.pattern  = pattern || Template.Pattern;
     432        },
     433
     434        evaluate: function(object) {
     435                return this.template.gsub(this.pattern, function(match) {
     436                        var before = match[1];
     437                        if (before == '\\') return match[2];
     438                        return before + String.interpret(object[match[3]]);
     439                });
     440        }
     441}
     442
     443var $break = {}, $continue = new Error('"throw $continue" is deprecated, use "return" instead');
    321444
    322445var Enumerable = {
    323   each: function(iterator) {
    324     var index = 0;
    325     try {
    326       this._each(function(value) {
    327         try {
    328           iterator(value, index++);
    329         } catch (e) {
    330           if (e != $continue) throw e;
    331         }
    332       });
    333     } catch (e) {
    334       if (e != $break) throw e;
    335     }
    336     return this;
    337   },
    338 
    339   eachSlice: function(number, iterator) {
    340     var index = -number, slices = [], array = this.toArray();
    341     while ((index += number) < array.length)
    342       slices.push(array.slice(index, index+number));
    343     return slices.map(iterator);
    344   },
    345 
    346   all: function(iterator) {
    347     var result = true;
    348     this.each(function(value, index) {
    349       result = result && !!(iterator || Prototype.K)(value, index);
    350       if (!result) throw $break;
    351     });
    352     return result;
    353   },
    354 
    355   any: function(iterator) {
    356     var result = false;
    357     this.each(function(value, index) {
    358       if (result = !!(iterator || Prototype.K)(value, index))
    359         throw $break;
    360     });
    361     return result;
    362   },
    363 
    364   collect: function(iterator) {
    365     var results = [];
    366     this.each(function(value, index) {
    367       results.push((iterator || Prototype.K)(value, index));
    368     });
    369     return results;
    370   },
    371 
    372   detect: function(iterator) {
    373     var result;
    374     this.each(function(value, index) {
    375       if (iterator(value, index)) {
    376         result = value;
    377         throw $break;
    378       }
    379     });
    380     return result;
    381   },
    382 
    383   findAll: function(iterator) {
    384     var results = [];
    385     this.each(function(value, index) {
    386       if (iterator(value, index))
    387         results.push(value);
    388     });
    389     return results;
    390   },
    391 
    392   grep: function(pattern, iterator) {
    393     var results = [];
    394     this.each(function(value, index) {
    395       var stringValue = value.toString();
    396       if (stringValue.match(pattern))
    397         results.push((iterator || Prototype.K)(value, index));
    398     })
    399     return results;
    400   },
    401 
    402   include: function(object) {
    403     var found = false;
    404     this.each(function(value) {
    405       if (value == object) {
    406         found = true;
    407         throw $break;
    408       }
    409     });
    410     return found;
    411   },
    412 
    413   inGroupsOf: function(number, fillWith) {
    414     fillWith = fillWith === undefined ? null : fillWith;
    415     return this.eachSlice(number, function(slice) {
    416       while(slice.length < number) slice.push(fillWith);
    417       return slice;
    418     });
    419   },
    420 
    421   inject: function(memo, iterator) {
    422     this.each(function(value, index) {
    423       memo = iterator(memo, value, index);
    424     });
    425     return memo;
    426   },
    427 
    428   invoke: function(method) {
    429     var args = $A(arguments).slice(1);
    430     return this.map(function(value) {
    431       return value[method].apply(value, args);
    432     });
    433   },
    434 
    435   max: function(iterator) {
    436     var result;
    437     this.each(function(value, index) {
    438       value = (iterator || Prototype.K)(value, index);
    439       if (result == undefined || value >= result)
    440         result = value;
    441     });
    442     return result;
    443   },
    444 
    445   min: function(iterator) {
    446     var result;
    447     this.each(function(value, index) {
    448       value = (iterator || Prototype.K)(value, index);
    449       if (result == undefined || value < result)
    450         result = value;
    451     });
    452     return result;
    453   },
    454 
    455   partition: function(iterator) {
    456     var trues = [], falses = [];
    457     this.each(function(value, index) {
    458       ((iterator || Prototype.K)(value, index) ?
    459         trues : falses).push(value);
    460     });
    461     return [trues, falses];
    462   },
    463 
    464   pluck: function(property) {
    465     var results = [];
    466     this.each(function(value, index) {
    467       results.push(value[property]);
    468     });
    469     return results;
    470   },
    471 
    472   reject: function(iterator) {
    473     var results = [];
    474     this.each(function(value, index) {
    475       if (!iterator(value, index))
    476         results.push(value);
    477     });
    478     return results;
    479   },
    480 
    481   sortBy: function(iterator) {
    482     return this.map(function(value, index) {
    483       return {value: value, criteria: iterator(value, index)};
    484     }).sort(function(left, right) {
    485       var a = left.criteria, b = right.criteria;
    486       return a < b ? -1 : a > b ? 1 : 0;
    487     }).pluck('value');
    488   },
    489 
    490   toArray: function() {
    491     return this.map();
    492   },
    493 
    494   zip: function() {
    495     var iterator = Prototype.K, args = $A(arguments);
    496     if (typeof args.last() == 'function')
    497       iterator = args.pop();
    498 
    499     var collections = [this].concat(args).map($A);
    500     return this.map(function(value, index) {
    501       return iterator(collections.pluck(index));
    502     });
    503   },
    504 
    505   size: function() {
    506     return this.toArray().length;
    507   },
    508 
    509   inspect: function() {
    510     return '#<Enumerable:' + this.toArray().inspect() + '>';
    511   }
     446        each: function(iterator) {
     447                var index = 0;
     448                try {
     449                        this._each(function(value) {
     450                                iterator(value, index++);
     451                        });
     452                } catch (e) {
     453                        if (e != $break) throw e;
     454                }
     455                return this;
     456        },
     457
     458        eachSlice: function(number, iterator) {
     459                var index = -number, slices = [], array = this.toArray();
     460                while ((index += number) < array.length)
     461                        slices.push(array.slice(index, index+number));
     462                return slices.map(iterator);
     463        },
     464
     465        all: function(iterator) {
     466                var result = true;
     467                this.each(function(value, index) {
     468                        result = result && !!(iterator || Prototype.K)(value, index);
     469                        if (!result) throw $break;
     470                });
     471                return result;
     472        },
     473
     474        any: function(iterator) {
     475                var result = false;
     476                this.each(function(value, index) {
     477                        if (result = !!(iterator || Prototype.K)(value, index))
     478                                throw $break;
     479                });
     480                return result;
     481        },
     482
     483        collect: function(iterator) {
     484                var results = [];
     485                this.each(function(value, index) {
     486                        results.push((iterator || Prototype.K)(value, index));
     487                });
     488                return results;
     489        },
     490
     491        detect: function(iterator) {
     492                var result;
     493                this.each(function(value, index) {
     494                        if (iterator(value, index)) {
     495                                result = value;
     496                                throw $break;
     497                        }
     498                });
     499                return result;
     500        },
     501
     502        findAll: function(iterator) {
     503                var results = [];
     504                this.each(function(value, index) {
     505                        if (iterator(value, index))
     506                                results.push(value);
     507                });
     508                return results;
     509        },
     510
     511        grep: function(pattern, iterator) {
     512                var results = [];
     513                this.each(function(value, index) {
     514                        var stringValue = value.toString();
     515                        if (stringValue.match(pattern))
     516                                results.push((iterator || Prototype.K)(value, index));
     517                })
     518                return results;
     519        },
     520
     521        include: function(object) {
     522                var found = false;
     523                this.each(function(value) {
     524                        if (value == object) {
     525                                found = true;
     526                                throw $break;
     527                        }
     528                });
     529                return found;
     530        },
     531
     532        inGroupsOf: function(number, fillWith) {
     533                fillWith = fillWith === undefined ? null : fillWith;
     534                return this.eachSlice(number, function(slice) {
     535                        while(slice.length < number) slice.push(fillWith);
     536                        return slice;
     537                });
     538        },
     539
     540        inject: function(memo, iterator) {
     541                this.each(function(value, index) {
     542                        memo = iterator(memo, value, index);
     543                });
     544                return memo;
     545        },
     546
     547        invoke: function(method) {
     548                var args = $A(arguments).slice(1);
     549                return this.map(function(value) {
     550                        return value[method].apply(value, args);
     551                });
     552        },
     553
     554        max: function(iterator) {
     555                var result;
     556                this.each(function(value, index) {
     557                        value = (iterator || Prototype.K)(value, index);
     558                        if (result == undefined || value >= result)
     559                                result = value;
     560                });
     561                return result;
     562        },
     563
     564        min: function(iterator) {
     565                var result;
     566                this.each(function(value, index) {
     567                        value = (iterator || Prototype.K)(value, index);
     568                        if (result == undefined || value < result)
     569                                result = value;
     570                });
     571                return result;
     572        },
     573
     574        partition: function(iterator) {
     575                var trues = [], falses = [];
     576                this.each(function(value, index) {
     577                        ((iterator || Prototype.K)(value, index) ?
     578                                trues : falses).push(value);
     579                });
     580                return [trues, falses];
     581        },
     582
     583        pluck: function(property) {
     584                var results = [];
     585                this.each(function(value, index) {
     586                        results.push(value[property]);
     587                });
     588                return results;
     589        },
     590
     591        reject: function(iterator) {
     592                var results = [];
     593                this.each(function(value, index) {
     594                        if (!iterator(value, index))
     595                                results.push(value);
     596                });
     597                return results;
     598        },
     599
     600        sortBy: function(iterator) {
     601                return this.map(function(value, index) {
     602                        return {value: value, criteria: iterator(value, index)};
     603                }).sort(function(left, right) {
     604                        var a = left.criteria, b = right.criteria;
     605                        return a < b ? -1 : a > b ? 1 : 0;
     606                }).pluck('value');
     607        },
     608
     609        toArray: function() {
     610                return this.map();
     611        },
     612
     613        zip: function() {
     614                var iterator = Prototype.K, args = $A(arguments);
     615                if (typeof args.last() == 'function')
     616                        iterator = args.pop();
     617
     618                var collections = [this].concat(args).map($A);
     619                return this.map(function(value, index) {
     620                        return iterator(collections.pluck(index));
     621                });
     622        },
     623
     624        size: function() {
     625                return this.toArray().length;
     626        },
     627
     628        inspect: function() {
     629                return '#<Enumerable:' + this.toArray().inspect() + '>';
     630        }
    512631}
    513632
    514633Object.extend(Enumerable, {
    515   map:     Enumerable.collect,
    516   find:    Enumerable.detect,
    517   select:  Enumerable.findAll,
    518   member:  Enumerable.include,
    519   entries: Enumerable.toArray
     634        map:     Enumerable.collect,
     635        find:    Enumerable.detect,
     636        select:  Enumerable.findAll,
     637        member:  Enumerable.include,
     638        entries: Enumerable.toArray
    520639});
    521640var $A = Array.from = function(iterable) {
    522   if (!iterable) return [];
    523   if (iterable.toArray) {
    524     return iterable.toArray();
    525   } else {
    526     var results = [];
    527     for (var i = 0, length = iterable.length; i < length; i++)
    528       results.push(iterable[i]);
    529     return results;
    530   }
     641        if (!iterable) return [];
     642        if (iterable.toArray) {
     643                return iterable.toArray();
     644        } else {
     645                var results = [];
     646                for (var i = 0, length = iterable.length; i < length; i++)
     647                        results.push(iterable[i]);
     648                return results;
     649        }
     650}
     651
     652if (Prototype.Browser.WebKit) {
     653        $A = Array.from = function(iterable) {
     654                if (!iterable) return [];
     655                if (!(typeof iterable == 'function' && iterable == '[object NodeList]') &&
     656                        iterable.toArray) {
     657                        return iterable.toArray();
     658                } else {
     659                        var results = [];
     660                        for (var i = 0, length = iterable.length; i < length; i++)
     661                                results.push(iterable[i]);
     662                        return results;
     663                }
     664        }
    531665}
    532666
     
    534668
    535669if (!Array.prototype._reverse)
    536   Array.prototype._reverse = Array.prototype.reverse;
     670        Array.prototype._reverse = Array.prototype.reverse;
    537671
    538672Object.extend(Array.prototype, {
    539   _each: function(iterator) {
    540     for (var i = 0, length = this.length; i < length; i++)
    541       iterator(this[i]);
    542   },
    543 
    544   clear: function() {
    545     this.length = 0;
    546     return this;
    547   },
    548 
    549   first: function() {
    550     return this[0];
    551   },
    552 
    553   last: function() {
    554     return this[this.length - 1];
    555   },
    556 
    557   compact: function() {
    558     return this.select(function(value) {
    559       return value != null;
    560     });
    561   },
    562 
    563   flatten: function() {
    564     return this.inject([], function(array, value) {
    565       return array.concat(value && value.constructor == Array ?
    566         value.flatten() : [value]);
    567     });
    568   },
    569 
    570   without: function() {
    571     var values = $A(arguments);
    572     return this.select(function(value) {
    573       return !values.include(value);
    574     });
    575   },
    576 
    577   indexOf: function(object) {
    578     for (var i = 0, length = this.length; i < length; i++)
    579       if (this[i] == object) return i;
    580     return -1;
    581   },
    582 
    583   reverse: function(inline) {
    584     return (inline !== false ? this : this.toArray())._reverse();
    585   },
    586 
    587   reduce: function() {
    588     return this.length > 1 ? this : this[0];
    589   },
    590 
    591   uniq: function() {
    592     return this.inject([], function(array, value) {
    593       return array.include(value) ? array : array.concat([value]);
    594     });
    595   },
    596 
    597   clone: function() {
    598     return [].concat(this);
    599   },
    600 
    601   size: function() {
    602     return this.length;
    603   },
    604 
    605   inspect: function() {
    606     return '[' + this.map(Object.inspect).join(', ') + ']';
    607   }
     673        _each: function(iterator) {
     674                for (var i = 0, length = this.length; i < length; i++)
     675                        iterator(this[i]);
     676        },
     677
     678        clear: function() {
     679                this.length = 0;
     680                return this;
     681        },
     682
     683        first: function() {
     684                return this[0];
     685        },
     686
     687        last: function() {
     688                return this[this.length - 1];
     689        },
     690
     691        compact: function() {
     692                return this.select(function(value) {
     693                        return value != null;
     694                });
     695        },
     696
     697        flatten: function() {
     698                return this.inject([], function(array, value) {
     699                        return array.concat(value && value.constructor == Array ?
     700                                value.flatten() : [value]);
     701                });
     702        },
     703
     704        without: function() {
     705                var values = $A(arguments);
     706                return this.select(function(value) {
     707                        return !values.include(value);
     708                });
     709        },
     710
     711        indexOf: function(object) {
     712                for (var i = 0, length = this.length; i < length; i++)
     713                        if (this[i] == object) return i;
     714                return -1;
     715        },
     716
     717        reverse: function(inline) {
     718                return (inline !== false ? this : this.toArray())._reverse();
     719        },
     720
     721        reduce: function() {
     722                return this.length > 1 ? this : this[0];
     723        },
     724
     725        uniq: function(sorted) {
     726                return this.inject([], function(array, value, index) {
     727                        if (0 == index || (sorted ? array.last() != value : !array.include(value)))
     728                                array.push(value);
     729                        return array;
     730                });
     731        },
     732
     733        clone: function() {
     734                return [].concat(this);
     735        },
     736
     737        size: function() {
     738                return this.length;
     739        },
     740
     741        inspect: function() {
     742                return '[' + this.map(Object.inspect).join(', ') + ']';
     743        },
     744
     745        toJSON: function() {
     746                var results = [];
     747                this.each(function(object) {
     748                        var value = Object.toJSON(object);
     749                        if (value !== undefined) results.push(value);
     750                });
     751                return '[' + results.join(', ') + ']';
     752        }
    608753});
    609754
    610755Array.prototype.toArray = Array.prototype.clone;
    611756
    612 function $w(string){
    613   string = string.strip();
    614   return string ? string.split(/\s+/) : [];
    615 }
    616 
    617 if(window.opera){
    618   Array.prototype.concat = function(){
    619     var array = [];
    620     for(var i = 0, length = this.length; i < length; i++) array.push(this[i]);
    621     for(var i = 0, length = arguments.length; i < length; i++) {
    622       if(arguments[i].constructor == Array) {
    623         for(var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
    624           array.push(arguments[i][j]);
    625       } else {
    626         array.push(arguments[i]);
    627       }
    628     }
    629     return array;
    630   }
    631 }
    632 var Hash = function(obj) {
    633   Object.extend(this, obj || {});
     757function $w(string) {
     758        string = string.strip();
     759        return string ? string.split(/\s+/) : [];
     760}
     761
     762if (Prototype.Browser.Opera){
     763        Array.prototype.concat = function() {
     764                var array = [];
     765                for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
     766                for (var i = 0, length = arguments.length; i < length; i++) {
     767                        if (arguments[i].constructor == Array) {
     768                                for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
     769                                        array.push(arguments[i][j]);
     770                        } else {
     771                                array.push(arguments[i]);
     772                        }
     773                }
     774                return array;
     775        }
     776}
     777var Hash = function(object) {
     778        if (object instanceof Hash) this.merge(object);
     779        else Object.extend(this, object || {});
    634780};
    635781
    636782Object.extend(Hash, {
    637   toQueryString: function(obj) {
    638     var parts = [];
    639 
    640           this.prototype._each.call(obj, function(pair) {
    641       if (!pair.key) return;
    642 
    643       if (pair.value && pair.value.constructor == Array) {
    644         var values = pair.value.compact();
    645         if (values.length < 2) pair.value = values.reduce();
    646         else {
    647                 key = encodeURIComponent(pair.key);
    648           values.each(function(value) {
    649             value = value != undefined ? encodeURIComponent(value) : '';
    650             parts.push(key + '=' + encodeURIComponent(value));
    651           });
    652           return;
    653         }
    654       }
    655       if (pair.value == undefined) pair[1] = '';
    656       parts.push(pair.map(encodeURIComponent).join('='));
    657           });
    658 
    659     return parts.join('&');
    660   }
     783        toQueryString: function(obj) {
     784                var parts = [];
     785                parts.add = arguments.callee.addPair;
     786
     787                this.prototype._each.call(obj, function(pair) {
     788                        if (!pair.key) return;
     789                        var value = pair.value;
     790
     791                        if (value && typeof value == 'object') {
     792                                if (value.constructor == Array) value.each(function(value) {
     793                                        parts.add(pair.key, value);
     794                                });
     795                                return;
     796                        }
     797                        parts.add(pair.key, value);
     798                });
     799
     800                return parts.join('&');
     801        },
     802
     803        toJSON: function(object) {
     804                var results = [];
     805                this.prototype._each.call(object, function(pair) {
     806                        var value = Object.toJSON(pair.value);
     807                        if (value !== undefined) results.push(pair.key.toJSON() + ': ' + value);
     808                });
     809                return '{' + results.join(', ') + '}';
     810        }
    661811});
     812
     813Hash.toQueryString.addPair = function(key, value, prefix) {
     814        key = encodeURIComponent(key);
     815        if (value === undefined) this.push(key);
     816        else this.push(key + '=' + (value == null ? '' : encodeURIComponent(value)));
     817}
    662818
    663819Object.extend(Hash.prototype, Enumerable);
    664820Object.extend(Hash.prototype, {
    665   _each: function(iterator) {
    666     for (var key in this) {
    667       var value = this[key];
    668       if (value && value == Hash.prototype[key]) continue;
    669 
    670       var pair = [key, value];
    671       pair.key = key;
    672       pair.value = value;
    673       iterator(pair);
    674     }
    675   },
    676 
    677   keys: function() {
    678     return this.pluck('key');
    679   },
    680 
    681   values: function() {
    682     return this.pluck('value');
    683   },
    684 
    685   merge: function(hash) {
    686     return $H(hash).inject(this, function(mergedHash, pair) {
    687       mergedHash[pair.key] = pair.value;
    688       return mergedHash;
    689     });
    690   },
    691 
    692   remove: function() {
    693     var result;
    694     for(var i = 0, length = arguments.length; i < length; i++) {
    695       var value = this[arguments[i]];
    696       if (value !== undefined){
    697         if (result === undefined) result = value;
    698         else {
    699           if (result.constructor != Array) result = [result];
    700           result.push(value)
    701         }
    702       }
    703       delete this[arguments[i]];
    704     }
    705     return result;
    706   },
    707 
    708   toQueryString: function() {
    709     return Hash.toQueryString(this);
    710   },
    711 
    712   inspect: function() {
    713     return '#<Hash:{' + this.map(function(pair) {
    714       return pair.map(Object.inspect).join(': ');
    715     }).join(', ') + '}>';
    716   }
     821        _each: function(iterator) {
     822                for (var key in this) {
     823                        var value = this[key];
     824                        if (value && value == Hash.prototype[key]) continue;
     825
     826                        var pair = [key, value];
     827                        pair.key = key;
     828                        pair.value = value;
     829                        iterator(pair);
     830                }
     831        },
     832
     833        keys: function() {
     834                return this.pluck('key');
     835        },
     836
     837        values: function() {
     838                return this.pluck('value');
     839        },
     840
     841        merge: function(hash) {
     842                return $H(hash).inject(this, function(mergedHash, pair) {
     843                        mergedHash[pair.key] = pair.value;
     844                        return mergedHash;
     845                });
     846        },
     847
     848        remove: function() {
     849                var result;
     850                for(var i = 0, length = arguments.length; i < length; i++) {
     851                        var value = this[arguments[i]];
     852                        if (value !== undefined){
     853                                if (result === undefined) result = value;
     854                                else {
     855                                        if (result.constructor != Array) result = [result];
     856                                        result.push(value)
     857                                }
     858                        }
     859                        delete this[arguments[i]];
     860                }
     861                return result;
     862        },
     863
     864        toQueryString: function() {
     865                return Hash.toQueryString(this);
     866        },
     867
     868        inspect: function() {
     869                return '#<Hash:{' + this.map(function(pair) {
     870                        return pair.map(Object.inspect).join(': ');
     871                }).join(', ') + '}>';
     872        },
     873
     874        toJSON: function() {
     875                return Hash.toJSON(this);
     876        }
    717877});
    718878
    719879function $H(object) {
    720   if (object && object.constructor == Hash) return object;
    721   return new Hash(object);
     880        if (object instanceof Hash) return object;
     881        return new Hash(object);
     882};
     883
     884// Safari iterates over shadowed properties
     885if (function() {
     886        var i = 0, Test = function(value) { this.key = value };
     887        Test.prototype.key = 'foo';
     888        for (var property in new Test('bar')) i++;
     889        return i > 1;
     890}()) Hash.prototype._each = function(iterator) {
     891        var cache = [];
     892        for (var key in this) {
     893                var value = this[key];
     894                if ((value && value == Hash.prototype[key]) || cache.include(key)) continue;
     895                cache.push(key);
     896                var pair = [key, value];
     897                pair.key = key;
     898                pair.value = value;
     899                iterator(pair);
     900        }
    722901};
    723902ObjectRange = Class.create();
    724903Object.extend(ObjectRange.prototype, Enumerable);
    725904Object.extend(ObjectRange.prototype, {
    726   initialize: function(start, end, exclusive) {
    727     this.start = start;
    728     this.end = end;
    729     this.exclusive = exclusive;
    730   },
    731 
    732   _each: function(iterator) {
    733     var value = this.start;
    734     while (this.include(value)) {
    735       iterator(value);
    736       value = value.succ();
    737     }
    738   },
    739 
    740   include: function(value) {
    741     if (value < this.start)
    742       return false;
    743     if (this.exclusive)
    744       return value < this.end;
    745     return value <= this.end;
    746   }
     905        initialize: function(start, end, exclusive) {
     906                this.start = start;
     907                this.end = end;
     908                this.exclusive = exclusive;
     909        },
     910
     911        _each: function(iterator) {
     912                var value = this.start;
     913                while (this.include(value)) {
     914                        iterator(value);
     915                        value = value.succ();
     916                }
     917        },
     918
     919        include: function(value) {
     920                if (value < this.start)
     921                        return false;
     922                if (this.exclusive)
     923                        return value < this.end;
     924                return value <= this.end;
     925        }
    747926});
    748927
    749928var $R = function(start, end, exclusive) {
    750   return new ObjectRange(start, end, exclusive);
     929        return new ObjectRange(start, end, exclusive);
    751930}
    752931
    753932var Ajax = {
    754   getTransport: function() {
    755     return Try.these(
    756       function() {return new XMLHttpRequest()},
    757       function() {return new ActiveXObject('Msxml2.XMLHTTP')},
    758       function() {return new ActiveXObject('Microsoft.XMLHTTP')}
    759     ) || false;
    760   },
    761 
    762   activeRequestCount: 0
     933        getTransport: function() {
     934                return Try.these(
     935                        function() {return new XMLHttpRequest()},
     936                        function() {return new ActiveXObject('Msxml2.XMLHTTP')},
     937                        function() {return new ActiveXObject('Microsoft.XMLHTTP')}
     938                ) || false;
     939        },
     940
     941        activeRequestCount: 0
    763942}
    764943
    765944Ajax.Responders = {
    766   responders: [],
    767 
    768   _each: function(iterator) {
    769     this.responders._each(iterator);
    770   },
    771 
    772   register: function(responder) {
    773     if (!this.include(responder))
    774       this.responders.push(responder);
    775   },
    776 
    777   unregister: function(responder) {
    778     this.responders = this.responders.without(responder);
    779   },
    780 
    781   dispatch: function(callback, request, transport, json) {
    782     this.each(function(responder) {
    783       if (typeof responder[callback] == 'function') {
    784         try {
    785           responder[callback].apply(responder, [request, transport, json]);
    786         } catch (e) {}
    787       }
    788     });
    789   }
     945        responders: [],
     946
     947        _each: function(iterator) {
     948                this.responders._each(iterator);
     949        },
     950
     951        register: function(responder) {
     952                if (!this.include(responder))
     953                        this.responders.push(responder);
     954        },
     955
     956        unregister: function(responder) {
     957                this.responders = this.responders.without(responder);
     958        },
     959
     960        dispatch: function(callback, request, transport, json) {
     961                this.each(function(responder) {
     962                        if (typeof responder[callback] == 'function') {
     963                                try {
     964                                        responder[callback].apply(responder, [request, transport, json]);
     965                                } catch (e) {}
     966                        }
     967                });
     968        }
    790969};
    791970
     
    793972
    794973Ajax.Responders.register({
    795   onCreate: function() {
    796     Ajax.activeRequestCount++;
    797   },
    798   onComplete: function() {
    799     Ajax.activeRequestCount--;
    800   }
     974        onCreate: function() {
     975                Ajax.activeRequestCount++;
     976        },
     977        onComplete: function() {
     978                Ajax.activeRequestCount--;
     979        }
    801980});
    802981
    803982Ajax.Base = function() {};
    804983Ajax.Base.prototype = {
    805   setOptions: function(options) {
    806     this.options = {
    807       method:       'post',
    808       asynchronous: true,
    809       contentType:  'application/x-www-form-urlencoded',
    810       encoding:     'UTF-8',
    811       parameters:   ''
    812     }
    813     Object.extend(this.options, options || {});
    814 
    815     this.options.method = this.options.method.toLowerCase();
    816     if (typeof this.options.parameters == 'string')
    817       this.options.parameters = this.options.parameters.toQueryParams();
    818   }
     984        setOptions: function(options) {
     985                this.options = {
     986                        method:       'post',
     987                        asynchronous: true,
     988                        contentType:  'application/x-www-form-urlencoded',
     989                        encoding:     'UTF-8',
     990                        parameters:   ''
     991                }
     992                Object.extend(this.options, options || {});
     993
     994                this.options.method = this.options.method.toLowerCase();
     995                if (typeof this.options.parameters == 'string')
     996                        this.options.parameters = this.options.parameters.toQueryParams();
     997        }
    819998}
    820999
    8211000Ajax.Request = Class.create();
    8221001Ajax.Request.Events =
    823   ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
     1002        ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
    8241003
    8251004Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
    826   _complete: false,
    827 
    828   initialize: function(url, options) {
    829     this.transport = Ajax.getTransport();
    830     this.setOptions(options);
    831     this.request(url);
    832   },
    833 
    834   request: function(url) {
    835     this.url = url;
    836     this.method = this.options.method;
    837     var params = this.options.parameters;
    838 
    839     if (!['get', 'post'].include(this.method)) {
    840       // simulate other verbs over post
    841       params['_method'] = this.method;
    842       this.method = 'post';
    843     }
    844 
    845     params = Hash.toQueryString(params);
    846     if (params && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) params += '&_='
    847 
    848     // when GET, append parameters to URL
    849     if (this.method == 'get' && params)
    850       this.url += (this.url.indexOf('?') > -1 ? '&' : '?') + params;
    851 
    852     try {
    853       Ajax.Responders.dispatch('onCreate', this, this.transport);
    854 
    855       this.transport.open(this.method.toUpperCase(), this.url,
    856         this.options.asynchronous);
    857 
    858       if (this.options.asynchronous)
    859         setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
    860 
    861       this.transport.onreadystatechange = this.onStateChange.bind(this);
    862       this.setRequestHeaders();
    863 
    864       var body = this.method == 'post' ? (this.options.postBody || params) : null;
    865 
    866       this.transport.send(body);
    867 
    868       /* Force Firefox to handle ready state 4 for synchronous requests */
    869       if (!this.options.asynchronous && this.transport.overrideMimeType)
    870         this.onStateChange();
    871 
    872     }
    873     catch (e) {
    874       this.dispatchException(e);
    875     }
    876   },
    877 
    878   onStateChange: function() {
    879     var readyState = this.transport.readyState;
    880     if (readyState > 1 && !((readyState == 4) && this._complete))
    881       this.respondToReadyState(this.transport.readyState);
    882   },
    883 
    884   setRequestHeaders: function() {
    885     var headers = {
    886       'X-Requested-With': 'XMLHttpRequest',
    887       'X-Prototype-Version': Prototype.Version,
    888       'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
    889     };
    890 
    891     if (this.method == 'post') {
    892       headers['Content-type'] = this.options.contentType +
    893         (this.options.encoding ? '; charset=' + this.options.encoding : '');
    894 
    895       /* Force "Connection: close" for older Mozilla browsers to work
    896        * around a bug where XMLHttpRequest sends an incorrect
    897        * Content-length header. See Mozilla Bugzilla #246651.
    898        */
    899       if (this.transport.overrideMimeType &&
    900           (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
    901             headers['Connection'] = 'close';
    902     }
    903 
    904     // user-defined headers
    905     if (typeof this.options.requestHeaders == 'object') {
    906       var extras = this.options.requestHeaders;
    907 
    908       if (typeof extras.push == 'function')
    909         for (var i = 0, length = extras.length; i < length; i += 2)
    910           headers[extras[i]] = extras[i+1];
    911       else
    912         $H(extras).each(function(pair) { headers[pair.key] = pair.value });
    913     }
    914 
    915     for (var name in headers)
    916       this.transport.setRequestHeader(name, headers[name]);
    917   },
    918 
    919   success: function() {
    920     return !this.transport.status
    921         || (this.transport.status >= 200 && this.transport.status < 300);
    922   },
    923 
    924   respondToReadyState: function(readyState) {
    925     var state = Ajax.Request.Events[readyState];
    926     var transport = this.transport, json = this.evalJSON();
    927 
    928     if (state == 'Complete') {
    929       try {
    930         this._complete = true;
    931         (this.options['on' + this.transport.status]
    932          || this.options['on' + (this.success() ? 'Success' : 'Failure')]
    933          || Prototype.emptyFunction)(transport, json);
    934       } catch (e) {
    935         this.dispatchException(e);
    936       }
    937 
    938       if ((this.getHeader('Content-type') || 'text/javascript').strip().
    939         match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
    940           this.evalResponse();
    941     }
    942 
    943     try {
    944       (this.options['on' + state] || Prototype.emptyFunction)(transport, json);
    945       Ajax.Responders.dispatch('on' + state, this, transport, json);
    946     } catch (e) {
    947       this.dispatchException(e);
    948     }
    949 
    950     if (state == 'Complete') {
    951       // avoid memory leak in MSIE: clean up
    952       this.transport.onreadystatechange = Prototype.emptyFunction;
    953     }
    954   },
    955 
    956   getHeader: function(name) {
    957     try {
    958       return this.transport.getResponseHeader(name);
    959     } catch (e) { return null }
    960   },
    961 
    962   evalJSON: function() {
    963     try {
    964       var json = this.getHeader('X-JSON');
    965       return json ? eval('(' + json + ')') : null;
    966     } catch (e) { return null }
    967   },
    968 
    969   evalResponse: function() {
    970     try {
    971       return eval(this.transport.responseText);
    972     } catch (e) {
    973       this.dispatchException(e);
    974     }
    975   },
    976 
    977   dispatchException: function(exception) {
    978     (this.options.onException || Prototype.emptyFunction)(this, exception);
    979     Ajax.Responders.dispatch('onException', this, exception);
    980   }
     1005        _complete: false,
     1006
     1007        initialize: function(url, options) {
     1008                this.transport = Ajax.getTransport();
     1009                this.setOptions(options);
     1010                this.request(url);
     1011        },
     1012
     1013        request: function(url) {
     1014                this.url = url;
     1015                this.method = this.options.method;
     1016                var params = Object.clone(this.options.parameters);
     1017
     1018                if (!['get', 'post'].include(this.method)) {
     1019                        // simulate other verbs over post
     1020                        params['_method'] = this.method;
     1021                        this.method = 'post';
     1022                }
     1023
     1024                this.parameters = params;
     1025
     1026                if (params = Hash.toQueryString(params)) {
     1027                        // when GET, append parameters to URL
     1028                        if (this.method == 'get')
     1029                                this.url += (this.url.include('?') ? '&' : '?') + params;
     1030                        else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
     1031                                params += '&_=';
     1032                }
     1033
     1034                try {
     1035                        if (this.options.onCreate) this.options.onCreate(this.transport);
     1036                        Ajax.Responders.dispatch('onCreate', this, this.transport);
     1037
     1038                        this.transport.open(this.method.toUpperCase(), this.url,
     1039                                this.options.asynchronous);
     1040
     1041                        if (this.options.asynchronous)
     1042                                setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
     1043
     1044                        this.transport.onreadystatechange = this.onStateChange.bind(this);
     1045                        this.setRequestHeaders();
     1046
     1047                        this.body = this.method == 'post' ? (this.options.postBody || params) : null;
     1048                        this.transport.send(this.body);
     1049
     1050                        /* Force Firefox to handle ready state 4 for synchronous requests */
     1051                        if (!this.options.asynchronous && this.transport.overrideMimeType)
     1052                                this.onStateChange();
     1053
     1054                }
     1055                catch (e) {
     1056                        this.dispatchException(e);
     1057                }
     1058        },
     1059
     1060        onStateChange: function() {
     1061                var readyState = this.transport.readyState;
     1062                if (readyState > 1 && !((readyState == 4) && this._complete))
     1063                        this.respondToReadyState(this.transport.readyState);
     1064        },
     1065
     1066        setRequestHeaders: function() {
     1067                var headers = {
     1068                        'X-Requested-With': 'XMLHttpRequest',
     1069                        'X-Prototype-Version': Prototype.Version,
     1070                        'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
     1071                };
     1072
     1073                if (this.method == 'post') {
     1074                        headers['Content-type'] = this.options.contentType +
     1075                                (this.options.encoding ? '; charset=' + this.options.encoding : '');
     1076
     1077                        /* Force "Connection: close" for older Mozilla browsers to work
     1078                         * around a bug where XMLHttpRequest sends an incorrect
     1079                         * Content-length header. See Mozilla Bugzilla #246651.
     1080                         */
     1081                        if (this.transport.overrideMimeType &&
     1082                                        (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
     1083                                                headers['Connection'] = 'close';
     1084                }
     1085
     1086                // user-defined headers
     1087                if (typeof this.options.requestHeaders == 'object') {
     1088                        var extras = this.options.requestHeaders;
     1089
     1090                        if (typeof extras.push == 'function')
     1091                                for (var i = 0, length = extras.length; i < length; i += 2)
     1092                                        headers[extras[i]] = extras[i+1];
     1093                        else
     1094                                $H(extras).each(function(pair) { headers[pair.key] = pair.value });
     1095                }
     1096
     1097                for (var name in headers)
     1098                        this.transport.setRequestHeader(name, headers[name]);
     1099        },
     1100
     1101        success: function() {
     1102                return !this.transport.status
     1103                                || (this.transport.status >= 200 && this.transport.status < 300);
     1104        },
     1105
     1106        respondToReadyState: function(readyState) {
     1107                var state = Ajax.Request.Events[readyState];
     1108                var transport = this.transport, json = this.evalJSON();
     1109
     1110                if (state == 'Complete') {
     1111                        try {
     1112                                this._complete = true;
     1113                                (this.options['on' + this.transport.status]
     1114                                 || this.options['on' + (this.success() ? 'Success' : 'Failure')]
     1115                                 || Prototype.emptyFunction)(transport, json);
     1116                        } catch (e) {
     1117                                this.dispatchException(e);
     1118                        }
     1119
     1120                        var contentType = this.getHeader('Content-type');
     1121                        if (contentType && contentType.strip().
     1122                                match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
     1123                                        this.evalResponse();
     1124                }
     1125
     1126                try {
     1127                        (this.options['on' + state] || Prototype.emptyFunction)(transport, json);
     1128                        Ajax.Responders.dispatch('on' + state, this, transport, json);
     1129                } catch (e) {
     1130                        this.dispatchException(e);
     1131                }
     1132
     1133                if (state == 'Complete') {
     1134                        // avoid memory leak in MSIE: clean up
     1135                        this.transport.onreadystatechange = Prototype.emptyFunction;
     1136                }
     1137        },
     1138
     1139        getHeader: function(name) {
     1140                try {
     1141                        return this.transport.getResponseHeader(name);
     1142                } catch (e) { return null }
     1143        },
     1144
     1145        evalJSON: function() {
     1146                try {
     1147                        var json = this.getHeader('X-JSON');
     1148                        return json ? json.evalJSON() : null;
     1149                } catch (e) { return null }
     1150        },
     1151
     1152        evalResponse: function() {
     1153                try {
     1154                        return eval((this.transport.responseText || '').unfilterJSON());
     1155                } catch (e) {
     1156                        this.dispatchException(e);
     1157                }
     1158        },
     1159
     1160        dispatchException: function(exception) {
     1161                (this.options.onException || Prototype.emptyFunction)(this, exception);
     1162                Ajax.Responders.dispatch('onException', this, exception);
     1163        }
    9811164});
    9821165
     
    9841167
    9851168Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
    986   initialize: function(container, url, options) {
    987     this.container = {
    988       success: (container.success || container),
    989       failure: (container.failure || (container.success ? null : container))
    990     }
    991 
    992     this.transport = Ajax.getTransport();
    993     this.setOptions(options);
    994 
    995     var onComplete = this.options.onComplete || Prototype.emptyFunction;
    996     this.options.onComplete = (function(transport, param) {
    997       this.updateContent();
    998       onComplete(transport, param);
    999     }).bind(this);
    1000 
    1001     this.request(url);
    1002   },
    1003 
    1004   updateContent: function() {
    1005     var receiver = this.container[this.success() ? 'success' : 'failure'];
    1006     var response = this.transport.responseText;
    1007 
    1008     if (!this.options.evalScripts) response = response.stripScripts();
    1009 
    1010     if (receiver = $(receiver)) {
    1011       if (this.options.insertion)
    1012         new this.options.insertion(receiver, response);
    1013       else
    1014         receiver.update(response);
    1015     }
    1016 
    1017     if (this.success()) {
    1018       if (this.onComplete)
    1019         setTimeout(this.onComplete.bind(this), 10);
    1020     }
    1021   }
     1169        initialize: function(container, url, options) {
     1170                this.container = {
     1171                        success: (container.success || container),
     1172                        failure: (container.failure || (container.success ? null : container))
     1173                }
     1174
     1175                this.transport = Ajax.getTransport();
     1176                this.setOptions(options);
     1177
     1178                var onComplete = this.options.onComplete || Prototype.emptyFunction;
     1179                this.options.onComplete = (function(transport, param) {
     1180                        this.updateContent();
     1181                        onComplete(transport, param);
     1182                }).bind(this);
     1183
     1184                this.request(url);
     1185        },
     1186
     1187        updateContent: function() {
     1188                var receiver = this.container[this.success() ? 'success' : 'failure'];
     1189                var response = this.transport.responseText;
     1190
     1191                if (!this.options.evalScripts) response = response.stripScripts();
     1192
     1193                if (receiver = $(receiver)) {
     1194                        if (this.options.insertion)
     1195                                new this.options.insertion(receiver, response);
     1196                        else
     1197                                receiver.update(response);
     1198                }
     1199
     1200                if (this.success()) {
     1201                        if (this.onComplete)
     1202                                setTimeout(this.onComplete.bind(this), 10);
     1203                }
     1204        }
    10221205});
    10231206
    10241207Ajax.PeriodicalUpdater = Class.create();
    10251208Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
    1026   initialize: function(container, url, options) {
    1027     this.setOptions(options);
    1028     this.onComplete = this.options.onComplete;
    1029 
    1030     this.frequency = (this.options.frequency || 2);
    1031     this.decay = (this.options.decay || 1);
    1032 
    1033     this.updater = {};
    1034     this.container = container;
    1035     this.url = url;
    1036 
    1037     this.start();
    1038   },
    1039 
    1040   start: function() {
    1041     this.options.onComplete = this.updateComplete.bind(this);
    1042     this.onTimerEvent();
    1043   },
    1044 
    1045   stop: function() {
    1046     this.updater.options.onComplete = undefined;
    1047     clearTimeout(this.timer);
    1048     (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
    1049   },
    1050 
    1051   updateComplete: function(request) {
    1052     if (this.options.decay) {
    1053       this.decay = (request.responseText == this.lastText ?
    1054         this.decay * this.options.decay : 1);
    1055 
    1056       this.lastText = request.responseText;
    1057     }
    1058     this.timer = setTimeout(this.onTimerEvent.bind(this),
    1059       this.decay * this.frequency * 1000);
    1060   },
    1061 
    1062   onTimerEvent: function() {
    1063     this.updater = new Ajax.Updater(this.container, this.url, this.options);
    1064   }
     1209        initialize: function(container, url, options) {
     1210                this.setOptions(options);
     1211                this.onComplete = this.options.onComplete;
     1212
     1213                this.frequency = (this.options.frequency || 2);
     1214                this.decay = (this.options.decay || 1);
     1215
     1216                this.updater = {};
     1217                this.container = container;
     1218                this.url = url;
     1219
     1220                this.start();
     1221        },
     1222
     1223        start: function() {
     1224                this.options.onComplete = this.updateComplete.bind(this);
     1225                this.onTimerEvent();
     1226        },
     1227
     1228        stop: function() {
     1229                this.updater.options.onComplete = undefined;
     1230                clearTimeout(this.timer);
     1231                (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
     1232        },
     1233
     1234        updateComplete: function(request) {
     1235                if (this.options.decay) {
     1236                        this.decay = (request.responseText == this.lastText ?
     1237                                this.decay * this.options.decay : 1);
     1238
     1239                        this.lastText = request.responseText;
     1240                }
     1241                this.timer = setTimeout(this.onTimerEvent.bind(this),
     1242                        this.decay * this.frequency * 1000);
     1243        },
     1244
     1245        onTimerEvent: function() {
     1246                this.updater = new Ajax.Updater(this.container, this.url, this.options);
     1247        }
    10651248});
    10661249function $(element) {
    1067   if (arguments.length > 1) {
    1068     for (var i = 0, elements = [], length = arguments.length; i < length; i++)
    1069       elements.push($(arguments[i]));
    1070     return elements;
    1071   }
    1072   if (typeof element == 'string')
    1073     element = document.getElementById(element);
    1074   return Element.extend(element);
     1250        if (arguments.length > 1) {
     1251                for (var i = 0, elements = [], length = arguments.length; i < length; i++)
     1252                        elements.push($(arguments[i]));
     1253                return elements;
     1254        }
     1255        if (typeof element == 'string')
     1256                element = document.getElementById(element);
     1257        return Element.extend(element);
    10751258}
    10761259
    10771260if (Prototype.BrowserFeatures.XPath) {
    1078   document._getElementsByXPath = function(expression, parentElement) {
    1079     var results = [];
    1080     var query = document.evaluate(expression, $(parentElement) || document,
    1081       null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    1082     for (var i = 0, length = query.snapshotLength; i < length; i++)
    1083       results.push(query.snapshotItem(i));
    1084     return results;
    1085   };
    1086 }
    1087 
    1088 document.getElementsByClassName = function(className, parentElement) {
    1089   if (Prototype.BrowserFeatures.XPath) {
    1090     var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
    1091     return document._getElementsByXPath(q, parentElement);
    1092   } else {
    1093     var children = ($(parentElement) || document.body).getElementsByTagName('*');
    1094     var elements = [], child;
    1095     for (var i = 0, length = children.length; i < length; i++) {
    1096       child = children[i];
    1097       if (Element.hasClassName(child, className))
    1098         elements.push(Element.extend(child));
    1099     }
    1100     return elements;
    1101   }
     1261        document._getElementsByXPath = function(expression, parentElement) {
     1262                var results = [];
     1263                var query = document.evaluate(expression, $(parentElement) || document,
     1264                        null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
     1265                for (var i = 0, length = query.snapshotLength; i < length; i++)
     1266                        results.push(query.snapshotItem(i));
     1267                return results;
     1268        };
     1269
     1270        document.getElementsByClassName = function(className, parentElement) {
     1271                var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
     1272                return document._getElementsByXPath(q, parentElement);
     1273        }
     1274
     1275} else document.getElementsByClassName = function(className, parentElement) {
     1276        var children = ($(parentElement) || document.body).getElementsByTagName('*');
     1277        var elements = [], child, pattern = new RegExp("(^|\\s)" + className + "(\\s|$)");
     1278        for (var i = 0, length = children.length; i < length; i++) {
     1279                child = children[i];
     1280                var elementClassName = child.className;
     1281                if (elementClassName.length == 0) continue;
     1282                if (elementClassName == className || elementClassName.match(pattern))
     1283                        elements.push(Element.extend(child));
     1284        }
     1285        return elements;
    11021286};
    11031287
    11041288/*--------------------------------------------------------------------------*/
    11051289
    1106 if (!window.Element)
    1107   var Element = new Object();
     1290if (!window.Element) var Element = {};
    11081291
    11091292Element.extend = function(element) {
    1110   if (!element || _nativeExtensions || element.nodeType == 3) return element;
    1111 
    1112   if (!element._extended && element.tagName && element != window) {
    1113     var methods = Object.clone(Element.Methods), cache = Element.extend.cache;
    1114 
    1115     if (element.tagName == 'FORM')
    1116       Object.extend(methods, Form.Methods);
    1117     if (['INPUT', 'TEXTAREA', 'SELECT'].include(element.tagName))
    1118       Object.extend(methods, Form.Element.Methods);
    1119 
    1120     Object.extend(methods, Element.Methods.Simulated);
    1121 
    1122     for (var property in methods) {
    1123       var value = methods[property];
    1124       if (typeof value == 'function' && !(property in element))
    1125         element[property] = cache.findOrStore(value);
    1126     }
    1127   }
    1128 
    1129   element._extended = true;
    1130   return element;
     1293        var F = Prototype.BrowserFeatures;
     1294        if (!element || !element.tagName || element.nodeType == 3 ||
     1295         element._extended || F.SpecificElementExtensions || element == window)
     1296                return element;
     1297
     1298        var methods = {}, tagName = element.tagName, cache = Element.extend.cache,
     1299         T = Element.Methods.ByTag;
     1300
     1301        // extend methods for all tags (Safari doesn't need this)
     1302        if (!F.ElementExtensions) {
     1303                Object.extend(methods, Element.Methods),
     1304                Object.extend(methods, Element.Methods.Simulated);
     1305        }
     1306
     1307        // extend methods for specific tags
     1308        if (T[tagName]) Object.extend(methods, T[tagName]);
     1309
     1310        for (var property in methods) {
     1311                var value = methods[property];
     1312                if (typeof value == 'function' && !(property in element))
     1313                        element[property] = cache.findOrStore(value);
     1314        }
     1315
     1316        element._extended = Prototype.emptyFunction;
     1317        return element;
    11311318};
    11321319
    11331320Element.extend.cache = {
    1134   findOrStore: function(value) {
    1135     return this[value] = this[value] || function() {
    1136       return value.apply(null, [this].concat($A(arguments)));
    1137     }
    1138   }
     1321        findOrStore: function(value) {
     1322                return this[value] = this[value] || function() {
     1323                        return value.apply(null, [this].concat($A(arguments)));
     1324                }
     1325        }
    11391326};
    11401327
    11411328Element.Methods = {
    1142   visible: function(element) {
    1143     return $(element).style.display != 'none';
    1144   },
    1145 
    1146   toggle: function(element) {
    1147     element = $(element);
    1148     Element[Element.visible(element) ? 'hide' : 'show'](element);
    1149     return element;
    1150   },
    1151 
    1152   hide: function(element) {
    1153     $(element).style.display = 'none';
    1154     return element;
    1155   },
    1156 
    1157   show: function(element) {
    1158     $(element).style.display = '';
    1159     return element;
    1160   },
    1161 
    1162   remove: function(element) {
    1163     element = $(element);
    1164     element.parentNode.removeChild(element);
    1165     return element;
    1166   },
    1167 
    1168   update: function(element, html) {
    1169     html = typeof html == 'undefined' ? '' : html.toString();
    1170     $(element).innerHTML = html.stripScripts();
    1171     setTimeout(function() {html.evalScripts()}, 10);
    1172     return element;
    1173   },
    1174 
    1175   replace: function(element, html) {
    1176     element = $(element);
    1177     html = typeof html == 'undefined' ? '' : html.toString();
    1178     if (element.outerHTML) {
    1179       element.outerHTML = html.stripScripts();
    1180     } else {
    1181       var range = element.ownerDocument.createRange();
    1182       range.selectNodeContents(element);
    1183       element.parentNode.replaceChild(
    1184         range.createContextualFragment(html.stripScripts()), element);
    1185     }
    1186     setTimeout(function() {html.evalScripts()}, 10);
    1187     return element;
    1188   },
    1189 
    1190   inspect: function(element) {
    1191     element = $(element);
    1192     var result = '<' + element.tagName.toLowerCase();
    1193     $H({'id': 'id', 'className': 'class'}).each(function(pair) {
    1194       var property = pair.first(), attribute = pair.last();
    1195       var value = (element[property] || '').toString();
    1196       if (value) result += ' ' + attribute + '=' + value.inspect(true);
    1197     });
    1198     return result + '>';
    1199   },
    1200 
    1201   recursivelyCollect: function(element, property) {
    1202     element = $(element);
    1203     var elements = [];
    1204     while (element = element[property])
    1205       if (element.nodeType == 1)
    1206         elements.push(Element.extend(element));
    1207     return elements;
    1208   },
    1209 
    1210   ancestors: function(element) {
    1211     return $(element).recursivelyCollect('parentNode');
    1212   },
    1213 
    1214   descendants: function(element) {
    1215     return $A($(element).getElementsByTagName('*'));
    1216   },
    1217 
    1218   immediateDescendants: function(element) {
    1219     if (!(element = $(element).firstChild)) return [];
    1220     while (element && element.nodeType != 1) element = element.nextSibling;
    1221     if (element) return [element].concat($(element).nextSiblings());
    1222     return [];
    1223   },
    1224 
    1225   previousSiblings: function(element) {
    1226     return $(element).recursivelyCollect('previousSibling');
    1227   },
    1228 
    1229   nextSiblings: function(element) {
    1230     return $(element).recursivelyCollect('nextSibling');
    1231   },
    1232 
    1233   siblings: function(element) {
    1234     element = $(element);
    1235     return element.previousSiblings().reverse().concat(element.nextSiblings());
    1236   },
    1237 
    1238   match: function(element, selector) {
    1239     if (typeof selector == 'string')
    1240       selector = new Selector(selector);
    1241     return selector.match($(element));
    1242   },
    1243 
    1244   up: function(element, expression, index) {
    1245     return Selector.findElement($(element).ancestors(), expression, index);
    1246   },
    1247 
    1248   down: function(element, expression, index) {
    1249     return Selector.findElement($(element).descendants(), expression, index);
    1250   },
    1251 
    1252   previous: function(element, expression, index) {
    1253     return Selector.findElement($(element).previousSiblings(), expression, index);
    1254   },
    1255 
    1256   next: function(element, expression, index) {
    1257     return Selector.findElement($(element).nextSiblings(), expression, index);
    1258   },
    1259 
    1260   getElementsBySelector: function() {
    1261     var args = $A(arguments), element = $(args.shift());
    1262     return Selector.findChildElements(element, args);
    1263   },
    1264 
    1265   getElementsByClassName: function(element, className) {
    1266     return document.getElementsByClassName(className, element);
    1267   },
    1268 
    1269   readAttribute: function(element, name) {
    1270     element = $(element);
    1271     if (document.all && !window.opera) {
    1272       var t = Element._attributeTranslations;
    1273       if (t.values[name]) return t.values[name](element, name);
    1274       if (t.names[name])  name = t.names[name];
    1275       var attribute = element.attributes[name];
    1276       if(attribute) return attribute.nodeValue;
    1277     }
    1278     return element.getAttribute(name);
    1279   },
    1280 
    1281   getHeight: function(element) {
    1282     return $(element).getDimensions().height;
    1283   },
    1284 
    1285   getWidth: function(element) {
    1286     return $(element).getDimensions().width;
    1287   },
    1288 
    1289   classNames: function(element) {
    1290     return new Element.ClassNames(element);
    1291   },
    1292 
    1293   hasClassName: function(element, className) {
    1294     if (!(element = $(element))) return;
    1295     var elementClassName = element.className;
    1296     if (elementClassName.length == 0) return false;
    1297     if (elementClassName == className ||
    1298         elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
    1299       return true;
    1300     return false;
    1301   },
    1302 
    1303   addClassName: function(element, className) {
    1304     if (!(element = $(element))) return;
    1305     Element.classNames(element).add(className);
    1306     return element;
    1307   },
    1308 
    1309   removeClassName: function(element, className) {
    1310     if (!(element = $(element))) return;
    1311     Element.classNames(element).remove(className);
    1312     return element;
    1313   },
    1314 
    1315   toggleClassName: function(element, className) {
    1316     if (!(element = $(element))) return;
    1317     Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
    1318     return element;
    1319   },
    1320 
    1321   observe: function() {
    1322     Event.observe.apply(Event, arguments);
    1323     return $A(arguments).first();
    1324   },
    1325 
    1326   stopObserving: function() {
    1327     Event.stopObserving.apply(Event, arguments);
    1328     return $A(arguments).first();
    1329   },
    1330 
    1331   // removes whitespace-only text node children
    1332   cleanWhitespace: function(element) {
    1333     element = $(element);
    1334     var node = element.firstChild;
    1335     while (node) {
    1336       var nextNode = node.nextSibling;
    1337       if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
    1338         element.removeChild(node);
    1339       node = nextNode;
    1340     }
    1341     return element;
    1342   },
    1343 
    1344   empty: function(element) {
    1345     return $(element).innerHTML.match(/^\s*$/);
    1346   },
    1347 
    1348   descendantOf: function(element, ancestor) {
    1349     element = $(element), ancestor = $(ancestor);
    1350     while (element = element.parentNode)
    1351       if (element == ancestor) return true;
    1352     return false;
    1353   },
    1354 
    1355   scrollTo: function(element) {
    1356     element = $(element);
    1357     var pos = Position.cumulativeOffset(element);
    1358     window.scrollTo(pos[0], pos[1]);
    1359     return element;
    1360   },
    1361 
    1362   getStyle: function(element, style) {
    1363     element = $(element);
    1364     if (['float','cssFloat'].include(style))
    1365       style = (typeof element.style.styleFloat != 'undefined' ? 'styleFloat' : 'cssFloat');
    1366     style = style.camelize();
    1367     var value = element.style[style];
    1368     if (!value) {
    1369       if (document.defaultView && document.defaultView.getComputedStyle) {
    1370         var css = document.defaultView.getComputedStyle(element, null);
    1371         value = css ? css[style] : null;
    1372       } else if (element.currentStyle) {
    1373         value = element.currentStyle[style];
    1374       }
    1375     }
    1376 
    1377     if((value == 'auto') && ['width','height'].include(style) && (element.getStyle('display') != 'none'))
    1378       value = element['offset'+style.capitalize()] + 'px';
    1379 
    1380     if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
    1381       if (Element.getStyle(element, 'position') == 'static') value = 'auto';
    1382     if(style == 'opacity') {
    1383       if(value) return parseFloat(value);
    1384       if(value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
    1385         if(value[1]) return parseFloat(value[1]) / 100;
    1386       return 1.0;
    1387     }
    1388     return value == 'auto' ? null : value;
    1389   },
    1390 
    1391   setStyle: function(element, style) {
    1392     element = $(element);
    1393     for (var name in style) {
    1394       var value = style[name];
    1395       if(name == 'opacity') {
    1396         if (value == 1) {
    1397           value = (/Gecko/.test(navigator.userAgent) &&
    1398             !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 0.999999 : 1.0;
    1399           if(/MSIE/.test(navigator.userAgent) && !window.opera)
    1400             element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
    1401         } else if(value == '') {
    1402           if(/MSIE/.test(navigator.userAgent) && !window.opera)
    1403             element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
    1404         } else {
    1405           if(value < 0.00001) value = 0;
    1406           if(/MSIE/.test(navigator.userAgent) && !window.opera)
    1407             element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') +
    1408               'alpha(opacity='+value*100+')';
    1409         }
    1410       } else if(['float','cssFloat'].include(name)) name = (typeof element.style.styleFloat != 'undefined') ? 'styleFloat' : 'cssFloat';
    1411       element.style[name.camelize()] = value;
    1412     }
    1413     return element;
    1414   },
    1415 
    1416   getDimensions: function(element) {
    1417     element = $(element);
    1418     var display = $(element).getStyle('display');
    1419     if (display != 'none' && display != null) // Safari bug
    1420       return {width: element.offsetWidth, height: element.offsetHeight};
    1421 
    1422     // All *Width and *Height properties give 0 on elements with display none,
    1423     // so enable the element temporarily
    1424     var els = element.style;
    1425     var originalVisibility = els.visibility;
    1426     var originalPosition = els.position;
    1427     var originalDisplay = els.display;
    1428     els.visibility = 'hidden';
    1429     els.position = 'absolute';
    1430     els.display = 'block';
    1431     var originalWidth = element.clientWidth;
    1432     var originalHeight = element.clientHeight;
    1433     els.display = originalDisplay;
    1434     els.position = originalPosition;
    1435     els.visibility = originalVisibility;
    1436     return {width: originalWidth, height: originalHeight};
    1437   },
    1438 
    1439   makePositioned: function(element) {
    1440     element = $(element);
    1441     var pos = Element.getStyle(element, 'position');
    1442     if (pos == 'static' || !pos) {
    1443       element._madePositioned = true;
    1444       element.style.position = 'relative';
    1445       // Opera returns the offset relative to the positioning context, when an
    1446       // element is position relative but top and left have not been defined
    1447       if (window.opera) {
    1448         element.style.top = 0;
    1449         element.style.left = 0;
    1450       }
    1451     }
    1452     return element;
    1453   },
    1454 
    1455   undoPositioned: function(element) {
    1456     element = $(element);
    1457     if (element._madePositioned) {
    1458       element._madePositioned = undefined;
    1459       element.style.position =
    1460         element.style.top =
    1461         element.style.left =
    1462         element.style.bottom =
    1463         element.style.right = '';
    1464     }
    1465     return element;
    1466   },
    1467 
    1468   makeClipping: function(element) {
    1469     element = $(element);
    1470     if (element._overflow) return element;
    1471     element._overflow = element.style.overflow || 'auto';
    1472     if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
    1473       element.style.overflow = 'hidden';
    1474     return element;
    1475   },
    1476 
    1477   undoClipping: function(element) {
    1478     element = $(element);
    1479     if (!element._overflow) return element;
    1480     element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
    1481     element._overflow = null;
    1482     return element;
    1483   }
     1329        visible: function(element) {
     1330                return $(element).style.display != 'none';
     1331        },
     1332
     1333        toggle: function(element) {
     1334                element = $(element);
     1335                Element[Element.visible(element) ? 'hide' : 'show'](element);
     1336                return element;
     1337        },
     1338
     1339        hide: function(element) {
     1340                $(element).style.display = 'none';
     1341                return element;
     1342        },
     1343
     1344        show: function(element) {
     1345                $(element).style.display = '';
     1346                return element;
     1347        },
     1348
     1349        remove: function(element) {
     1350                element = $(element);
     1351                element.parentNode.removeChild(element);
     1352                return element;
     1353        },
     1354
     1355        update: function(element, html) {
     1356                html = typeof html == 'undefined' ? '' : html.toString();
     1357                $(element).innerHTML = html.stripScripts();
     1358                setTimeout(function() {html.evalScripts()}, 10);
     1359                return element;
     1360        },
     1361
     1362        replace: function(element, html) {
     1363                element = $(element);
     1364                html = typeof html == 'undefined' ? '' : html.toString();
     1365                if (element.outerHTML) {
     1366                        element.outerHTML = html.stripScripts();
     1367                } else {
     1368                        var range = element.ownerDocument.createRange();
     1369                        range.selectNodeContents(element);
     1370                        element.parentNode.replaceChild(
     1371                                range.createContextualFragment(html.stripScripts()), element);
     1372                }
     1373                setTimeout(function() {html.evalScripts()}, 10);
     1374                return element;
     1375        },
     1376
     1377        inspect: function(element) {
     1378                element = $(element);
     1379                var result = '<' + element.tagName.toLowerCase();
     1380                $H({'id': 'id', 'className': 'class'}).each(function(pair) {
     1381                        var property = pair.first(), attribute = pair.last();
     1382                        var value = (element[property] || '').toString();
     1383                        if (value) result += ' ' + attribute + '=' + value.inspect(true);
     1384                });
     1385                return result + '>';
     1386        },
     1387
     1388        recursivelyCollect: function(element, property) {
     1389                element = $(element);
     1390                var elements = [];
     1391                while (element = element[property])
     1392                        if (element.nodeType == 1)
     1393                                elements.push(Element.extend(element));
     1394                return elements;
     1395        },
     1396
     1397        ancestors: function(element) {
     1398                return $(element).recursivelyCollect('parentNode');
     1399        },
     1400
     1401        descendants: function(element) {
     1402                return $A($(element).getElementsByTagName('*')).each(Element.extend);
     1403        },
     1404
     1405        firstDescendant: function(element) {
     1406                element = $(element).firstChild;
     1407                while (element && element.nodeType != 1) element = element.nextSibling;
     1408                return $(element);
     1409        },
     1410
     1411        immediateDescendants: function(element) {
     1412                if (!(element = $(element).firstChild)) return [];
     1413                while (element && element.nodeType != 1) element = element.nextSibling;
     1414                if (element) return [element].concat($(element).nextSiblings());
     1415                return [];
     1416        },
     1417
     1418        previousSiblings: function(element) {
     1419                return $(element).recursivelyCollect('previousSibling');
     1420        },
     1421
     1422        nextSiblings: function(element) {
     1423                return $(element).recursivelyCollect('nextSibling');
     1424        },
     1425
     1426        siblings: function(element) {
     1427                element = $(element);
     1428                return element.previousSiblings().reverse().concat(element.nextSiblings());
     1429        },
     1430
     1431        match: function(element, selector) {
     1432                if (typeof selector == 'string')
     1433                        selector = new Selector(selector);
     1434                return selector.match($(element));
     1435        },
     1436
     1437        up: function(element, expression, index) {
     1438                element = $(element);
     1439                if (arguments.length == 1) return $(element.parentNode);
     1440                var ancestors = element.ancestors();
     1441                return expression ? Selector.findElement(ancestors, expression, index) :
     1442                        ancestors[index || 0];
     1443        },
     1444
     1445        down: function(element, expression, index) {
     1446                element = $(element);
     1447                if (arguments.length == 1) return element.firstDescendant();
     1448                var descendants = element.descendants();
     1449                return expression ? Selector.findElement(descendants, expression, index) :
     1450                        descendants[index || 0];
     1451        },
     1452
     1453        previous: function(element, expression, index) {
     1454                element = $(element);
     1455                if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
     1456                var previousSiblings = element.previousSiblings();
     1457                return expression ? Selector.findElement(previousSiblings, expression, index) :
     1458                        previousSiblings[index || 0];
     1459        },
     1460
     1461        next: function(element, expression, index) {
     1462                element = $(element);
     1463                if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
     1464                var nextSiblings = element.nextSiblings();
     1465                return expression ? Selector.findElement(nextSiblings, expression, index) :
     1466                        nextSiblings[index || 0];
     1467        },
     1468
     1469        getElementsBySelector: function() {
     1470                var args = $A(arguments), element = $(args.shift());
     1471                return Selector.findChildElements(element, args);
     1472        },
     1473
     1474        getElementsByClassName: function(element, className) {
     1475                return document.getElementsByClassName(className, element);
     1476        },
     1477
     1478        readAttribute: function(element, name) {
     1479                element = $(element);
     1480                if (Prototype.Browser.IE) {
     1481                        if (!element.attributes) return null;
     1482                        var t = Element._attributeTranslations;
     1483                        if (t.values[name]) return t.values[name](element, name);
     1484                        if (t.names[name])  name = t.names[name];
     1485                        var attribute = element.attributes[name];
     1486                        return attribute ? attribute.nodeValue : null;
     1487                }
     1488                return element.getAttribute(name);
     1489        },
     1490
     1491        getHeight: function(element) {
     1492                return $(element).getDimensions().height;
     1493        },
     1494
     1495        getWidth: function(element) {
     1496                return $(element).getDimensions().width;
     1497        },
     1498
     1499        classNames: function(element) {
     1500                return new Element.ClassNames(element);
     1501        },
     1502
     1503        hasClassName: function(element, className) {
     1504                if (!(element = $(element))) return;
     1505                var elementClassName = element.className;
     1506                if (elementClassName.length == 0) return false;
     1507                if (elementClassName == className ||
     1508                                elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
     1509                        return true;
     1510                return false;
     1511        },
     1512
     1513        addClassName: function(element, className) {
     1514                if (!(element = $(element))) return;
     1515                Element.classNames(element).add(className);
     1516                return element;
     1517        },
     1518
     1519        removeClassName: function(element, className) {
     1520                if (!(element = $(element))) return;
     1521                Element.classNames(element).remove(className);
     1522                return element;
     1523        },
     1524
     1525        toggleClassName: function(element, className) {
     1526                if (!(element = $(element))) return;
     1527                Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
     1528                return element;
     1529        },
     1530
     1531        observe: function() {
     1532                Event.observe.apply(Event, arguments);
     1533                return $A(arguments).first();
     1534        },
     1535
     1536        stopObserving: function() {
     1537                Event.stopObserving.apply(Event, arguments);
     1538                return $A(arguments).first();
     1539        },
     1540
     1541        // removes whitespace-only text node children
     1542        cleanWhitespace: function(element) {
     1543                element = $(element);
     1544                var node = element.firstChild;
     1545                while (node) {
     1546                        var nextNode = node.nextSibling;
     1547                        if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
     1548                                element.removeChild(node);
     1549                        node = nextNode;
     1550                }
     1551                return element;
     1552        },
     1553
     1554        empty: function(element) {
     1555                return $(element).innerHTML.blank();
     1556        },
     1557
     1558        descendantOf: function(element, ancestor) {
     1559                element = $(element), ancestor = $(ancestor);
     1560                while (element = element.parentNode)
     1561                        if (element == ancestor) return true;
     1562                return false;
     1563        },
     1564
     1565        scrollTo: function(element) {
     1566                element = $(element);
     1567                var pos = Position.cumulativeOffset(element);
     1568                window.scrollTo(pos[0], pos[1]);
     1569                return element;
     1570        },
     1571
     1572        getStyle: function(element, style) {
     1573                element = $(element);
     1574                style = style == 'float' ? 'cssFloat' : style.camelize();
     1575                var value = element.style[style];
     1576                if (!value) {
     1577                        var css = document.defaultView.getComputedStyle(element, null);
     1578                        value = css ? css[style] : null;
     1579                }
     1580                if (style == 'opacity') return value ? parseFloat(value) : 1.0;
     1581                return value == 'auto' ? null : value;
     1582        },
     1583
     1584        getOpacity: function(element) {
     1585                return $(element).getStyle('opacity');
     1586        },
     1587
     1588        setStyle: function(element, styles, camelized) {
     1589                element = $(element);
     1590                var elementStyle = element.style;
     1591
     1592                for (var property in styles)
     1593                        if (property == 'opacity') element.setOpacity(styles[property])
     1594                        else
     1595                                elementStyle[(property == 'float' || property == 'cssFloat') ?
     1596                                        (elementStyle.styleFloat === undefined ? 'cssFloat' : 'styleFloat') :
     1597                                        (camelized ? property : property.camelize())] = styles[property];
     1598
     1599                return element;
     1600        },
     1601
     1602        setOpacity: function(element, value) {
     1603                element = $(element);
     1604                element.style.opacity = (value == 1 || value === '') ? '' :
     1605                        (value < 0.00001) ? 0 : value;
     1606                return element;
     1607        },
     1608
     1609        getDimensions: function(element) {
     1610                element = $(element);
     1611                var display = $(element).getStyle('display');
     1612                if (display != 'none' && display != null) // Safari bug
     1613                        return {width: element.offsetWidth, height: element.offsetHeight};
     1614
     1615                // All *Width and *Height properties give 0 on elements with display none,
     1616                // so enable the element temporarily
     1617                var els = element.style;
     1618                var originalVisibility = els.visibility;
     1619                var originalPosition = els.position;
     1620                var originalDisplay = els.display;
     1621                els.visibility = 'hidden';
     1622                els.position = 'absolute';
     1623                els.display = 'block';
     1624                var originalWidth = element.clientWidth;
     1625                var originalHeight = element.clientHeight;
     1626                els.display = originalDisplay;
     1627                els.position = originalPosition;
     1628                els.visibility = originalVisibility;
     1629                return {width: originalWidth, height: originalHeight};
     1630        },
     1631
     1632        makePositioned: function(element) {
     1633                element = $(element);
     1634                var pos = Element.getStyle(element, 'position');
     1635                if (pos == 'static' || !pos) {
     1636                        element._madePositioned = true;
     1637                        element.style.position = 'relative';
     1638                        // Opera returns the offset relative to the positioning context, when an
     1639                        // element is position relative but top and left have not been defined
     1640                        if (window.opera) {
     1641                                element.style.top = 0;
     1642                                element.style.left = 0;
     1643                        }
     1644                }
     1645                return element;
     1646        },
     1647
     1648        undoPositioned: function(element) {
     1649                element = $(element);
     1650                if (element._madePositioned) {
     1651                        element._madePositioned = undefined;
     1652                        element.style.position =
     1653                                element.style.top =
     1654                                element.style.left =
     1655                                element.style.bottom =
     1656                                element.style.right = '';
     1657                }
     1658                return element;
     1659        },
     1660
     1661        makeClipping: function(element) {
     1662                element = $(element);
     1663                if (element._overflow) return element;
     1664                element._overflow = element.style.overflow || 'auto';
     1665                if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
     1666                        element.style.overflow = 'hidden';
     1667                return element;
     1668        },
     1669
     1670        undoClipping: function(element) {
     1671                element = $(element);
     1672                if (!element._overflow) return element;
     1673                element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
     1674                element._overflow = null;
     1675                return element;
     1676        }
    14841677};
    14851678
    1486 Object.extend(Element.Methods, {childOf: Element.Methods.descendantOf});
    1487 
    1488 Element._attributeTranslations = {};
    1489 
    1490 Element._attributeTranslations.names = {
    1491   colspan:   "colSpan",
    1492   rowspan:   "rowSpan",
    1493   valign:    "vAlign",
    1494   datetime:  "dateTime",
    1495   accesskey: "accessKey",
    1496   tabindex:  "tabIndex",
    1497   enctype:   "encType",
    1498   maxlength: "maxLength",
    1499   readonly:  "readOnly",
    1500   longdesc:  "longDesc"
     1679Object.extend(Element.Methods, {
     1680        childOf: Element.Methods.descendantOf,
     1681        childElements: Element.Methods.immediateDescendants
     1682});
     1683
     1684if (Prototype.Browser.Opera) {
     1685        Element.Methods._getStyle = Element.Methods.getStyle;
     1686        Element.Methods.getStyle = function(element, style) {
     1687                switch(style) {
     1688                        case 'left':
     1689                        case 'top':
     1690                        case 'right':
     1691                        case 'bottom':
     1692                                if (Element._getStyle(element, 'position') == 'static') return null;
     1693                        default: return Element._getStyle(element, style);
     1694                }
     1695        };
     1696}
     1697else if (Prototype.Browser.IE) {
     1698        Element.Methods.getStyle = function(element, style) {
     1699                element = $(element);
     1700                style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
     1701                var value = element.style[style];
     1702                if (!value && element.currentStyle) value = element.currentStyle[style];
     1703
     1704                if (style == 'opacity') {
     1705                        if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
     1706                                if (value[1]) return parseFloat(value[1]) / 100;
     1707                        return 1.0;
     1708                }
     1709
     1710                if (value == 'auto') {
     1711                        if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
     1712                                return element['offset'+style.capitalize()] + 'px';
     1713                        return null;
     1714                }
     1715                return value;
     1716        };
     1717
     1718        Element.Methods.setOpacity = function(element, value) {
     1719                element = $(element);
     1720                var filter = element.getStyle('filter'), style = element.style;
     1721                if (value == 1 || value === '') {
     1722                        style.filter = filter.replace(/alpha\([^\)]*\)/gi,'');
     1723                        return element;
     1724                } else if (value < 0.00001) value = 0;
     1725                style.filter = filter.replace(/alpha\([^\)]*\)/gi, '') +
     1726                        'alpha(opacity=' + (value * 100) + ')';
     1727                return element;
     1728        };
     1729
     1730        // IE is missing .innerHTML support for TABLE-related elements
     1731        Element.Methods.update = function(element, html) {
     1732                element = $(element);
     1733                html = typeof html == 'undefined' ? '' : html.toString();
     1734                var tagName = element.tagName.toUpperCase();
     1735                if (['THEAD','TBODY','TR','TD'].include(tagName)) {
     1736                        var div = document.createElement('div');
     1737                        switch (tagName) {
     1738                                case 'THEAD':
     1739                                case 'TBODY':
     1740                                        div.innerHTML = '<table><tbody>' +  html.stripScripts() + '</tbody></table>';
     1741                                        depth = 2;
     1742                                        break;
     1743                                case 'TR':
     1744                                        div.innerHTML = '<table><tbody><tr>' +  html.stripScripts() + '</tr></tbody></table>';
     1745                                        depth = 3;
     1746                                        break;
     1747                                case 'TD':
     1748                                        div.innerHTML = '<table><tbody><tr><td>' +  html.stripScripts() + '</td></tr></tbody></table>';
     1749                                        depth = 4;
     1750                        }
     1751                        $A(element.childNodes).each(function(node) { element.removeChild(node) });
     1752                        depth.times(function() { div = div.firstChild });
     1753                        $A(div.childNodes).each(function(node) { element.appendChild(node) });
     1754                } else {
     1755                        element.innerHTML = html.stripScripts();
     1756                }
     1757                setTimeout(function() { html.evalScripts() }, 10);
     1758                return element;
     1759        }
     1760}
     1761else if (Prototype.Browser.Gecko) {
     1762        Element.Methods.setOpacity = function(element, value) {
     1763                element = $(element);
     1764                element.style.opacity = (value == 1) ? 0.999999 :
     1765                        (value === '') ? '' : (value < 0.00001) ? 0 : value;
     1766                return element;
     1767        };
     1768}
     1769
     1770Element._attributeTranslations = {
     1771        names: {
     1772                colspan:   "colSpan",
     1773                rowspan:   "rowSpan",
     1774                valign:    "vAlign",
     1775                datetime:  "dateTime",
     1776                accesskey: "accessKey",
     1777                tabindex:  "tabIndex",
     1778                enctype:   "encType",
     1779                maxlength: "maxLength",
     1780                readonly:  "readOnly",
     1781                longdesc:  "longDesc"
     1782        },
     1783        values: {
     1784                _getAttr: function(element, attribute) {
     1785                        return element.getAttribute(attribute, 2);
     1786                },
     1787                _flag: function(element, attribute) {
     1788                        return $(element).hasAttribute(attribute) ? attribute : null;
     1789                },
     1790                style: function(element) {
     1791                        return element.style.cssText.toLowerCase();
     1792                },
     1793                title: function(element) {
     1794                        var node = element.getAttributeNode('title');
     1795                        return node.specified ? node.nodeValue : null;
     1796                }
     1797        }
    15011798};
    15021799
    1503 Element._attributeTranslations.values = {
    1504   _getAttr: function(element, attribute) {
    1505     return element.getAttribute(attribute, 2);
    1506   },
    1507 
    1508   _flag: function(element, attribute) {
    1509     return $(element).hasAttribute(attribute) ? attribute : null;
    1510   },
    1511 
    1512   style: function(element) {
    1513     return element.style.cssText.toLowerCase();
    1514   },
    1515 
    1516   title: function(element) {
    1517     var node = element.getAttributeNode('title');
    1518     return node.specified ? node.nodeValue : null;
    1519   }
     1800(function() {
     1801        Object.extend(this, {
     1802                href: this._getAttr,
     1803                src:  this._getAttr,
     1804                type: this._getAttr,
     1805                disabled: this._flag,
     1806                checked:  this._flag,
     1807                readonly: this._flag,
     1808                multiple: this._flag
     1809        });
     1810}).call(Element._attributeTranslations.values);
     1811
     1812Element.Methods.Simulated = {
     1813        hasAttribute: function(element, attribute) {
     1814                var t = Element._attributeTranslations, node;
     1815                attribute = t.names[attribute] || attribute;
     1816                node = $(element).getAttributeNode(attribute);
     1817                return node && node.specified;
     1818        }
    15201819};
    15211820
    1522 Object.extend(Element._attributeTranslations.values, {
    1523   href: Element._attributeTranslations.values._getAttr,
    1524   src:  Element._attributeTranslations.values._getAttr,
    1525   disabled: Element._attributeTranslations.values._flag,
    1526   checked:  Element._attributeTranslations.values._flag,
    1527   readonly: Element._attributeTranslations.values._flag,
    1528   multiple: Element._attributeTranslations.values._flag
    1529 });
    1530 
    1531 Element.Methods.Simulated = {
    1532   hasAttribute: function(element, attribute) {
    1533     var t = Element._attributeTranslations;
    1534     attribute = t.names[attribute] || attribute;
    1535     return $(element).getAttributeNode(attribute).specified;
    1536   }
     1821Element.Methods.ByTag = {};
     1822
     1823Object.extend(Element, Element.Methods);
     1824
     1825if (!Prototype.BrowserFeatures.ElementExtensions &&
     1826 document.createElement('div').__proto__) {
     1827        window.HTMLElement = {};
     1828        window.HTMLElement.prototype = document.createElement('div').__proto__;
     1829        Prototype.BrowserFeatures.ElementExtensions = true;
     1830}
     1831
     1832Element.hasAttribute = function(element, attribute) {
     1833        if (element.hasAttribute) return element.hasAttribute(attribute);
     1834        return Element.Methods.Simulated.hasAttribute(element, attribute);
    15371835};
    15381836
    1539 // IE is missing .innerHTML support for TABLE-related elements
    1540 if (document.all && !window.opera){
    1541   Element.Methods.update = function(element, html) {
    1542     element = $(element);
    1543     html = typeof html == 'undefined' ? '' : html.toString();
    1544     var tagName = element.tagName.toUpperCase();
    1545     if (['THEAD','TBODY','TR','TD'].include(tagName)) {
    1546       var div = document.createElement('div');
    1547       switch (tagName) {
    1548         case 'THEAD':
    1549         case 'TBODY':
    1550           div.innerHTML = '<table><tbody>' +  html.stripScripts() + '</tbody></table>';
    1551           depth = 2;
    1552           break;
    1553         case 'TR':
    1554           div.innerHTML = '<table><tbody><tr>' +  html.stripScripts() + '</tr></tbody></table>';
    1555           depth = 3;
    1556           break;
    1557         case 'TD':
    1558           div.innerHTML = '<table><tbody><tr><td>' +  html.stripScripts() + '</td></tr></tbody></table>';
    1559           depth = 4;
    1560       }
    1561       $A(element.childNodes).each(function(node){
    1562         element.removeChild(node)
    1563       });
    1564       depth.times(function(){ div = div.firstChild });
    1565 
    1566       $A(div.childNodes).each(
    1567         function(node){ element.appendChild(node) });
    1568     } else {
    1569       element.innerHTML = html.stripScripts();
    1570     }
    1571     setTimeout(function() {html.evalScripts()}, 10);
    1572     return element;
    1573   }
     1837Element.addMethods = function(methods) {
     1838        var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
     1839
     1840        if (!methods) {
     1841                Object.extend(Form, Form.Methods);
     1842                Object.extend(Form.Element, Form.Element.Methods);
     1843                Object.extend(Element.Methods.ByTag, {
     1844                        "FORM":     Object.clone(Form.Methods),
     1845                        "INPUT":    Object.clone(Form.Element.Methods),
     1846                        "SELECT":   Object.clone(Form.Element.Methods),
     1847                        "TEXTAREA": Object.clone(Form.Element.Methods)
     1848                });
     1849        }
     1850
     1851        if (arguments.length == 2) {
     1852                var tagName = methods;
     1853                methods = arguments[1];
     1854        }
     1855
     1856        if (!tagName) Object.extend(Element.Methods, methods || {});
     1857        else {
     1858                if (tagName.constructor == Array) tagName.each(extend);
     1859                else extend(tagName);
     1860        }
     1861
     1862        function extend(tagName) {
     1863                tagName = tagName.toUpperCase();
     1864                if (!Element.Methods.ByTag[tagName])
     1865                        Element.Methods.ByTag[tagName] = {};
     1866                Object.extend(Element.Methods.ByTag[tagName], methods);
     1867        }
     1868
     1869        function copy(methods, destination, onlyIfAbsent) {
     1870                onlyIfAbsent = onlyIfAbsent || false;
     1871                var cache = Element.extend.cache;
     1872                for (var property in methods) {
     1873                        var value = methods[property];
     1874                        if (!onlyIfAbsent || !(property in destination))
     1875                                destination[property] = cache.findOrStore(value);
     1876                }
     1877        }
     1878
     1879        function findDOMClass(tagName) {
     1880                var klass;
     1881                var trans = {
     1882                        "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
     1883                        "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
     1884                        "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
     1885                        "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
     1886                        "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
     1887                        "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
     1888                        "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
     1889                        "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
     1890                        "FrameSet", "IFRAME": "IFrame"
     1891                };
     1892                if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
     1893                if (window[klass]) return window[klass];
     1894                klass = 'HTML' + tagName + 'Element';
     1895                if (window[klass]) return window[klass];
     1896                klass = 'HTML' + tagName.capitalize() + 'Element';
     1897                if (window[klass]) return window[klass];
     1898
     1899                window[klass] = {};
     1900                window[klass].prototype = document.createElement(tagName).__proto__;
     1901                return window[klass];
     1902        }
     1903
     1904        if (F.ElementExtensions) {
     1905                copy(Element.Methods, HTMLElement.prototype);
     1906                copy(Element.Methods.Simulated, HTMLElement.prototype, true);
     1907        }
     1908
     1909        if (F.SpecificElementExtensions) {
     1910                for (var tag in Element.Methods.ByTag) {
     1911                        var klass = findDOMClass(tag);
     1912                        if (typeof klass == "undefined") continue;
     1913                        copy(T[tag], klass.prototype);
     1914                }
     1915        }
     1916
     1917        Object.extend(Element, Element.Methods);
     1918        delete Element.ByTag;
    15741919};
    15751920
    1576 Object.extend(Element, Element.Methods);
    1577 
    1578 var _nativeExtensions = false;
    1579 
    1580 if(/Konqueror|Safari|KHTML/.test(navigator.userAgent))
    1581   ['', 'Form', 'Input', 'TextArea', 'Select'].each(function(tag) {
    1582     var className = 'HTML' + tag + 'Element';
    1583     if(window[className]) return;
    1584     var klass = window[className] = {};
    1585     klass.prototype = document.createElement(tag ? tag.toLowerCase() : 'div').__proto__;
    1586   });
    1587 
    1588 Element.addMethods = function(methods) {
    1589   Object.extend(Element.Methods, methods || {});
    1590 
    1591   function copy(methods, destination, onlyIfAbsent) {
    1592     onlyIfAbsent = onlyIfAbsent || false;
    1593     var cache = Element.extend.cache;
    1594     for (var property in methods) {
    1595       var value = methods[property];
    1596       if (!onlyIfAbsent || !(property in destination))
    1597         destination[property] = cache.findOrStore(value);
    1598     }
    1599   }
    1600 
    1601   if (typeof HTMLElement != 'undefined') {
    1602     copy(Element.Methods, HTMLElement.prototype);
    1603     copy(Element.Methods.Simulated, HTMLElement.prototype, true);
    1604     copy(Form.Methods, HTMLFormElement.prototype);
    1605     [HTMLInputElement, HTMLTextAreaElement, HTMLSelectElement].each(function(klass) {
    1606       copy(Form.Element.Methods, klass.prototype);
    1607     });
    1608     _nativeExtensions = true;
    1609   }
    1610 }
    1611 
    1612 var Toggle = new Object();
    1613 Toggle.display = Element.toggle;
     1921var Toggle = { display: Element.toggle };
    16141922
    16151923/*--------------------------------------------------------------------------*/
    16161924
    16171925Abstract.Insertion = function(adjacency) {
    1618   this.adjacency = adjacency;
     1926        this.adjacency = adjacency;
    16191927}
    16201928
    16211929Abstract.Insertion.prototype = {
    1622   initialize: function(element, content) {
    1623     this.element = $(element);
    1624     this.content = content.stripScripts();
    1625 
    1626     if (this.adjacency && this.element.insertAdjacentHTML) {
    1627       try {
    1628         this.element.insertAdjacentHTML(this.adjacency, this.content);
    1629       } catch (e) {
    1630         var tagName = this.element.tagName.toUpperCase();
    1631         if (['TBODY', 'TR'].include(tagName)) {
    1632           this.insertContent(this.contentFromAnonymousTable());
    1633         } else {
    1634           throw e;
    1635         }
    1636       }
    1637     } else {
    1638       this.range = this.element.ownerDocument.createRange();
    1639       if (this.initializeRange) this.initializeRange();
    1640       this.insertContent([this.range.createContextualFragment(this.content)]);
    1641     }
    1642 
    1643     setTimeout(function() {content.evalScripts()}, 10);
    1644   },
    1645 
    1646   contentFromAnonymousTable: function() {
    1647     var div = document.createElement('div');
    1648     div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
    1649     return $A(div.childNodes[0].childNodes[0].childNodes);
    1650   }
     1930        initialize: function(element, content) {
     1931                this.element = $(element);
     1932                this.content = content.stripScripts();
     1933
     1934                if (this.adjacency && this.element.insertAdjacentHTML) {
     1935                        try {
     1936                                this.element.insertAdjacentHTML(this.adjacency, this.content);
     1937                        } catch (e) {
     1938                                var tagName = this.element.tagName.toUpperCase();
     1939                                if (['TBODY', 'TR'].include(tagName)) {
     1940                                        this.insertContent(this.contentFromAnonymousTable());
     1941                                } else {
     1942                                        throw e;
     1943                                }
     1944                        }
     1945                } else {
     1946                        this.range = this.element.ownerDocument.createRange();
     1947                        if (this.initializeRange) this.initializeRange();
     1948                        this.insertContent([this.range.createContextualFragment(this.content)]);
     1949                }
     1950
     1951                setTimeout(function() {content.evalScripts()}, 10);
     1952        },
     1953
     1954        contentFromAnonymousTable: function() {
     1955                var div = document.createElement('div');
     1956                div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
     1957                return $A(div.childNodes[0].childNodes[0].childNodes);
     1958        }
    16511959}
    16521960
     
    16551963Insertion.Before = Class.create();
    16561964Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
    1657   initializeRange: function() {
    1658     this.range.setStartBefore(this.element);
    1659   },
    1660 
    1661   insertContent: function(fragments) {
    1662     fragments.each((function(fragment) {
    1663       this.element.parentNode.insertBefore(fragment, this.element);
    1664     }).bind(this));
    1665   }
     1965        initializeRange: function() {
     1966                this.range.setStartBefore(this.element);
     1967        },
     1968
     1969        insertContent: function(fragments) {
     1970                fragments.each((function(fragment) {
     1971                        this.element.parentNode.insertBefore(fragment, this.element);
     1972                }).bind(this));
     1973        }
    16661974});
    16671975
    16681976Insertion.Top = Class.create();
    16691977Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
    1670   initializeRange: function() {
    1671     this.range.selectNodeContents(this.element);
    1672     this.range.collapse(true);
    1673   },
    1674 
    1675   insertContent: function(fragments) {
    1676     fragments.reverse(false).each((function(fragment) {
    1677       this.element.insertBefore(fragment, this.element.firstChild);
    1678     }).bind(this));
    1679   }
     1978        initializeRange: function() {
     1979                this.range.selectNodeContents(this.element);
     1980                this.range.collapse(true);
     1981        },
     1982
     1983        insertContent: function(fragments) {
     1984                fragments.reverse(false).each((function(fragment) {
     1985                        this.element.insertBefore(fragment, this.element.firstChild);
     1986                }).bind(this));
     1987        }
    16801988});
    16811989
    16821990Insertion.Bottom = Class.create();
    16831991Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
    1684   initializeRange: function() {
    1685     this.range.selectNodeContents(this.element);
    1686     this.range.collapse(this.element);
    1687   },
    1688 
    1689   insertContent: function(fragments) {
    1690     fragments.each((function(fragment) {
    1691       this.element.appendChild(fragment);
    1692     }).bind(this));
    1693   }
     1992        initializeRange: function() {
     1993                this.range.selectNodeContents(this.element);
     1994                this.range.collapse(this.element);
     1995        },
     1996
     1997        insertContent: function(fragments) {
     1998                fragments.each((function(fragment) {
     1999                        this.element.appendChild(fragment);
     2000                }).bind(this));
     2001        }
    16942002});
    16952003
    16962004Insertion.After = Class.create();
    16972005Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
    1698   initializeRange: function() {
    1699     this.range.setStartAfter(this.element);
    1700   },
    1701 
    1702   insertContent: function(fragments) {
    1703     fragments.each((function(fragment) {
    1704       this.element.parentNode.insertBefore(fragment,
    1705         this.element.nextSibling);
    1706     }).bind(this));
    1707   }
     2006        initializeRange: function() {
     2007                this.range.setStartAfter(this.element);
     2008        },
     2009
     2010        insertContent: function(fragments) {
     2011                fragments.each((function(fragment) {
     2012                        this.element.parentNode.insertBefore(fragment,
     2013                                this.element.nextSibling);
     2014                }).bind(this));
     2015        }
    17082016});
    17092017
     
    17122020Element.ClassNames = Class.create();
    17132021Element.ClassNames.prototype = {
    1714   initialize: function(element) {
    1715     this.element = $(element);
    1716   },
    1717 
    1718   _each: function(iterator) {
    1719     this.element.className.split(/\s+/).select(function(name) {
    1720       return name.length > 0;
    1721     })._each(iterator);
    1722   },
    1723 
    1724   set: function(className) {
    1725     this.element.className = className;
    1726   },
    1727 
    1728   add: function(classNameToAdd) {
    1729     if (this.include(classNameToAdd)) return;
    1730     this.set($A(this).concat(classNameToAdd).join(' '));
    1731   },
    1732 
    1733   remove: function(classNameToRemove) {
    1734     if (!this.include(classNameToRemove)) return;
    1735     this.set($A(this).without(classNameToRemove).join(' '));
    1736   },
    1737 
    1738   toString: function() {
    1739     return $A(this).join(' ');
    1740   }
     2022        initialize: function(element) {
     2023                this.element = $(element);
     2024        },
     2025
     2026        _each: function(iterator) {
     2027                this.element.className.split(/\s+/).select(function(name) {
     2028                        return name.length > 0;
     2029                })._each(iterator);
     2030        },
     2031
     2032        set: function(className) {
     2033                this.element.className = className;
     2034        },
     2035
     2036        add: function(classNameToAdd) {
     2037                if (this.include(classNameToAdd)) return;
     2038                this.set($A(this).concat(classNameToAdd).join(' '));
     2039        },
     2040
     2041        remove: function(classNameToRemove) {
     2042                if (!this.include(classNameToRemove)) return;
     2043                this.set($A(this).without(classNameToRemove).join(' '));
     2044        },
     2045
     2046        toString: function() {
     2047                return $A(this).join(' ');
     2048        }
    17412049};
    17422050
    17432051Object.extend(Element.ClassNames.prototype, Enumerable);
     2052/* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
     2053 * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
     2054 * license.  Please see http://www.yui-ext.com/ for more information. */
     2055
    17442056var Selector = Class.create();
     2057
    17452058Selector.prototype = {
    1746   initialize: function(expression) {
    1747     this.params = {classNames: []};
    1748     this.expression = expression.toString().strip();
    1749     this.parseExpression();
    1750     this.compileMatcher();
    1751   },
    1752 
    1753   parseExpression: function() {
    1754     function abort(message) { throw 'Parse error in selector: ' + message; }
    1755 
    1756     if (this.expression == '')  abort('empty expression');
    1757 
    1758     var params = this.params, expr = this.expression, match, modifier, clause, rest;
    1759     while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) {
    1760       params.attributes = params.attributes || [];
    1761       params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''});
    1762       expr = match[1];
    1763     }
    1764 
    1765     if (expr == '*') return this.params.wildcard = true;
    1766 
    1767     while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) {
    1768       modifier = match[1], clause = match[2], rest = match[3];
    1769       switch (modifier) {
    1770         case '#':       params.id = clause; break;
    1771         case '.':       params.classNames.push(clause); break;
    1772         case '':
    1773         case undefined: params.tagName = clause.toUpperCase(); break;
    1774         default:        abort(expr.inspect());
    1775       }
    1776       expr = rest;
    1777     }
    1778 
    1779     if (expr.length > 0) abort(expr.inspect());
    1780   },
    1781 
    1782   buildMatchExpression: function() {
    1783     var params = this.params, conditions = [], clause;
    1784 
    1785     if (params.wildcard)
    1786       conditions.push('true');
    1787     if (clause = params.id)
    1788       conditions.push('element.readAttribute("id") == ' + clause.inspect());
    1789     if (clause = params.tagName)
    1790       conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
    1791     if ((clause = params.classNames).length > 0)
    1792       for (var i = 0, length = clause.length; i < length; i++)
    1793         conditions.push('element.hasClassName(' + clause[i].inspect() + ')');
    1794     if (clause = params.attributes) {
    1795       clause.each(function(attribute) {
    1796         var value = 'element.readAttribute(' + attribute.name.inspect() + ')';
    1797         var splitValueBy = function(delimiter) {
    1798           return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';
    1799         }
    1800 
    1801         switch (attribute.operator) {
    1802           case '=':       conditions.push(value + ' == ' + attribute.value.inspect()); break;
    1803           case '~=':      conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break;
    1804           case '|=':      conditions.push(
    1805                             splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect()
    1806                           ); break;
    1807           case '!=':      conditions.push(value + ' != ' + attribute.value.inspect()); break;
    1808           case '':
    1809           case undefined: conditions.push('element.hasAttribute(' + attribute.name.inspect() + ')'); break;
    1810           default:        throw 'Unknown operator ' + attribute.operator + ' in selector';
    1811         }
    1812       });
    1813     }
    1814 
    1815     return conditions.join(' && ');
    1816   },
    1817 
    1818   compileMatcher: function() {
    1819     this.match = new Function('element', 'if (!element.tagName) return false; \
    1820       element = $(element); \
    1821       return ' + this.buildMatchExpression());
    1822   },
    1823 
    1824   findElements: function(scope) {
    1825     var element;
    1826 
    1827     if (element = $(this.params.id))
    1828       if (this.match(element))
    1829         if (!scope || Element.childOf(element, scope))
    1830           return [element];
    1831 
    1832     scope = (scope || document).getElementsByTagName(this.params.tagName || '*');
    1833 
    1834     var results = [];
    1835     for (var i = 0, length = scope.length; i < length; i++)
    1836       if (this.match(element = scope[i]))
    1837         results.push(Element.extend(element));
    1838 
    1839     return results;
    1840   },
    1841 
    1842   toString: function() {
    1843     return this.expression;
    1844   }
    1845 }
     2059        initialize: function(expression) {
     2060                this.expression = expression.strip();
     2061                this.compileMatcher();
     2062        },
     2063
     2064        compileMatcher: function() {
     2065                // Selectors with namespaced attributes can't use the XPath version
     2066                if (Prototype.BrowserFeatures.XPath && !(/\[[\w-]*?:/).test(this.expression))
     2067                        return this.compileXPathMatcher();
     2068
     2069                var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
     2070                                c = Selector.criteria, le, p, m;
     2071
     2072                if (Selector._cache[e]) {
     2073                        this.matcher = Selector._cache[e]; return;
     2074                }
     2075                this.matcher = ["this.matcher = function(root) {",
     2076                                                                                "var r = root, h = Selector.handlers, c = false, n;"];
     2077
     2078                while (e && le != e && (/\S/).test(e)) {
     2079                        le = e;
     2080                        for (var i in ps) {
     2081                                p = ps[i];
     2082                                if (m = e.match(p)) {
     2083                                        this.matcher.push(typeof c[i] == 'function' ? c[i](m) :
     2084                                                new Template(c[i]).evaluate(m));
     2085                                        e = e.replace(m[0], '');
     2086                                        break;
     2087                                }
     2088                        }
     2089                }
     2090
     2091                this.matcher.push("return h.unique(n);\n}");
     2092                eval(this.matcher.join('\n'));
     2093                Selector._cache[this.expression] = this.matcher;
     2094        },
     2095
     2096        compileXPathMatcher: function() {
     2097                var e = this.expression, ps = Selector.patterns,
     2098                                x = Selector.xpath, le,  m;
     2099
     2100                if (Selector._cache[e]) {
     2101                        this.xpath = Selector._cache[e]; return;
     2102                }
     2103
     2104                this.matcher = ['.//*'];
     2105                while (e && le != e && (/\S/).test(e)) {
     2106                        le = e;
     2107                        for (var i in ps) {
     2108                                if (m = e.match(ps[i])) {
     2109                                        this.matcher.push(typeof x[i] == 'function' ? x[i](m) :
     2110                                                new Template(x[i]).evaluate(m));
     2111                                        e = e.replace(m[0], '');
     2112                                        break;
     2113                                }
     2114                        }
     2115                }
     2116
     2117                this.xpath = this.matcher.join('');
     2118                Selector._cache[this.expression] = this.xpath;
     2119        },
     2120
     2121        findElements: function(root) {
     2122                root = root || document;
     2123                if (this.xpath) return document._getElementsByXPath(this.xpath, root);
     2124                return this.matcher(root);
     2125        },
     2126
     2127        match: function(element) {
     2128                return this.findElements(document).include(element);
     2129        },
     2130
     2131        toString: function() {
     2132                return this.expression;
     2133        },
     2134
     2135        inspect: function() {
     2136                return "#<Selector:" + this.expression.inspect() + ">";
     2137        }
     2138};
    18462139
    18472140Object.extend(Selector, {
    1848   matchElements: function(elements, expression) {
    1849     var selector = new Selector(expression);
    1850     return elements.select(selector.match.bind(selector)).map(Element.extend);
    1851   },
    1852 
    1853   findElement: function(elements, expression, index) {
    1854     if (typeof expression == 'number') index = expression, expression = false;
    1855     return Selector.matchElements(elements, expression || '*')[index || 0];
    1856   },
    1857 
    1858   findChildElements: function(element, expressions) {
    1859     return expressions.map(function(expression) {
    1860       return expression.match(/[^\s"]+(?:"[^"]*"[^\s"]+)*/g).inject([null], function(results, expr) {
    1861         var selector = new Selector(expr);
    1862         return results.inject([], function(elements, result) {
    1863           return elements.concat(selector.findElements(result || element));
    1864         });
    1865       });
    1866     }).flatten();
    1867   }
     2141        _cache: {},
     2142
     2143        xpath: {
     2144                descendant:   "//*",
     2145                child:        "/*",
     2146                adjacent:     "/following-sibling::*[1]",
     2147                laterSibling: '/following-sibling::*',
     2148                tagName:      function(m) {
     2149                        if (m[1] == '*') return '';
     2150                        return "[local-name()='" + m[1].toLowerCase() +
     2151                                                 "' or local-name()='" + m[1].toUpperCase() + "']";
     2152                },
     2153                className:    "[contains(concat(' ', @class, ' '), ' #{1} ')]",
     2154                id:           "[@id='#{1}']",
     2155                attrPresence: "[@#{1}]",
     2156                attr: function(m) {
     2157                        m[3] = m[5] || m[6];
     2158                        return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
     2159                },
     2160                pseudo: function(m) {
     2161                        var h = Selector.xpath.pseudos[m[1]];
     2162                        if (!h) return '';
     2163                        if (typeof h === 'function') return h(m);
     2164                        return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
     2165                },
     2166                operators: {
     2167                        '=':  "[@#{1}='#{3}']",
     2168                        '!=': "[@#{1}!='#{3}']",
     2169                        '^=': "[starts-with(@#{1}, '#{3}')]",
     2170                        '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
     2171                        '*=': "[contains(@#{1}, '#{3}')]",
     2172                        '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
     2173                        '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
     2174                },
     2175                pseudos: {
     2176                        'first-child': '[not(preceding-sibling::*)]',
     2177                        'last-child':  '[not(following-sibling::*)]',
     2178                        'only-child':  '[not(preceding-sibling::* or following-sibling::*)]',
     2179                        'empty':       "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
     2180                        'checked':     "[@checked]",
     2181                        'disabled':    "[@disabled]",
     2182                        'enabled':     "[not(@disabled)]",
     2183                        'not': function(m) {
     2184                                var e = m[6], p = Selector.patterns,
     2185                                                x = Selector.xpath, le, m, v;
     2186
     2187                                var exclusion = [];
     2188                                while (e && le != e && (/\S/).test(e)) {
     2189                                        le = e;
     2190                                        for (var i in p) {
     2191                                                if (m = e.match(p[i])) {
     2192                                                        v = typeof x[i] == 'function' ? x[i](m) : new Template(x[i]).evaluate(m);
     2193                                                        exclusion.push("(" + v.substring(1, v.length - 1) + ")");
     2194                                                        e = e.replace(m[0], '');
     2195                                                        break;
     2196                                                }
     2197                                        }
     2198                                }
     2199                                return "[not(" + exclusion.join(" and ") + ")]";
     2200                        },
     2201                        'nth-child':      function(m) {
     2202                                return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
     2203                        },
     2204                        'nth-last-child': function(m) {
     2205                                return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
     2206                        },
     2207                        'nth-of-type':    function(m) {
     2208                                return Selector.xpath.pseudos.nth("position() ", m);
     2209                        },
     2210                        'nth-last-of-type': function(m) {
     2211                                return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
     2212                        },
     2213                        'first-of-type':  function(m) {
     2214                                m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
     2215                        },
     2216                        'last-of-type':   function(m) {
     2217                                m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
     2218                        },
     2219                        'only-of-type':   function(m) {
     2220                                var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
     2221                        },
     2222                        nth: function(fragment, m) {
     2223                                var mm, formula = m[6], predicate;
     2224                                if (formula == 'even') formula = '2n+0';
     2225                                if (formula == 'odd')  formula = '2n+1';
     2226                                if (mm = formula.match(/^(\d+)$/)) // digit only
     2227                                        return '[' + fragment + "= " + mm[1] + ']';
     2228                                if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
     2229                                        if (mm[1] == "-") mm[1] = -1;
     2230                                        var a = mm[1] ? Number(mm[1]) : 1;
     2231                                        var b = mm[2] ? Number(mm[2]) : 0;
     2232                                        predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
     2233                                        "((#{fragment} - #{b}) div #{a} >= 0)]";
     2234                                        return new Template(predicate).evaluate({
     2235                                                fragment: fragment, a: a, b: b });
     2236                                }
     2237                        }
     2238                }
     2239        },
     2240
     2241        criteria: {
     2242                tagName:      'n = h.tagName(n, r, "#{1}", c);   c = false;',
     2243                className:    'n = h.className(n, r, "#{1}", c); c = false;',
     2244                id:           'n = h.id(n, r, "#{1}", c);        c = false;',
     2245                attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;',
     2246                attr: function(m) {
     2247                        m[3] = (m[5] || m[6]);
     2248                        return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m);
     2249                },
     2250                pseudo:       function(m) {
     2251                        if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
     2252                        return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
     2253                },
     2254                descendant:   'c = "descendant";',
     2255                child:        'c = "child";',
     2256                adjacent:     'c = "adjacent";',
     2257                laterSibling: 'c = "laterSibling";'
     2258        },
     2259
     2260        patterns: {
     2261                // combinators must be listed first
     2262                // (and descendant needs to be last combinator)
     2263                laterSibling: /^\s*~\s*/,
     2264                child:        /^\s*>\s*/,
     2265                adjacent:     /^\s*\+\s*/,
     2266                descendant:   /^\s/,
     2267
     2268                // selectors follow
     2269                tagName:      /^\s*(\*|[\w\-]+)(\b|$)?/,
     2270                id:           /^#([\w\-\*]+)(\b|$)/,
     2271                className:    /^\.([\w\-\*]+)(\b|$)/,
     2272                pseudo:       /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|\s|(?=:))/,
     2273                attrPresence: /^\[([\w]+)\]/,
     2274                attr:         /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\]]*?)\4|([^'"][^\]]*?)))?\]/
     2275        },
     2276
     2277        handlers: {
     2278                // UTILITY FUNCTIONS
     2279                // joins two collections
     2280                concat: function(a, b) {
     2281                        for (var i = 0, node; node = b[i]; i++)
     2282                                a.push(node);
     2283                        return a;
     2284                },
     2285
     2286                // marks an array of nodes for counting
     2287                mark: function(nodes) {
     2288                        for (var i = 0, node; node = nodes[i]; i++)
     2289                                node._counted = true;
     2290                        return nodes;
     2291                },
     2292
     2293                unmark: function(nodes) {
     2294                        for (var i = 0, node; node = nodes[i]; i++)
     2295                                node._counted = undefined;
     2296                        return nodes;
     2297                },
     2298
     2299                // mark each child node with its position (for nth calls)
     2300                // "ofType" flag indicates whether we're indexing for nth-of-type
     2301                // rather than nth-child
     2302                index: function(parentNode, reverse, ofType) {
     2303                        parentNode._counted = true;
     2304                        if (reverse) {
     2305                                for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
     2306                                        node = nodes[i];
     2307                                        if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
     2308                                }
     2309                        } else {
     2310                                for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
     2311                                        if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
     2312                        }
     2313                },
     2314
     2315                // filters out duplicates and extends all nodes
     2316                unique: function(nodes) {
     2317                        if (nodes.length == 0) return nodes;
     2318                        var results = [], n;
     2319                        for (var i = 0, l = nodes.length; i < l; i++)
     2320                                if (!(n = nodes[i])._counted) {
     2321                                        n._counted = true;
     2322                                        results.push(Element.extend(n));
     2323                                }
     2324                        return Selector.handlers.unmark(results);
     2325                },
     2326
     2327                // COMBINATOR FUNCTIONS
     2328                descendant: function(nodes) {
     2329                        var h = Selector.handlers;
     2330                        for (var i = 0, results = [], node; node = nodes[i]; i++)
     2331                                h.concat(results, node.getElementsByTagName('*'));
     2332                        return results;
     2333                },
     2334
     2335                child: function(nodes) {
     2336                        var h = Selector.handlers;
     2337                        for (var i = 0, results = [], node; node = nodes[i]; i++) {
     2338                                for (var j = 0, children = [], child; child = node.childNodes[j]; j++)
     2339                                        if (child.nodeType == 1 && child.tagName != '!') results.push(child);
     2340                        }
     2341                        return results;
     2342                },
     2343
     2344                adjacent: function(nodes) {
     2345                        for (var i = 0, results = [], node; node = nodes[i]; i++) {
     2346                                var next = this.nextElementSibling(node);
     2347                                if (next) results.push(next);
     2348                        }
     2349                        return results;
     2350                },
     2351
     2352                laterSibling: function(nodes) {
     2353                        var h = Selector.handlers;
     2354                        for (var i = 0, results = [], node; node = nodes[i]; i++)
     2355                                h.concat(results, Element.nextSiblings(node));
     2356                        return results;
     2357                },
     2358
     2359                nextElementSibling: function(node) {
     2360                        while (node = node.nextSibling)
     2361                                if (node.nodeType == 1) return node;
     2362                        return null;
     2363                },
     2364
     2365                previousElementSibling: function(node) {
     2366                        while (node = node.previousSibling)
     2367                                if (node.nodeType == 1) return node;
     2368                        return null;
     2369                },
     2370
     2371                // TOKEN FUNCTIONS
     2372                tagName: function(nodes, root, tagName, combinator) {
     2373                        tagName = tagName.toUpperCase();
     2374                        var results = [], h = Selector.handlers;
     2375                        if (nodes) {
     2376                                if (combinator) {
     2377                                        // fastlane for ordinary descendant combinators
     2378                                        if (combinator == "descendant") {
     2379                                                for (var i = 0, node; node = nodes[i]; i++)
     2380                                                        h.concat(results, node.getElementsByTagName(tagName));
     2381                                                return results;
     2382                                        } else nodes = this[combinator](nodes);
     2383                                        if (tagName == "*") return nodes;
     2384                                }
     2385                                for (var i = 0, node; node = nodes[i]; i++)
     2386                                        if (node.tagName.toUpperCase() == tagName) results.push(node);
     2387                                return results;
     2388                        } else return root.getElementsByTagName(tagName);
     2389                },
     2390
     2391                id: function(nodes, root, id, combinator) {
     2392                        var targetNode = $(id), h = Selector.handlers;
     2393                        if (!nodes && root == document) return targetNode ? [targetNode] : [];
     2394                        if (nodes) {
     2395                                if (combinator) {
     2396                                        if (combinator == 'child') {
     2397                                                for (var i = 0, node; node = nodes[i]; i++)
     2398                                                        if (targetNode.parentNode == node) return [targetNode];
     2399                                        } else if (combinator == 'descendant') {
     2400                                                for (var i = 0, node; node = nodes[i]; i++)
     2401                                                        if (Element.descendantOf(targetNode, node)) return [targetNode];
     2402                                        } else if (combinator == 'adjacent') {
     2403                                                for (var i = 0, node; node = nodes[i]; i++)
     2404                                                        if (Selector.handlers.previousElementSibling(targetNode) == node)
     2405                                                                return [targetNode];
     2406                                        } else nodes = h[combinator](nodes);
     2407                                }
     2408                                for (var i = 0, node; node = nodes[i]; i++)
     2409                                        if (node == targetNode) return [targetNode];
     2410                                return [];
     2411                        }
     2412                        return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
     2413                },
     2414
     2415                className: function(nodes, root, className, combinator) {
     2416                        if (nodes && combinator) nodes = this[combinator](nodes);
     2417                        return Selector.handlers.byClassName(nodes, root, className);
     2418                },
     2419
     2420                byClassName: function(nodes, root, className) {
     2421                        if (!nodes) nodes = Selector.handlers.descendant([root]);
     2422                        var needle = ' ' + className + ' ';
     2423                        for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
     2424                                nodeClassName = node.className;
     2425                                if (nodeClassName.length == 0) continue;
     2426                                if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
     2427                                        results.push(node);
     2428                        }
     2429                        return results;
     2430                },
     2431
     2432                attrPresence: function(nodes, root, attr) {
     2433                        var results = [];
     2434                        for (var i = 0, node; node = nodes[i]; i++)
     2435                                if (Element.hasAttribute(node, attr)) results.push(node);
     2436                        return results;
     2437                },
     2438
     2439                attr: function(nodes, root, attr, value, operator) {
     2440                        if (!nodes) nodes = root.getElementsByTagName("*");
     2441                        var handler = Selector.operators[operator], results = [];
     2442                        for (var i = 0, node; node = nodes[i]; i++) {
     2443                                var nodeValue = Element.readAttribute(node, attr);
     2444                                if (nodeValue === null) continue;
     2445                                if (handler(nodeValue, value)) results.push(node);
     2446                        }
     2447                        return results;
     2448                },
     2449
     2450                pseudo: function(nodes, name, value, root, combinator) {
     2451                        if (nodes && combinator) nodes = this[combinator](nodes);
     2452                        if (!nodes) nodes = root.getElementsByTagName("*");
     2453                        return Selector.pseudos[name](nodes, value, root);
     2454                }
     2455        },
     2456
     2457        pseudos: {
     2458                'first-child': function(nodes, value, root) {
     2459                        for (var i = 0, results = [], node; node = nodes[i]; i++) {
     2460                                if (Selector.handlers.previousElementSibling(node)) continue;
     2461                                        results.push(node);
     2462                        }
     2463                        return results;
     2464                },
     2465                'last-child': function(nodes, value, root) {
     2466                        for (var i = 0, results = [], node; node = nodes[i]; i++) {
     2467                                if (Selector.handlers.nextElementSibling(node)) continue;
     2468                                        results.push(node);
     2469                        }
     2470                        return results;
     2471                },
     2472                'only-child': function(nodes, value, root) {
     2473                        var h = Selector.handlers;
     2474                        for (var i = 0, results = [], node; node = nodes[i]; i++)
     2475                                if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
     2476                                        results.push(node);
     2477                        return results;
     2478                },
     2479                'nth-child':        function(nodes, formula, root) {
     2480                        return Selector.pseudos.nth(nodes, formula, root);
     2481                },
     2482                'nth-last-child':   function(nodes, formula, root) {
     2483                        return Selector.pseudos.nth(nodes, formula, root, true);
     2484                },
     2485                'nth-of-type':      function(nodes, formula, root) {
     2486                        return Selector.pseudos.nth(nodes, formula, root, false, true);
     2487                },
     2488                'nth-last-of-type': function(nodes, formula, root) {
     2489                        return Selector.pseudos.nth(nodes, formula, root, true, true);
     2490                },
     2491                'first-of-type':    function(nodes, formula, root) {
     2492                        return Selector.pseudos.nth(nodes, "1", root, false, true);
     2493                },
     2494                'last-of-type':     function(nodes, formula, root) {
     2495                        return Selector.pseudos.nth(nodes, "1", root, true, true);
     2496                },
     2497                'only-of-type':     function(nodes, formula, root) {
     2498                        var p = Selector.pseudos;
     2499                        return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
     2500                },
     2501
     2502                // handles the an+b logic
     2503                getIndices: function(a, b, total) {
     2504                        if (a == 0) return b > 0 ? [b] : [];
     2505                        return $R(1, total).inject([], function(memo, i) {
     2506                                if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
     2507                                return memo;
     2508                        });
     2509                },
     2510
     2511                // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
     2512                nth: function(nodes, formula, root, reverse, ofType) {
     2513                        if (nodes.length == 0) return [];
     2514                        if (formula == 'even') formula = '2n+0';
     2515                        if (formula == 'odd')  formula = '2n+1';
     2516                        var h = Selector.handlers, results = [], indexed = [], m;
     2517                        h.mark(nodes);
     2518                        for (var i = 0, node; node = nodes[i]; i++) {
     2519                                if (!node.parentNode._counted) {
     2520                                        h.index(node.parentNode, reverse, ofType);
     2521                                        indexed.push(node.parentNode);
     2522                                }
     2523                        }
     2524                        if (formula.match(/^\d+$/)) { // just a number
     2525                                formula = Number(formula);
     2526                                for (var i = 0, node; node = nodes[i]; i++)
     2527                                        if (node.nodeIndex == formula) results.push(node);
     2528                        } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
     2529                                if (m[1] == "-") m[1] = -1;
     2530                                var a = m[1] ? Number(m[1]) : 1;
     2531                                var b = m[2] ? Number(m[2]) : 0;
     2532                                var indices = Selector.pseudos.getIndices(a, b, nodes.length);
     2533                                for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
     2534                                        for (var j = 0; j < l; j++)
     2535                                                if (node.nodeIndex == indices[j]) results.push(node);
     2536                                }
     2537                        }
     2538                        h.unmark(nodes);
     2539                        h.unmark(indexed);
     2540                        return results;
     2541                },
     2542
     2543                'empty': function(nodes, value, root) {
     2544                        for (var i = 0, results = [], node; node = nodes[i]; i++) {
     2545                                // IE treats comments as element nodes
     2546                                if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
     2547                                results.push(node);
     2548                        }
     2549                        return results;
     2550                },
     2551
     2552                'not': function(nodes, selector, root) {
     2553                        var h = Selector.handlers, selectorType, m;
     2554                        var exclusions = new Selector(selector).findElements(root);
     2555                        h.mark(exclusions);
     2556                        for (var i = 0, results = [], node; node = nodes[i]; i++)
     2557                                if (!node._counted) results.push(node);
     2558                        h.unmark(exclusions);
     2559                        return results;
     2560                },
     2561
     2562                'enabled': function(nodes, value, root) {
     2563                        for (var i = 0, results = [], node; node = nodes[i]; i++)
     2564                                if (!node.disabled) results.push(node);
     2565                        return results;
     2566                },
     2567
     2568                'disabled': function(nodes, value, root) {
     2569                        for (var i = 0, results = [], node; node = nodes[i]; i++)
     2570                                if (node.disabled) results.push(node);
     2571                        return results;
     2572                },
     2573
     2574                'checked': function(nodes, value, root) {
     2575                        for (var i = 0, results = [], node; node = nodes[i]; i++)
     2576                                if (node.checked) results.push(node);
     2577                        return results;
     2578                }
     2579        },
     2580
     2581        operators: {
     2582                '=':  function(nv, v) { return nv == v; },
     2583                '!=': function(nv, v) { return nv != v; },
     2584                '^=': function(nv, v) { return nv.startsWith(v); },
     2585                '$=': function(nv, v) { return nv.endsWith(v); },
     2586                '*=': function(nv, v) { return nv.include(v); },
     2587                '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
     2588                '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
     2589        },
     2590
     2591        matchElements: function(elements, expression) {
     2592                var matches = new Selector(expression).findElements(), h = Selector.handlers;
     2593                h.mark(matches);
     2594                for (var i = 0, results = [], element; element = elements[i]; i++)
     2595                        if (element._counted) results.push(element);
     2596                h.unmark(matches);
     2597                return results;
     2598        },
     2599
     2600        findElement: function(elements, expression, index) {
     2601                if (typeof expression == 'number') {
     2602                        index = expression; expression = false;
     2603                }
     2604                return Selector.matchElements(elements, expression || '*')[index || 0];
     2605        },
     2606
     2607        findChildElements: function(element, expressions) {
     2608                var exprs = expressions.join(','), expressions = [];
     2609                exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
     2610                        expressions.push(m[1].strip());
     2611                });
     2612                var results = [], h = Selector.handlers;
     2613                for (var i = 0, l = expressions.length, selector; i < l; i++) {
     2614                        selector = new Selector(expressions[i].strip());
     2615                        h.concat(results, selector.findElements(element));
     2616                }
     2617                return (l > 1) ? h.unique(results) : results;
     2618        }
    18682619});
    18692620
    18702621function $$() {
    1871   return Selector.findChildElements(document, $A(arguments));
     2622        return Selector.findChildElements(document, $A(arguments));
    18722623}
    18732624var Form = {
    1874   reset: function(form) {
    1875     $(form).reset();
    1876     return form;
    1877   },
    1878 
    1879   serializeElements: function(elements, getHash) {
    1880     var data = elements.inject({}, function(result, element) {
    1881       if (!element.disabled && element.name) {
    1882         var key = element.name, value = $(element).getValue();
    1883         if (value != undefined) {
    1884           if (result[key]) {
    1885             if (result[key].constructor != Array) result[key] = [result[key]];
    1886             result[key].push(value);
    1887           }
    1888           else result[key] = value;
    1889         }
    1890       }
    1891       return result;
    1892     });
    1893 
    1894     return getHash ? data : Hash.toQueryString(data);
    1895   }
     2625        reset: function(form) {
     2626                $(form).reset();
     2627                return form;
     2628        },
     2629
     2630        serializeElements: function(elements, getHash) {
     2631                var data = elements.inject({}, function(result, element) {
     2632                        if (!element.disabled && element.name) {
     2633                                var key = element.name, value = $(element).getValue();
     2634                                if (value != null) {
     2635                                        if (key in result) {
     2636                                                if (result[key].constructor != Array) result[key] = [result[key]];
     2637                                                result[key].push(value);
     2638                                        }
     2639                                        else result[key] = value;
     2640                                }
     2641                        }
     2642                        return result;
     2643                });
     2644
     2645                return getHash ? data : Hash.toQueryString(data);
     2646        }
    18962647};
    18972648
    18982649Form.Methods = {
    1899   serialize: function(form, getHash) {
    1900     return Form.serializeElements(Form.getElements(form), getHash);
    1901   },
    1902 
    1903   getElements: function(form) {
    1904     return $A($(form).getElementsByTagName('*')).inject([],
    1905       function(elements, child) {
    1906         if (Form.Element.Serializers[child.tagName.toLowerCase()])
    1907           elements.push(Element.extend(child));
    1908         return elements;
    1909       }
    1910     );
    1911   },
    1912 
    1913   getInputs: function(form, typeName, name) {
    1914     form = $(form);
    1915     var inputs = form.getElementsByTagName('input');
    1916 
    1917     if (!typeName && !name) return $A(inputs).map(Element.extend);
    1918 
    1919     for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
    1920       var input = inputs[i];
    1921       if ((typeName && input.type != typeName) || (name && input.name != name))
    1922         continue;
    1923       matchingInputs.push(Element.extend(input));
    1924     }
    1925 
    1926     return matchingInputs;
    1927   },
    1928 
    1929   disable: function(form) {
    1930     form = $(form);
    1931     form.getElements().each(function(element) {
    1932       element.blur();
    1933       element.disabled = 'true';
    1934     });
    1935     return form;
    1936   },
    1937 
    1938   enable: function(form) {
    1939     form = $(form);
    1940     form.getElements().each(function(element) {
    1941       element.disabled = '';
    1942     });
    1943     return form;
    1944   },
    1945 
    1946   findFirstElement: function(form) {
    1947     return $(form).getElements().find(function(element) {
    1948       return element.type != 'hidden' && !element.disabled &&
    1949         ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
    1950     });
    1951   },
    1952 
    1953   focusFirstElement: function(form) {
    1954     form = $(form);
    1955     form.findFirstElement().activate();
    1956     return form;
    1957   }
    1958 }
    1959 
    1960 Object.extend(Form, Form.Methods);
     2650        serialize: function(form, getHash) {
     2651                return Form.serializeElements(Form.getElements(form), getHash);
     2652        },
     2653
     2654        getElements: function(form) {
     2655                return $A($(form).getElementsByTagName('*')).inject([],
     2656                        function(elements, child) {
     2657                                if (Form.Element.Serializers[child.tagName.toLowerCase()])
     2658                                        elements.push(Element.extend(child));
     2659                                return elements;
     2660                        }
     2661                );
     2662        },
     2663
     2664        getInputs: function(form, typeName, name) {
     2665                form = $(form);
     2666                var inputs = form.getElementsByTagName('input');
     2667
     2668                if (!typeName && !name) return $A(inputs).map(Element.extend);
     2669
     2670                for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
     2671                        var input = inputs[i];
     2672                        if ((typeName && input.type != typeName) || (name && input.name != name))
     2673                                continue;
     2674                        matchingInputs.push(Element.extend(input));
     2675                }
     2676
     2677                return matchingInputs;
     2678        },
     2679
     2680        disable: function(form) {
     2681                form = $(form);
     2682                Form.getElements(form).invoke('disable');
     2683                return form;
     2684        },
     2685
     2686        enable: function(form) {
     2687                form = $(form);
     2688                Form.getElements(form).invoke('enable');
     2689                return form;
     2690        },
     2691
     2692        findFirstElement: function(form) {
     2693                return $(form).getElements().find(function(element) {
     2694                        return element.type != 'hidden' && !element.disabled &&
     2695                                ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
     2696                });
     2697        },
     2698
     2699        focusFirstElement: function(form) {
     2700                form = $(form);
     2701                form.findFirstElement().activate();
     2702                return form;
     2703        },
     2704
     2705        request: function(form, options) {
     2706                form = $(form), options = Object.clone(options || {});
     2707
     2708                var params = options.parameters;
     2709                options.parameters = form.serialize(true);
     2710
     2711                if (params) {
     2712                        if (typeof params == 'string') params = params.toQueryParams();
     2713                        Object.extend(options.parameters, params);
     2714                }
     2715
     2716                if (form.hasAttribute('method') && !options.method)
     2717                        options.method = form.method;
     2718
     2719                return new Ajax.Request(form.readAttribute('action'), options);
     2720        }
     2721}
    19612722
    19622723/*--------------------------------------------------------------------------*/
    19632724
    19642725Form.Element = {
    1965   focus: function(element) {
    1966     $(element).focus();
    1967     return element;
    1968   },
    1969 
    1970   select: function(element) {
    1971     $(element).select();
    1972     return element;
    1973   }
     2726        focus: function(element) {
     2727                $(element).focus();
     2728                return element;
     2729        },
     2730
     2731        select: function(element) {
     2732                $(element).select();
     2733                return element;
     2734        }
    19742735}
    19752736
    19762737Form.Element.Methods = {
    1977   serialize: function(element) {
    1978     element = $(element);
    1979     if (!element.disabled && element.name) {
    1980       var value = element.getValue();
    1981       if (value != undefined) {
    1982         var pair = {};
    1983         pair[element.name] = value;
    1984         return Hash.toQueryString(pair);
    1985       }
    1986     }
    1987     return '';
    1988   },
    1989 
    1990   getValue: function(element) {
    1991     element = $(element);
    1992     var method = element.tagName.toLowerCase();
    1993     return Form.Element.Serializers[method](element);
    1994   },
    1995 
    1996   clear: function(element) {
    1997     $(element).value = '';
    1998     return element;
    1999   },
    2000 
    2001   present: function(element) {
    2002     return $(element).value != '';
    2003   },
    2004 
    2005   activate: function(element) {
    2006     element = $(element);
    2007     element.focus();
    2008     if (element.select && ( element.tagName.toLowerCase() != 'input' ||
    2009       !['button', 'reset', 'submit'].include(element.type) ) )
    2010       element.select();
    2011     return element;
    2012   },
    2013 
    2014   disable: function(element) {
    2015     element = $(element);
    2016     element.disabled = true;
    2017     return element;
    2018   },
    2019 
    2020   enable: function(element) {
    2021     element = $(element);
    2022     element.blur();
    2023     element.disabled = false;
    2024     return element;
    2025   }
    2026 }
    2027 
    2028 Object.extend(Form.Element, Form.Element.Methods);
     2738        serialize: function(element) {
     2739                element = $(element);
     2740                if (!element.disabled && element.name) {
     2741                        var value = element.getValue();
     2742                        if (value != undefined) {
     2743                                var pair = {};
     2744                                pair[element.name] = value;
     2745                                return Hash.toQueryString(pair);
     2746                        }
     2747                }
     2748                return '';
     2749        },
     2750
     2751        getValue: function(element) {
     2752                element = $(element);
     2753                var method = element.tagName.toLowerCase();
     2754                return Form.Element.Serializers[method](element);
     2755        },
     2756
     2757        clear: function(element) {
     2758                $(element).value = '';
     2759                return element;
     2760        },
     2761
     2762        present: function(element) {
     2763                return $(element).value != '';
     2764        },
     2765
     2766        activate: function(element) {
     2767                element = $(element);
     2768                try {
     2769                        element.focus();
     2770                        if (element.select && (element.tagName.toLowerCase() != 'input' ||
     2771                                !['button', 'reset', 'submit'].include(element.type)))
     2772                                element.select();
     2773                } catch (e) {}
     2774                return element;
     2775        },
     2776
     2777        disable: function(element) {
     2778                element = $(element);
     2779                element.blur();
     2780                element.disabled = true;
     2781                return element;
     2782        },
     2783
     2784        enable: function(element) {
     2785                element = $(element);
     2786                element.disabled = false;
     2787                return element;
     2788        }
     2789}
     2790
     2791/*--------------------------------------------------------------------------*/
     2792
    20292793var Field = Form.Element;
    2030 var $F = Form.Element.getValue;
     2794var $F = Form.Element.Methods.getValue;
    20312795
    20322796/*--------------------------------------------------------------------------*/
    20332797
    20342798Form.Element.Serializers = {
    2035   input: function(element) {
    2036     switch (element.type.toLowerCase()) {
    2037       case 'checkbox':
    2038       case 'radio':
    2039         return Form.Element.Serializers.inputSelector(element);
    2040       default:
    2041         return Form.Element.Serializers.textarea(element);
    2042     }
    2043   },
    2044 
    2045   inputSelector: function(element) {
    2046     return element.checked ? element.value : null;
    2047   },
    2048 
    2049   textarea: function(element) {
    2050     return element.value;
    2051   },
    2052 
    2053   select: function(element) {
    2054     return this[element.type == 'select-one' ?
    2055       'selectOne' : 'selectMany'](element);
    2056   },
    2057 
    2058   selectOne: function(element) {
    2059     var index = element.selectedIndex;
    2060     return index >= 0 ? this.optionValue(element.options[index]) : null;
    2061   },
    2062 
    2063   selectMany: function(element) {
    2064     var values, length = element.length;
    2065     if (!length) return null;
    2066 
    2067     for (var i = 0, values = []; i < length; i++) {
    2068       var opt = element.options[i];
    2069       if (opt.selected) values.push(this.optionValue(opt));
    2070     }
    2071     return values;
    2072   },
    2073 
    2074   optionValue: function(opt) {
    2075     // extend element because hasAttribute may not be native
    2076     return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
    2077   }
     2799        input: function(element) {
     2800                switch (element.type.toLowerCase()) {
     2801                        case 'checkbox':
     2802                        case 'radio':
     2803                                return Form.Element.Serializers.inputSelector(element);
     2804                        default:
     2805                                return Form.Element.Serializers.textarea(element);
     2806                }
     2807        },
     2808
     2809        inputSelector: function(element) {
     2810                return element.checked ? element.value : null;
     2811        },
     2812
     2813        textarea: function(element) {
     2814                return element.value;
     2815        },
     2816
     2817        select: function(element) {
     2818                return this[element.type == 'select-one' ?
     2819                        'selectOne' : 'selectMany'](element);
     2820        },
     2821
     2822        selectOne: function(element) {
     2823                var index = element.selectedIndex;
     2824                return index >= 0 ? this.optionValue(element.options[index]) : null;
     2825        },
     2826
     2827        selectMany: function(element) {
     2828                var values, length = element.length;
     2829                if (!length) return null;
     2830
     2831                for (var i = 0, values = []; i < length; i++) {
     2832                        var opt = element.options[i];
     2833                        if (opt.selected) values.push(this.optionValue(opt));
     2834                }
     2835                return values;
     2836        },
     2837
     2838        optionValue: function(opt) {
     2839                // extend element because hasAttribute may not be native
     2840                return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
     2841        }
    20782842}
    20792843
     
    20822846Abstract.TimedObserver = function() {}
    20832847Abstract.TimedObserver.prototype = {
    2084   initialize: function(element, frequency, callback) {
    2085     this.frequency = frequency;
    2086     this.element   = $(element);
    2087     this.callback  = callback;
    2088 
    2089     this.lastValue = this.getValue();
    2090     this.registerCallback();
    2091   },
    2092 
    2093   registerCallback: function() {
    2094     setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
    2095   },
    2096 
    2097   onTimerEvent: function() {
    2098     var value = this.getValue();
    2099     var changed = ('string' == typeof this.lastValue && 'string' == typeof value
    2100       ? this.lastValue != value : String(this.lastValue) != String(value));
    2101     if (changed) {
    2102       this.callback(this.element, value);
    2103       this.lastValue = value;
    2104     }
    2105   }
     2848        initialize: function(element, frequency, callback) {
     2849                this.frequency = frequency;
     2850                this.element   = $(element);
     2851                this.callback  = callback;
     2852
     2853                this.lastValue = this.getValue();
     2854                this.registerCallback();
     2855        },
     2856
     2857        registerCallback: function() {
     2858                setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
     2859        },
     2860
     2861        onTimerEvent: function() {
     2862                var value = this.getValue();
     2863                var changed = ('string' == typeof this.lastValue && 'string' == typeof value
     2864                        ? this.lastValue != value : String(this.lastValue) != String(value));
     2865                if (changed) {
     2866                        this.callback(this.element, value);
     2867                        this.lastValue = value;
     2868                }
     2869        }
    21062870}
    21072871
    21082872Form.Element.Observer = Class.create();
    21092873Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
    2110   getValue: function() {
    2111     return Form.Element.getValue(this.element);
    2112   }
     2874        getValue: function() {
     2875                return Form.Element.getValue(this.element);
     2876        }
    21132877});
    21142878
    21152879Form.Observer = Class.create();
    21162880Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
    2117   getValue: function() {
    2118     return Form.serialize(this.element);
    2119   }
     2881        getValue: function() {
     2882                return Form.serialize(this.element);
     2883        }
    21202884});
    21212885
     
    21242888Abstract.EventObserver = function() {}
    21252889Abstract.EventObserver.prototype = {
    2126   initialize: function(element, callback) {
    2127     this.element  = $(element);
    2128     this.callback = callback;
    2129 
    2130     this.lastValue = this.getValue();
    2131     if (this.element.tagName.toLowerCase() == 'form')
    2132       this.registerFormCallbacks();
    2133     else
    2134       this.registerCallback(this.element);
    2135   },
    2136 
    2137   onElementEvent: function() {
    2138     var value = this.getValue();
    2139     if (this.lastValue != value) {
    2140       this.callback(this.element, value);
    2141       this.lastValue = value;
    2142     }
    2143   },
    2144 
    2145   registerFormCallbacks: function() {
    2146     Form.getElements(this.element).each(this.registerCallback.bind(this));
    2147   },
    2148 
    2149   registerCallback: function(element) {
    2150     if (element.type) {
    2151       switch (element.type.toLowerCase()) {
    2152         case 'checkbox':
    2153         case 'radio':
    2154           Event.observe(element, 'click', this.onElementEvent.bind(this));
    2155           break;
    2156         default:
    2157           Event.observe(element, 'change', this.onElementEvent.bind(this));
    2158           break;
    2159       }
    2160     }
    2161   }
     2890        initialize: function(element, callback) {
     2891                this.element  = $(element);
     2892                this.callback = callback;
     2893
     2894                this.lastValue = this.getValue();
     2895                if (this.element.tagName.toLowerCase() == 'form')
     2896                        this.registerFormCallbacks();
     2897                else
     2898                        this.registerCallback(this.element);
     2899        },
     2900
     2901        onElementEvent: function() {
     2902                var value = this.getValue();
     2903                if (this.lastValue != value) {
     2904                        this.callback(this.element, value);
     2905                        this.lastValue = value;
     2906                }
     2907        },
     2908
     2909        registerFormCallbacks: function() {
     2910                Form.getElements(this.element).each(this.registerCallback.bind(this));
     2911        },
     2912
     2913        registerCallback: function(element) {
     2914                if (element.type) {
     2915                        switch (element.type.toLowerCase()) {
     2916                                case 'checkbox':
     2917                                case 'radio':
     2918                                        Event.observe(element, 'click', this.onElementEvent.bind(this));
     2919                                        break;
     2920                                default:
     2921                                        Event.observe(element, 'change', this.onElementEvent.bind(this));
     2922                                        break;
     2923                        }
     2924                }
     2925        }
    21622926}
    21632927
    21642928Form.Element.EventObserver = Class.create();
    21652929Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
    2166   getValue: function() {
    2167     return Form.Element.getValue(this.element);
    2168   }
     2930        getValue: function() {
     2931                return Form.Element.getValue(this.element);
     2932        }
    21692933});
    21702934
    21712935Form.EventObserver = Class.create();
    21722936Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
    2173   getValue: function() {
    2174     return Form.serialize(this.element);
    2175   }
     2937        getValue: function() {
     2938                return Form.serialize(this.element);
     2939        }
    21762940});
    21772941if (!window.Event) {
    2178   var Event = new Object();
     2942        var Event = new Object();
    21792943}
    21802944
    21812945Object.extend(Event, {
    2182   KEY_BACKSPACE: 8,
    2183   KEY_TAB:       9,
    2184   KEY_RETURN:   13,
    2185   KEY_ESC:      27,
    2186   KEY_LEFT:     37,
    2187   KEY_UP:       38,
    2188   KEY_RIGHT:    39,
    2189   KEY_DOWN:     40,
    2190   KEY_DELETE:   46,
    2191   KEY_HOME:     36,
    2192   KEY_END:      35,
    2193   KEY_PAGEUP:   33,
    2194   KEY_PAGEDOWN: 34,
    2195 
    2196   element: function(event) {
    2197     return event.target || event.srcElement;
    2198   },
    2199 
    2200   isLeftClick: function(event) {
    2201     return (((event.which) && (event.which == 1)) ||
    2202             ((event.button) && (event.button == 1)));
    2203   },
    2204 
    2205   pointerX: function(event) {
    2206     return event.pageX || (event.clientX +
    2207       (document.documentElement.scrollLeft || document.body.scrollLeft));
    2208   },
    2209 
    2210   pointerY: function(event) {
    2211     return event.pageY || (event.clientY +
    2212       (document.documentElement.scrollTop || document.body.scrollTop));
    2213   },
    2214 
    2215   stop: function(event) {
    2216     if (event.preventDefault) {
    2217       event.preventDefault();
    2218       event.stopPropagation();
    2219     } else {
    2220       event.returnValue = false;
    2221       event.cancelBubble = true;
    2222     }
    2223   },
    2224 
    2225   // find the first node with the given tagName, starting from the
    2226   // node the event was triggered on; traverses the DOM upwards
    2227   findElement: function(event, tagName) {
    2228     var element = Event.element(event);
    2229     while (element.parentNode && (!element.tagName ||
    2230         (element.tagName.toUpperCase() != tagName.toUpperCase())))
    2231       element = element.parentNode;
    2232     return element;
    2233   },
    2234 
    2235   observers: false,
    2236 
    2237   _observeAndCache: function(element, name, observer, useCapture) {
    2238     if (!this.observers) this.observers = [];
    2239     if (element.addEventListener) {
    2240       this.observers.push([element, name, observer, useCapture]);
    2241       element.addEventListener(name, observer, useCapture);
    2242     } else if (element.attachEvent) {
    2243       this.observers.push([element, name, observer, useCapture]);
    2244       element.attachEvent('on' + name, observer);
    2245     }
    2246   },
    2247 
    2248   unloadCache: function() {
    2249     if (!Event.observers) return;
    2250     for (var i = 0, length = Event.observers.length; i < length; i++) {
    2251       Event.stopObserving.apply(this, Event.observers[i]);
    2252       Event.observers[i][0] = null;
    2253     }
    2254     Event.observers = false;
    2255   },
    2256 
    2257   observe: function(element, name, observer, useCapture) {
    2258     element = $(element);
    2259     useCapture = useCapture || false;
    2260 
    2261     if (name == 'keypress' &&
    2262         (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
    2263         || element.attachEvent))
    2264       name = 'keydown';
    2265 
    2266     Event._observeAndCache(element, name, observer, useCapture);
    2267   },
    2268 
    2269   stopObserving: function(element, name, observer, useCapture) {
    2270     element = $(element);
    2271     useCapture = useCapture || false;
    2272 
    2273     if (name == 'keypress' &&
    2274         (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
    2275         || element.detachEvent))
    2276       name = 'keydown';
    2277 
    2278     if (element.removeEventListener) {
    2279       element.removeEventListener(name, observer, useCapture);
    2280     } else if (element.detachEvent) {
    2281       try {
    2282         element.detachEvent('on' + name, observer);
    2283       } catch (e) {}
    2284     }
    2285   }
     2946        KEY_BACKSPACE: 8,
     2947        KEY_TAB:       9,
     2948        KEY_RETURN:   13,
     2949        KEY_ESC:      27,
     2950        KEY_LEFT:     37,
     2951        KEY_UP:       38,
     2952        KEY_RIGHT:    39,
     2953        KEY_DOWN:     40,
     2954        KEY_DELETE:   46,
     2955        KEY_HOME:     36,
     2956        KEY_END:      35,
     2957        KEY_PAGEUP:   33,
     2958        KEY_PAGEDOWN: 34,
     2959
     2960        element: function(event) {
     2961                return $(event.target || event.srcElement);
     2962        },
     2963
     2964        isLeftClick: function(event) {
     2965                return (((event.which) && (event.which == 1)) ||
     2966                                                ((event.button) && (event.button == 1)));
     2967        },
     2968
     2969        pointerX: function(event) {
     2970                return event.pageX || (event.clientX +
     2971                        (document.documentElement.scrollLeft || document.body.scrollLeft));
     2972        },
     2973
     2974        pointerY: function(event) {
     2975                return event.pageY || (event.clientY +
     2976                        (document.documentElement.scrollTop || document.body.scrollTop));
     2977        },
     2978
     2979        stop: function(event) {
     2980                if (event.preventDefault) {
     2981                        event.preventDefault();
     2982                        event.stopPropagation();
     2983                } else {
     2984                        event.returnValue = false;
     2985                        event.cancelBubble = true;
     2986                }
     2987        },
     2988
     2989        // find the first node with the given tagName, starting from the
     2990        // node the event was triggered on; traverses the DOM upwards
     2991        findElement: function(event, tagName) {
     2992                var element = Event.element(event);
     2993                while (element.parentNode && (!element.tagName ||
     2994                                (element.tagName.toUpperCase() != tagName.toUpperCase())))
     2995                        element = element.parentNode;
     2996                return element;
     2997        },
     2998
     2999        observers: false,
     3000
     3001        _observeAndCache: function(element, name, observer, useCapture) {
     3002                if (!this.observers) this.observers = [];
     3003                if (element.addEventListener) {
     3004                        this.observers.push([element, name, observer, useCapture]);
     3005                        element.addEventListener(name, observer, useCapture);
     3006                } else if (element.attachEvent) {
     3007                        this.observers.push([element, name, observer, useCapture]);
     3008                        element.attachEvent('on' + name, observer);
     3009                }
     3010        },
     3011
     3012        unloadCache: function() {
     3013                if (!Event.observers) return;
     3014                for (var i = 0, length = Event.observers.length; i < length; i++) {
     3015                        Event.stopObserving.apply(this, Event.observers[i]);
     3016                        Event.observers[i][0] = null;
     3017                }
     3018                Event.observers = false;
     3019        },
     3020
     3021        observe: function(element, name, observer, useCapture) {
     3022                element = $(element);
     3023                useCapture = useCapture || false;
     3024
     3025                if (name == 'keypress' &&
     3026                        (Prototype.Browser.WebKit || element.attachEvent))
     3027                        name = 'keydown';
     3028
     3029                Event._observeAndCache(element, name, observer, useCapture);
     3030        },
     3031
     3032        stopObserving: function(element, name, observer, useCapture) {
     3033                element = $(element);
     3034                useCapture = useCapture || false;
     3035
     3036                if (name == 'keypress' &&
     3037                                (Prototype.Browser.WebKit || element.attachEvent))
     3038                        name = 'keydown';
     3039
     3040                if (element.removeEventListener) {
     3041                        element.removeEventListener(name, observer, useCapture);
     3042                } else if (element.detachEvent) {
     3043                        try {
     3044                                element.detachEvent('on' + name, observer);
     3045                        } catch (e) {}
     3046                }
     3047        }
    22863048});
    22873049
    22883050/* prevent memory leaks in IE */
    2289 if (navigator.appVersion.match(/\bMSIE\b/))
    2290   Event.observe(window, 'unload', Event.unloadCache, false);
     3051if (Prototype.Browser.IE)
     3052        Event.observe(window, 'unload', Event.unloadCache, false);
    22913053var Position = {
    2292   // set to true if needed, warning: firefox performance problems
    2293   // NOT neeeded for page scrolling, only if draggable contained in
    2294   // scrollable elements
    2295   includeScrollOffsets: false,
    2296 
    2297   // must be called before calling withinIncludingScrolloffset, every time the
    2298   // page is scrolled
    2299   prepare: function() {
    2300     this.deltaX =  window.pageXOffset
    2301                 || document.documentElement.scrollLeft
    2302                 || document.body.scrollLeft
    2303                 || 0;
    2304     this.deltaY =  window.pageYOffset
    2305                 || document.documentElement.scrollTop
    2306                 || document.body.scrollTop
    2307                 || 0;
    2308   },
    2309 
    2310   realOffset: function(element) {
    2311     var valueT = 0, valueL = 0;
    2312     do {
    2313       valueT += element.scrollTop  || 0;
    2314       valueL += element.scrollLeft || 0;
    2315       element = element.parentNode;
    2316     } while (element);
    2317     return [valueL, valueT];
    2318   },
    2319 
    2320   cumulativeOffset: function(element) {
    2321     var valueT = 0, valueL = 0;
    2322     do {
    2323       valueT += element.offsetTop  || 0;
    2324       valueL += element.offsetLeft || 0;
    2325       element = element.offsetParent;
    2326     } while (element);
    2327     return [valueL, valueT];
    2328   },
    2329 
    2330   positionedOffset: function(element) {
    2331     var valueT = 0, valueL = 0;
    2332     do {
    2333       valueT += element.offsetTop  || 0;
    2334       valueL += element.offsetLeft || 0;
    2335       element = element.offsetParent;
    2336       if (element) {
    2337         if(element.tagName=='BODY') break;
    2338         var p = Element.getStyle(element, 'position');
    2339         if (p == 'relative' || p == 'absolute') break;
    2340       }
    2341     } while (element);
    2342     return [valueL, valueT];
    2343   },
    2344 
    2345   offsetParent: function(element) {
    2346     if (element.offsetParent) return element.offsetParent;
    2347     if (element == document.body) return element;
    2348 
    2349     while ((element = element.parentNode) && element != document.body)
    2350       if (Element.getStyle(element, 'position') != 'static')
    2351         return element;
    2352 
    2353     return document.body;
    2354   },
    2355 
    2356   // caches x/y coordinate pair to use with overlap
    2357   within: function(element, x, y) {
    2358     if (this.includeScrollOffsets)
    2359       return this.withinIncludingScrolloffsets(element, x, y);
    2360     this.xcomp = x;
    2361     this.ycomp = y;
    2362     this.offset = this.cumulativeOffset(element);
    2363 
    2364     return (y >= this.offset[1] &&
    2365             y <  this.offset[1] + element.offsetHeight &&
    2366             x >= this.offset[0] &&
    2367             x <  this.offset[0] + element.offsetWidth);
    2368   },
    2369 
    2370   withinIncludingScrolloffsets: function(element, x, y) {
    2371     var offsetcache = this.realOffset(element);
    2372 
    2373     this.xcomp = x + offsetcache[0] - this.deltaX;
    2374     this.ycomp = y + offsetcache[1] - this.deltaY;
    2375     this.offset = this.cumulativeOffset(element);
    2376 
    2377     return (this.ycomp >= this.offset[1] &&
    2378             this.ycomp <  this.offset[1] + element.offsetHeight &&
    2379             this.xcomp >= this.offset[0] &&
    2380             this.xcomp <  this.offset[0] + element.offsetWidth);
    2381   },
    2382 
    2383   // within must be called directly before
    2384   overlap: function(mode, element) {
    2385     if (!mode) return 0;
    2386     if (mode == 'vertical')
    2387       return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
    2388         element.offsetHeight;
    2389     if (mode == 'horizontal')
    2390       return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
    2391         element.offsetWidth;
    2392   },
    2393 
    2394   page: function(forElement) {
    2395     var valueT = 0, valueL = 0;
    2396 
    2397     var element = forElement;
    2398     do {
    2399       valueT += element.offsetTop  || 0;
    2400       valueL += element.offsetLeft || 0;
    2401 
    2402       // Safari fix
    2403       if (element.offsetParent==document.body)
    2404         if (Element.getStyle(element,'position')=='absolute') break;
    2405 
    2406     } while (element = element.offsetParent);
    2407 
    2408     element = forElement;
    2409     do {
    2410       if (!window.opera || element.tagName=='BODY') {
    2411         valueT -= element.scrollTop  || 0;
    2412         valueL -= element.scrollLeft || 0;
    2413       }
    2414     } while (element = element.parentNode);
    2415 
    2416     return [valueL, valueT];
    2417   },
    2418 
    2419   clone: function(source, target) {
    2420     var options = Object.extend({
    2421       setLeft:    true,
    2422       setTop:     true,
    2423       setWidth:   true,
    2424       setHeight:  true,
    2425       offsetTop:  0,
    2426       offsetLeft: 0
    2427     }, arguments[2] || {})
    2428 
    2429     // find page position of source
    2430     source = $(source);
    2431     var p = Position.page(source);
    2432 
    2433     // find coordinate system to use
    2434     target = $(target);
    2435     var delta = [0, 0];
    2436     var parent = null;
    2437     // delta [0,0] will do fine with position: fixed elements,
    2438     // position:absolute needs offsetParent deltas
    2439     if (Element.getStyle(target,'position') == 'absolute') {
    2440       parent = Position.offsetParent(target);
    2441       delta = Position.page(parent);
    2442     }
    2443 
    2444     // correct by body offsets (fixes Safari)
    2445     if (parent == document.body) {
    2446       delta[0] -= document.body.offsetLeft;
    2447       delta[1] -= document.body.offsetTop;
    2448     }
    2449 
    2450     // set position
    2451     if(options.setLeft)   target.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
    2452     if(options.setTop)    target.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
    2453     if(options.setWidth)  target.style.width = source.offsetWidth + 'px';
    2454     if(options.setHeight) target.style.height = source.offsetHeight + 'px';
    2455   },
    2456 
    2457   absolutize: function(element) {
    2458     element = $(element);
    2459     if (element.style.position == 'absolute') return;
    2460     Position.prepare();
    2461 
    2462     var offsets = Position.positionedOffset(element);
    2463     var top     = offsets[1];
    2464     var left    = offsets[0];
    2465     var width   = element.clientWidth;
    2466     var height  = element.clientHeight;
    2467 
    2468     element._originalLeft   = left - parseFloat(element.style.left  || 0);
    2469     element._originalTop    = top  - parseFloat(element.style.top || 0);
    2470     element._originalWidth  = element.style.width;
    2471     element._originalHeight = element.style.height;
    2472 
    2473     element.style.position = 'absolute';
    2474     element.style.top    = top + 'px';
    2475     element.style.left   = left + 'px';
    2476     element.style.width  = width + 'px';
    2477     element.style.height = height + 'px';
    2478   },
    2479 
    2480   relativize: function(element) {
    2481     element = $(element);
    2482     if (element.style.position == 'relative') return;
    2483     Position.prepare();
    2484 
    2485     element.style.position = 'relative';
    2486     var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
    2487     var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
    2488 
    2489     element.style.top    = top + 'px';
    2490     element.style.left   = left + 'px';
    2491     element.style.height = element._originalHeight;
    2492     element.style.width  = element._originalWidth;
    2493   }
     3054        // set to true if needed, warning: firefox performance problems
     3055        // NOT neeeded for page scrolling, only if draggable contained in
     3056        // scrollable elements
     3057        includeScrollOffsets: false,
     3058
     3059        // must be called before calling withinIncludingScrolloffset, every time the
     3060        // page is scrolled
     3061        prepare: function() {
     3062                this.deltaX =  window.pageXOffset
     3063                                                                || document.documentElement.scrollLeft
     3064                                                                || document.body.scrollLeft
     3065                                                                || 0;
     3066                this.deltaY =  window.pageYOffset
     3067                                                                || document.documentElement.scrollTop
     3068                                                                || document.body.scrollTop
     3069                                                                || 0;
     3070        },
     3071
     3072        realOffset: function(element) {
     3073                var valueT = 0, valueL = 0;
     3074                do {
     3075                        valueT += element.scrollTop  || 0;
     3076                        valueL += element.scrollLeft || 0;
     3077                        element = element.parentNode;
     3078                } while (element);
     3079                return [valueL, valueT];
     3080        },
     3081
     3082        cumulativeOffset: function(element) {
     3083                var valueT = 0, valueL = 0;
     3084                do {
     3085                        valueT += element.offsetTop  || 0;
     3086                        valueL += element.offsetLeft || 0;
     3087                        element = element.offsetParent;
     3088                } while (element);
     3089                return [valueL, valueT];
     3090        },
     3091
     3092        positionedOffset: function(element) {
     3093                var valueT = 0, valueL = 0;
     3094                do {
     3095                        valueT += element.offsetTop  || 0;
     3096                        valueL += element.offsetLeft || 0;
     3097                        element = element.offsetParent;
     3098                        if (element) {
     3099                                if(element.tagName=='BODY') break;
     3100                                var p = Element.getStyle(element, 'position');
     3101                                if (p == 'relative' || p == 'absolute') break;
     3102                        }
     3103                } while (element);
     3104                return [valueL, valueT];
     3105        },
     3106
     3107        offsetParent: function(element) {
     3108                if (element.offsetParent) return element.offsetParent;
     3109                if (element == document.body) return element;
     3110
     3111                while ((element = element.parentNode) && element != document.body)
     3112                        if (Element.getStyle(element, 'position') != 'static')
     3113                                return element;
     3114
     3115                return document.body;
     3116        },
     3117
     3118        // caches x/y coordinate pair to use with overlap
     3119        within: function(element, x, y) {
     3120                if (this.includeScrollOffsets)
     3121                        return this.withinIncludingScrolloffsets(element, x, y);
     3122                this.xcomp = x;
     3123                this.ycomp = y;
     3124                this.offset = this.cumulativeOffset(element);
     3125
     3126                return (y >= this.offset[1] &&
     3127                                                y <  this.offset[1] + element.offsetHeight &&
     3128                                                x >= this.offset[0] &&
     3129                                                x <  this.offset[0] + element.offsetWidth);
     3130        },
     3131
     3132        withinIncludingScrolloffsets: function(element, x, y) {
     3133                var offsetcache = this.realOffset(element);
     3134
     3135                this.xcomp = x + offsetcache[0] - this.deltaX;
     3136                this.ycomp = y + offsetcache[1] - this.deltaY;
     3137                this.offset = this.cumulativeOffset(element);
     3138
     3139                return (this.ycomp >= this.offset[1] &&
     3140                                                this.ycomp <  this.offset[1] + element.offsetHeight &&
     3141                                                this.xcomp >= this.offset[0] &&
     3142                                                this.xcomp <  this.offset[0] + element.offsetWidth);
     3143        },
     3144
     3145        // within must be called directly before
     3146        overlap: function(mode, element) {
     3147                if (!mode) return 0;
     3148                if (mode == 'vertical')
     3149                        return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
     3150                                element.offsetHeight;
     3151                if (mode == 'horizontal')
     3152                        return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
     3153                                element.offsetWidth;
     3154        },
     3155
     3156        page: function(forElement) {
     3157                var valueT = 0, valueL = 0;
     3158
     3159                var element = forElement;
     3160                do {
     3161                        valueT += element.offsetTop  || 0;
     3162                        valueL += element.offsetLeft || 0;
     3163
     3164                        // Safari fix
     3165                        if (element.offsetParent == document.body)
     3166                                if (Element.getStyle(element,'position')=='absolute') break;
     3167
     3168                } while (element = element.offsetParent);
     3169
     3170                element = forElement;
     3171                do {
     3172                        if (!window.opera || element.tagName=='BODY') {
     3173                                valueT -= element.scrollTop  || 0;
     3174                                valueL -= element.scrollLeft || 0;
     3175                        }
     3176                } while (element = element.parentNode);
     3177
     3178                return [valueL, valueT];
     3179        },
     3180
     3181        clone: function(source, target) {
     3182                var options = Object.extend({
     3183                        setLeft:    true,
     3184                        setTop:     true,
     3185                        setWidth:   true,
     3186                        setHeight:  true,
     3187                        offsetTop:  0,
     3188                        offsetLeft: 0
     3189                }, arguments[2] || {})
     3190
     3191                // find page position of source
     3192                source = $(source);
     3193                var p = Position.page(source);
     3194
     3195                // find coordinate system to use
     3196                target = $(target);
     3197                var delta = [0, 0];
     3198                var parent = null;
     3199                // delta [0,0] will do fine with position: fixed elements,
     3200                // position:absolute needs offsetParent deltas
     3201                if (Element.getStyle(target,'position') == 'absolute') {
     3202                        parent = Position.offsetParent(target);
     3203                        delta = Position.page(parent);
     3204                }
     3205
     3206                // correct by body offsets (fixes Safari)
     3207                if (parent == document.body) {
     3208                        delta[0] -= document.body.offsetLeft;
     3209                        delta[1] -= document.body.offsetTop;
     3210                }
     3211
     3212                // set position
     3213                if(options.setLeft)   target.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
     3214                if(options.setTop)    target.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
     3215                if(options.setWidth)  target.style.width = source.offsetWidth + 'px';
     3216                if(options.setHeight) target.style.height = source.offsetHeight + 'px';
     3217        },
     3218
     3219        absolutize: function(element) {
     3220                element = $(element);
     3221                if (element.style.position == 'absolute') return;
     3222                Position.prepare();
     3223
     3224                var offsets = Position.positionedOffset(element);
     3225                var top     = offsets[1];
     3226                var left    = offsets[0];
     3227                var width   = element.clientWidth;
     3228                var height  = element.clientHeight;
     3229
     3230                element._originalLeft   = left - parseFloat(element.style.left  || 0);
     3231                element._originalTop    = top  - parseFloat(element.style.top || 0);
     3232                element._originalWidth  = element.style.width;
     3233                element._originalHeight = element.style.height;
     3234
     3235                element.style.position = 'absolute';
     3236                element.style.top    = top + 'px';
     3237                element.style.left   = left + 'px';
     3238                element.style.width  = width + 'px';
     3239                element.style.height = height + 'px';
     3240        },
     3241
     3242        relativize: function(element) {
     3243                element = $(element);
     3244                if (element.style.position == 'relative') return;
     3245                Position.prepare();
     3246
     3247                element.style.position = 'relative';
     3248                var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
     3249                var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
     3250
     3251                element.style.top    = top + 'px';
     3252                element.style.left   = left + 'px';
     3253                element.style.height = element._originalHeight;
     3254                element.style.width  = element._originalWidth;
     3255        }
    24943256}
    24953257
     
    24973259// positioned.  For performance reasons, redefine Position.cumulativeOffset for
    24983260// KHTML/WebKit only.
    2499 if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
    2500   Position.cumulativeOffset = function(element) {
    2501     var valueT = 0, valueL = 0;
    2502     do {
    2503       valueT += element.offsetTop  || 0;
    2504       valueL += element.offsetLeft || 0;
    2505       if (element.offsetParent == document.body)
    2506         if (Element.getStyle(element, 'position') == 'absolute') break;
    2507 
    2508       element = element.offsetParent;
    2509     } while (element);
    2510 
    2511     return [valueL, valueT];
    2512   }
     3261if (Prototype.Browser.WebKit) {
     3262        Position.cumulativeOffset = function(element) {
     3263                var valueT = 0, valueL = 0;
     3264                do {
     3265                        valueT += element.offsetTop  || 0;
     3266                        valueL += element.offsetLeft || 0;
     3267                        if (element.offsetParent == document.body)
     3268                                if (Element.getStyle(element, 'position') == 'absolute') break;
     3269
     3270                        element = element.offsetParent;
     3271                } while (element);
     3272
     3273                return [valueL, valueT];
     3274        }
    25133275}
    25143276
Note: See TracChangeset for help on using the changeset viewer.