Changeset 1852


Ignore:
Timestamp:
Feb 23, 2007, 2:18:34 PM (17 years ago)
Author:
rvelices
Message:

Plugins:

  • display author and and author url (if present) on plugin admin page
  • uniformized versions/authors... for all plugins in svn
  • security fix (html escape name, version, uri, author... to avoid javascript injection which could automatically simulate click on Install)
  • added confirmation for install/uninstall plugins

Web services:

  • web service explorer now caches method details in order to avoid unnecessary web calls
  • web service explorer can now send parameters as arrays
  • web service explorer uses now prototype.js version 1.5
  • small improvements
  • added and use function bad_request (sends http status code 400)
Location:
trunk
Files:
15 edited

Legend:

Unmodified
Added
Removed
  • trunk/admin/include/functions_plugins.inc.php

    r1731 r1852  
    4242          )
    4343      {
    44         $plugin = array('name'=>$file, 'version'=>'0', 'uri'=>'', 'description'=>'');
     44        $plugin = array(
     45            'name'=>$file,
     46            'version'=>'0',
     47            'uri'=>'',
     48            'description'=>'',
     49            'author'=>'',
     50          );
    4551        $plg_data = implode( '', file($path.'/main.inc.php') );
    4652
    47         if ( preg_match("|Plugin Name: (.*)|i", $plg_data, $val) )
     53        if ( preg_match("|Plugin Name: (.*)|", $plg_data, $val) )
    4854        {
    4955          $plugin['name'] = trim( $val[1] );
    5056        }
    51         if (preg_match("|Version: (.*)|i", $plg_data, $val))
     57        if (preg_match("|Version: (.*)|", $plg_data, $val))
    5258        {
    5359          $plugin['version'] = trim($val[1]);
    5460        }
    55         if ( preg_match("|Plugin URI: (.*)|i", $plg_data, $val) )
     61        if ( preg_match("|Plugin URI: (.*)|", $plg_data, $val) )
    5662        {
    57           $plugin['uri'] = $val[1];
     63          $plugin['uri'] = trim($val[1]);
    5864        }
    59         if ( preg_match("|Description: (.*)|i", $plg_data, $val) )
     65        if ( preg_match("|Description: (.*)|", $plg_data, $val) )
    6066        {
    6167          $plugin['description'] = trim($val[1]);
    6268        }
     69        if ( preg_match("|Author: (.*)|", $plg_data, $val) )
     70        {
     71          $plugin['author'] = trim($val[1]);
     72        }
     73        if ( preg_match("|Author URI: (.*)|", $plg_data, $val) )
     74        {
     75          $plugin['author uri'] = trim($val[1]);
     76        }
     77        // IMPORTANT SECURITY !
     78        $plugin = array_map('htmlspecialchars', $plugin);
    6379        $plugins[$file] = $plugin;
    6480      }
  • trunk/admin/plugins.php

    r1716 r1852  
    44// | Copyright (C) 2003-2007 PhpWebGallery Team - http://phpwebgallery.net |
    55// +-----------------------------------------------------------------------+
    6 // | branch        : BSF (Best So Far)
    76// | file          : $Id$
    87// | last update   : $Date$
     
    3938// |                     perform requested actions                         |
    4039// +-----------------------------------------------------------------------+
    41 if ( isset($_REQUEST['action']) and isset($_REQUEST['plugin'])  )
    42 {
    43   $plugin_id = $_REQUEST['plugin'];
     40if ( isset($_GET['action']) and isset($_GET['plugin'])  )
     41{
     42  $plugin_id = $_GET['plugin'];
    4443  $crt_db_plugin = get_db_plugins('', $plugin_id);
    4544  if (!empty($crt_db_plugin))
     
    5554  $file_to_include = PHPWG_PLUGINS_PATH.$plugin_id.'/maintain.inc.php';
    5655
    57   switch ( $_REQUEST['action'] )
     56  switch ( $_GET['action'] )
    5857  {
    5958    case 'install':
     
    9089      if ( !isset($crt_db_plugin) )
    9190      {
    92         array_push($errors, 'CANNOT '. $_REQUEST['action'] .' - NOT INSTALLED');
     91        array_push($errors, 'CANNOT '. $_GET['action'] .' - NOT INSTALLED');
    9392      }
    9493      if ($crt_db_plugin['state']!='inactive')
     
    115114      if ( !isset($crt_db_plugin) )
    116115      {
    117         die ('CANNOT '. $_REQUEST['action'] .' - NOT INSTALLED');
     116        die ('CANNOT '. $_GET['action'] .' - NOT INSTALLED');
    118117      }
    119118      if ($crt_db_plugin['state']!='active')
     
    135134      if ( !isset($crt_db_plugin) )
    136135      {
    137         die ('CANNOT '. $_REQUEST['action'] .' - NOT INSTALLED');
     136        die ('CANNOT '. $_GET['action'] .' - NOT INSTALLED');
    138137      }
    139138      $query = '
     
    182181    $display_name='<a href="'.$fs_plugin['uri'].'">'.$display_name.'</a>';
    183182  }
     183  $desc = $fs_plugin['description'];
     184  if (!empty($fs_plugin['author']))
     185  {
     186    $desc.= ' (<em>';
     187    if (!empty($fs_plugin['author uri']))
     188    {
     189      $desc.= '<a href="'.$fs_plugin['author uri'].'">'.$fs_plugin['author'].'</a>';
     190    }
     191    else
     192    {
     193      $desc.= $fs_plugin['author'];
     194    }
     195    $desc.= '</em>)';
     196  }
    184197  $template->assign_block_vars( 'plugins.plugin',
    185198      array(
    186199        'NAME' => $display_name,
    187200        'VERSION' => $fs_plugin['version'],
    188         'DESCRIPTION' => $fs_plugin['description'],
     201        'DESCRIPTION' => $desc,
    189202        'CLASS' => ($num++ % 2 == 1) ? 'row2' : 'row1',
    190203        )
     
    219232            )
    220233          );
     234        $template->assign_block_vars( 'plugins.plugin.action.confirm', array());
    221235        break;
    222236    }
     
    230244        )
    231245      );
     246    $template->assign_block_vars( 'plugins.plugin.action.confirm', array());
    232247  }
    233248}
  • trunk/include/functions_html.inc.php

    r1851 r1852  
    628628
    629629/**
     630 * exits the current script with 400 code
     631 * @param string msg a message to display
     632 * @param string alternate_url redirect to this url
     633 */
     634function bad_request($msg, $alternate_url=null)
     635{
     636  set_status_header(400);
     637  if ($alternate_url==null)
     638    $alternate_url = make_index_url();
     639  redirect_html( $alternate_url,
     640    '<div style="text-align:left; margin-left:5em;margin-bottom:5em;">
     641<h1 style="text-align:left; font-size:36px;">Bad request</h1><br/>'
     642.$msg.'</div>',
     643    5 );
     644}
     645
     646/**
    630647 * exits the current script with 404 code when a page cannot be found
    631648 * @param string msg a message to display
  • trunk/include/functions_tag.inc.php

    r1816 r1852  
    272272  return $tags;
    273273}
     274
     275/**
     276 * return a list of tags corresponding to any of ids, url_names, names
     277 *
     278 * @param array ids
     279 * @param array url_names
     280 * @param array names
     281 * @return array
     282 */
     283function find_tags($ids, $url_names=array(), $names=array() )
     284{
     285  $where_clauses = array();
     286  if ( !empty($ids) )
     287  {
     288    $where_clauses[] = 'id IN ('.implode(',', $ids).')';
     289  }
     290  if ( !empty($url_names) )
     291  {
     292    $where_clauses[] =
     293      'url_name IN ('.
     294      implode(
     295        ',',
     296        array_map(
     297          create_function('$s', 'return "\'".$s."\'";'),
     298          $url_names
     299          )
     300        )
     301      .')';
     302  }
     303  if ( !empty($names) )
     304  {
     305    $where_clauses[] =
     306      'name IN ('.
     307      implode(
     308        ',',
     309        array_map(
     310          create_function('$s', 'return "\'".$s."\'";'),
     311          $names
     312          )
     313        )
     314      .')';
     315  }
     316  if (empty($where_clauses))
     317  {
     318    return array();
     319  }
     320
     321  $query = '
     322SELECT id, url_name, name
     323  FROM '.TAGS_TABLE.'
     324  WHERE '. implode( '
     325    OR ', $where_clauses);
     326
     327  $result = pwg_query($query);
     328  $tags = array();
     329  while ($row = mysql_fetch_assoc($result))
     330  {
     331    array_push($tags, $row);
     332  }
     333  return $tags;
     334}
    274335?>
  • trunk/include/section_init.inc.php

    r1820 r1852  
    55// | Copyright (C) 2003-2007 PhpWebGallery Team - http://phpwebgallery.net |
    66// +-----------------------------------------------------------------------+
    7 // | branch        : BSF (Best So Far)
    87// | file          : $Id$
    98// | last update   : $Date$
     
    120119      else
    121120      {
    122         die('Fatal: picture identifier is missing');
     121        bad_request('picture identifier is missing');
    123122      }
    124123    }
     
    160159    else
    161160    {
    162       array_push($requested_tag_url_names, "'".$tokens[$i]."'");
     161      array_push($requested_tag_url_names, $tokens[$i]);
    163162    }
    164163    $i++;
     
    168167  if ( empty($requested_tag_ids) && empty($requested_tag_url_names) )
    169168  {
    170     die('Fatal: at least one tag required');
    171   }
    172   // tag infos
    173   $query = '
    174 SELECT name, url_name, id
    175   FROM '.TAGS_TABLE.'
    176   WHERE ';
    177   if ( !empty($requested_tag_ids) )
    178   {
    179     $query.= 'id IN ('.implode(',', $requested_tag_ids ).')';
    180   }
    181   if ( !empty($requested_tag_url_names) )
    182   {
    183     if ( !empty($requested_tag_ids) )
    184     {
    185       $query.= ' OR ';
    186     }
    187     $query.= 'url_name IN ('.implode(',', $requested_tag_url_names ).')';
    188   }
    189   $result = pwg_query($query);
    190   $tag_infos = array();
    191   while ($row = mysql_fetch_assoc($result))
    192   {
    193     $tag_infos[ $row['id'] ] = $row;
    194     array_push($page['tags'], $row );//we loose given tag order; is it important?
    195   }
     169    bad_request('at least one tag required');
     170  }
     171
     172  $page['tags'] = find_tags($requested_tag_ids, $requested_tag_url_names);
    196173  if ( empty($page['tags']) )
    197174  {
     
    229206  $next_token++;
    230207
    231   preg_match('/(\d+)/', $tokens[$next_token], $matches);
     208  preg_match('/(\d+)/', @$tokens[$next_token], $matches);
    232209  if (!isset($matches[1]))
    233210  {
    234     die('Fatal: search identifier is missing');
     211    bad_request('search identifier is missing');
    235212  }
    236213  $page['search'] = $matches[1];
     
    255232    if (!preg_match('/^\d+(,\d+)*$/', $tokens[$next_token]))
    256233    {
    257       die('wrong format on list GET parameter');
     234      bad_request('wrong format on list GET parameter');
    258235    }
    259236    foreach (explode(',', $tokens[$next_token]) as $image_id)
  • trunk/include/ws_core.inc.php

    r1781 r1852  
    465465          $flags |= WS_PARAM_OPTIONAL;
    466466        }
     467        if ( $flags & WS_PARAM_FORCE_ARRAY )
     468        {
     469          $flags |= WS_PARAM_ACCEPT_ARRAY;
     470        }
    467471        $options['flags'] = $flags;
    468472        $params[$param] = $options;
     
    605609        'name' => $name,
    606610        'optional' => ($options['flags']&WS_PARAM_OPTIONAL)?true:false,
     611        'acceptArray' => ($options['flags']&WS_PARAM_ACCEPT_ARRAY)?true:false,
    607612        );
    608613      if (isset($options['default']))
  • trunk/include/ws_functions.inc.php

    r1849 r1852  
    270270function ws_getVersion($params, &$service)
    271271{
    272 //  TODO = Version availability is under control of $conf['show_version']
    273   return PHPWG_VERSION;
     272  global $conf;
     273  if ($conf['show_version'])
     274    return PHPWG_VERSION;
     275  else
     276    return new PwgError(403, 'Forbidden');
    274277}
    275278
     
    337340
    338341    $order_by = ws_std_image_sql_order($params, 'i.');
    339     if (empty($order_by))
    340     {// TODO check for category order by (image_order)
    341       $order_by = $conf['order_by'];
    342     }
    343     else
    344     {
    345       $order_by = 'ORDER BY '.$order_by;
    346     }
     342    if ( empty($order_by)
     343          and count($params['cat_id'])==1
     344          and isset($cats[ $params['cat_id'][0] ]['image_order'])
     345        )
     346    {
     347      $order_by = $cats[ $params['cat_id'][0] ]['image_order'];
     348    }
     349    $order_by = empty($order_by) ? $conf['order_by'] : 'ORDER BY '.$order_by;
     350
    347351    $query = '
    348352SELECT i.*, GROUP_CONCAT(category_id) cat_ids
     
    500504function ws_images_addComment($params, &$service)
    501505{
     506  if (!$service->isPost())
     507  {
     508    return new PwgError(405, "This method requires HTTP POST");
     509  }
    502510  $params['image_id'] = (int)$params['image_id'];
    503511  $query = '
     
    580588  if ($image_row==null)
    581589  {
    582     return new PwgError(999, "image_id not found");
     590    return new PwgError(404, "image_id not found");
    583591  }
    584592  $image_row = array_merge( $image_row, ws_std_get_urls($image_row) );
     
    860868  if (!$service->isPost())
    861869  {
    862     return new PwgError(400, "This method requires POST");
     870    return new PwgError(405, "This method requires HTTP POST");
    863871  }
    864872  if (try_log_user($params['username'], $params['password'],false))
     
    943951  @include_once(PHPWG_ROOT_PATH.'include/functions_picture.inc.php');
    944952  global $conf;
    945 
     953 
    946954  // first build all the tag_ids we are interested in
    947   $tag_ids = array();
    948   $tags = get_available_tags();
     955  $params['tag_id'] = array_map( 'intval',$params['tag_id'] );
     956  $tags = find_tags($params['tag_id'], $params['tag_url_name'], $params['tag_name']);
    949957  $tags_by_id = array();
    950   for( $i=0; $i<count($tags); $i++ )
    951   {
    952     $tags[$i]['id']=(int)$tags[$i]['id'];
    953   }
    954958  foreach( $tags as $tag )
    955959  {
     960    $tags['id'] = (int)$tag['id'];
    956961    $tags_by_id[ $tag['id'] ] = $tag;
    957     if (
    958         in_array($tag['name'], $params['tag_name'])
    959       or
    960         in_array($tag['url_name'], $params['tag_url_name'])
    961       or
    962         in_array($tag['id'], $params['tag_id'])
    963        )
    964     {
    965       $tag_ids[] = $tag['id'];
    966     }
    967962  }
    968963  unset($tags);
    969 
    970   $tag_ids = array_unique( $tag_ids );
     964  $tag_ids = array_keys($tags_by_id);
     965
    971966
    972967  $image_ids = array();
  • trunk/plugins/add_index/main.inc.php

    r1705 r1852  
    1 <?php
    2 /*
     1<?php /*
    32Plugin Name: Add Index
    4 Version: 1.1.0.0
     3Version: 1.0
    54Description: Add file index.php file on all sub-directories of local galleries pictures. / Ajoute le fichier index.php sur les sous-répertoires de galeries d'images locales.
    65Plugin URI: http://www.phpwebgallery.net
     6Author: PhpWebGallery team
     7Author URI: http://www.phpwebgallery.net
    78*/
    89// +-----------------------------------------------------------------------+
  • trunk/plugins/admin_advices/main.inc.php

    r1841 r1852  
    11<?php /*
    2 Plugin Name: Admin Advices !
    3 Version: 1.0.0
    4 Author: PhpWebGallery team
     2Plugin Name: Admin Advices
     3Version: 1.0
    54Description: Give you an advice on the administration page.
    65Plugin URI: http://www.phpwebgallery.net
     6Author: PhpWebGallery team
     7Author URI: http://www.phpwebgallery.net
    78*/
    89
  • trunk/plugins/admin_multi_view/main.inc.php

    r1821 r1852  
    44Description: Allows administrators to view gallery as guests and/or change the language and/or theme on the fly. Practical to debug changes ...
    55Plugin URI: http://www.phpwebgallery.net
     6Author: PhpWebGallery team
     7Author URI: http://www.phpwebgallery.net
    68*/
    79
  • trunk/plugins/event_tracer/main.inc.php

    r1731 r1852  
    1 <?php
    2 /*
     1<?php /*
    32Plugin Name: Event tracer
    43Version: 1.0
    54Description: For developers. Shows all calls to trigger_event.
    65Plugin URI: http://www.phpwebgallery.net
     6Author: PhpWebGallery team
     7Author URI: http://www.phpwebgallery.net
    78*/
    89if (!defined('PHPWG_ROOT_PATH')) die('Hacking attempt!');
  • trunk/plugins/hello_world/main.inc.php

    r1590 r1852  
    11<?php /*
    2 Plugin Name: Hello World !
     2Plugin Name: Hello World
     3Version: 1.0
     4Description: This example plugin changes the page banner for the administration page.
     5Plugin URI: http://www.phpwebgallery.net
    36Author: PhpWebGallery team
    4 Description: This example plugin changes the page banner for the administration page.
     7Author URI: http://www.phpwebgallery.net
    58*/
    69
  • trunk/template/yoga/admin/plugins.tpl

    r1655 r1852  
    2020  <td>
    2121  <!-- BEGIN action -->
    22   <a href="{plugins.plugin.action.U_ACTION}" {TAG_INPUT_ENABLED}>{plugins.plugin.action.L_ACTION}</a>
     22  <a href="{plugins.plugin.action.U_ACTION}"
     23<!-- BEGIN confirm -->
     24    onclick="return confirm('Are you sure?');"
     25<!-- END confirm -->
     26    {TAG_INPUT_ENABLED}>{plugins.plugin.action.L_ACTION}</a>
    2327  <!-- END action -->
    2428  </td>
  • trunk/tools/prototype.js

    r1698 r1852  
    1 /*  Prototype JavaScript framework, version 1.4.0
    2  *  (c) 2005 Sam Stephenson <sam@conio.net>
     1/*  Prototype JavaScript framework, version 1.5.0
     2 *  (c) 2005-2007 Sam Stephenson
    33 *
    44 *  Prototype is freely distributable under the terms of an MIT-style license.
     
    88
    99var Prototype = {
    10   Version: '1.4.0',
     10  Version: '1.5.0',
     11  BrowserFeatures: {
     12    XPath: !!document.evaluate
     13  },
     14
    1115  ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
    12 
    1316  emptyFunction: function() {},
    14   K: function(x) {return x}
     17  K: function(x) { return x }
    1518}
    1619
     
    2629
    2730Object.extend = function(destination, source) {
    28   for (property in source) {
     31  for (var property in source) {
    2932    destination[property] = source[property];
    3033  }
     
    3235}
    3336
    34 Object.inspect = function(object) {
    35   try {
    36     if (object == undefined) return 'undefined';
    37     if (object == null) return 'null';
    38     return object.inspect ? object.inspect() : object.toString();
    39   } catch (e) {
    40     if (e instanceof RangeError) return '...';
    41     throw e;
    42   }
    43 }
     37Object.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  }
     66});
    4467
    4568Function.prototype.bind = function() {
     
    5174
    5275Function.prototype.bindAsEventListener = function(object) {
    53   var __method = this;
     76  var __method = this, args = $A(arguments), object = args.shift();
    5477  return function(event) {
    55     return __method.call(object, event || window.event);
     78    return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments)));
    5679  }
    5780}
     
    78101    var returnValue;
    79102
    80     for (var i = 0; i < arguments.length; i++) {
     103    for (var i = 0, length = arguments.length; i < length; i++) {
    81104      var lambda = arguments[i];
    82105      try {
     
    103126
    104127  registerCallback: function() {
    105     setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
     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;
    106135  },
    107136
     
    110139      try {
    111140        this.currentlyExecuting = true;
    112         this.callback();
     141        this.callback(this);
    113142      } finally {
    114143        this.currentlyExecuting = false;
     
    117146  }
    118147}
    119 
    120 /*--------------------------------------------------------------------------*/
    121 
    122 function $() {
    123   var elements = new Array();
    124 
    125   for (var i = 0; i < arguments.length; i++) {
    126     var element = arguments[i];
    127     if (typeof element == 'string')
    128       element = document.getElementById(element);
    129 
    130     if (arguments.length == 1)
    131       return element;
    132 
    133     elements.push(element);
    134   }
    135 
    136   return elements;
    137 }
     148String.interpret = function(value){
     149  return value == null ? '' : String(value);
     150}
     151
    138152Object.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
    139195  stripTags: function() {
    140196    return this.replace(/<\/?[^>]+>/gi, '');
     
    154210
    155211  evalScripts: function() {
    156     return this.extractScripts().map(eval);
     212    return this.extractScripts().map(function(script) { return eval(script) });
    157213  },
    158214
     
    167223    var div = document.createElement('div');
    168224    div.innerHTML = this.stripTags();
    169     return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
    170   },
    171 
    172   toQueryParams: function() {
    173     var pairs = this.match(/^\??(.*)$/)[1].split('&');
    174     return pairs.inject({}, function(params, pairString) {
    175       var pair = pairString.split('=');
    176       params[pair[0]] = pair[1];
    177       return params;
     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;
    178247    });
    179248  },
     
    183252  },
    184253
     254  succ: function() {
     255    return this.slice(0, this.length - 1) +
     256      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
     257  },
     258
    185259  camelize: function() {
    186     var oStringList = this.split('-');
    187     if (oStringList.length == 1) return oStringList[0];
    188 
    189     var camelizedString = this.indexOf('-') == 0
    190       ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
    191       : oStringList[0];
    192 
    193     for (var i = 1, len = oStringList.length; i < len; i++) {
    194       var s = oStringList[i];
    195       camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
    196     }
    197 
    198     return camelizedString;
    199   },
    200 
    201   inspect: function() {
    202     return "'" + this.replace('\\', '\\\\').replace("'", '\\\'') + "'";
     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, '\\\'') + "'";
    203291  }
    204292});
    205293
     294String.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) };
     298}
     299
    206300String.prototype.parseQuery = String.prototype.toQueryParams;
     301
     302var Template = Class.create();
     303Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
     304Template.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}
    207318
    208319var $break    = new Object();
     
    223334      if (e != $break) throw e;
    224335    }
     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);
    225344  },
    226345
     
    235354
    236355  any: function(iterator) {
    237     var result = true;
     356    var result = false;
    238357    this.each(function(value, index) {
    239358      if (result = !!(iterator || Prototype.K)(value, index))
     
    246365    var results = [];
    247366    this.each(function(value, index) {
    248       results.push(iterator(value, index));
     367      results.push((iterator || Prototype.K)(value, index));
    249368    });
    250369    return results;
    251370  },
    252371
    253   detect: function (iterator) {
     372  detect: function(iterator) {
    254373    var result;
    255374    this.each(function(value, index) {
     
    292411  },
    293412
     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
    294421  inject: function(memo, iterator) {
    295422    this.each(function(value, index) {
     
    301428  invoke: function(method) {
    302429    var args = $A(arguments).slice(1);
    303     return this.collect(function(value) {
     430    return this.map(function(value) {
    304431      return value[method].apply(value, args);
    305432    });
     
    310437    this.each(function(value, index) {
    311438      value = (iterator || Prototype.K)(value, index);
    312       if (value >= (result || value))
     439      if (result == undefined || value >= result)
    313440        result = value;
    314441    });
     
    320447    this.each(function(value, index) {
    321448      value = (iterator || Prototype.K)(value, index);
    322       if (value <= (result || value))
     449      if (result == undefined || value < result)
    323450        result = value;
    324451    });
     
    353480
    354481  sortBy: function(iterator) {
    355     return this.collect(function(value, index) {
     482    return this.map(function(value, index) {
    356483      return {value: value, criteria: iterator(value, index)};
    357484    }).sort(function(left, right) {
     
    362489
    363490  toArray: function() {
    364     return this.collect(Prototype.K);
     491    return this.map();
    365492  },
    366493
     
    372499    var collections = [this].concat(args).map($A);
    373500    return this.map(function(value, index) {
    374       iterator(value = collections.pluck(index));
    375       return value;
    376     });
     501      return iterator(collections.pluck(index));
     502    });
     503  },
     504
     505  size: function() {
     506    return this.toArray().length;
    377507  },
    378508
     
    395525  } else {
    396526    var results = [];
    397     for (var i = 0; i < iterable.length; i++)
     527    for (var i = 0, length = iterable.length; i < length; i++)
    398528      results.push(iterable[i]);
    399529    return results;
     
    403533Object.extend(Array.prototype, Enumerable);
    404534
    405 Array.prototype._reverse = Array.prototype.reverse;
     535if (!Array.prototype._reverse)
     536  Array.prototype._reverse = Array.prototype.reverse;
    406537
    407538Object.extend(Array.prototype, {
    408539  _each: function(iterator) {
    409     for (var i = 0; i < this.length; i++)
     540    for (var i = 0, length = this.length; i < length; i++)
    410541      iterator(this[i]);
    411542  },
     
    426557  compact: function() {
    427558    return this.select(function(value) {
    428       return value != undefined || value != null;
     559      return value != null;
    429560    });
    430561  },
     
    432563  flatten: function() {
    433564    return this.inject([], function(array, value) {
    434       return array.concat(value.constructor == Array ?
     565      return array.concat(value && value.constructor == Array ?
    435566        value.flatten() : [value]);
    436567    });
     
    445576
    446577  indexOf: function(object) {
    447     for (var i = 0; i < this.length; i++)
     578    for (var i = 0, length = this.length; i < length; i++)
    448579      if (this[i] == object) return i;
    449580    return -1;
     
    454585  },
    455586
    456   shift: function() {
    457     var result = this[0];
    458     for (var i = 0; i < this.length - 1; i++)
    459       this[i] = this[i + 1];
    460     this.length--;
    461     return result;
     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;
    462603  },
    463604
     
    466607  }
    467608});
    468 var Hash = {
     609
     610Array.prototype.toArray = Array.prototype.clone;
     611
     612function $w(string){
     613  string = string.strip();
     614  return string ? string.split(/\s+/) : [];
     615}
     616
     617if(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}
     632var Hash = function(obj) {
     633  Object.extend(this, obj || {});
     634};
     635
     636Object.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  }
     661});
     662
     663Object.extend(Hash.prototype, Enumerable);
     664Object.extend(Hash.prototype, {
    469665  _each: function(iterator) {
    470     for (key in this) {
     666    for (var key in this) {
    471667      var value = this[key];
    472       if (typeof value == 'function') continue;
     668      if (value && value == Hash.prototype[key]) continue;
    473669
    474670      var pair = [key, value];
     
    488684
    489685  merge: function(hash) {
    490     return $H(hash).inject($H(this), function(mergedHash, pair) {
     686    return $H(hash).inject(this, function(mergedHash, pair) {
    491687      mergedHash[pair.key] = pair.value;
    492688      return mergedHash;
     
    494690  },
    495691
     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
    496708  toQueryString: function() {
    497     return this.map(function(pair) {
    498       return pair.map(encodeURIComponent).join('=');
    499     }).join('&');
     709    return Hash.toQueryString(this);
    500710  },
    501711
     
    505715    }).join(', ') + '}>';
    506716  }
    507 }
     717});
    508718
    509719function $H(object) {
    510   var hash = Object.extend({}, object || {});
    511   Object.extend(hash, Enumerable);
    512   Object.extend(hash, Hash);
    513   return hash;
    514 }
     720  if (object && object.constructor == Hash) return object;
     721  return new Hash(object);
     722};
    515723ObjectRange = Class.create();
    516724Object.extend(ObjectRange.prototype, Enumerable);
     
    524732  _each: function(iterator) {
    525733    var value = this.start;
    526     do {
     734    while (this.include(value)) {
    527735      iterator(value);
    528736      value = value.succ();
    529     } while (this.include(value));
     737    }
    530738  },
    531739
     
    546754  getTransport: function() {
    547755    return Try.these(
     756      function() {return new XMLHttpRequest()},
    548757      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
    549       function() {return new ActiveXObject('Microsoft.XMLHTTP')},
    550       function() {return new XMLHttpRequest()}
     758      function() {return new ActiveXObject('Microsoft.XMLHTTP')}
    551759    ) || false;
    552760  },
     
    562770  },
    563771
    564   register: function(responderToAdd) {
    565     if (!this.include(responderToAdd))
    566       this.responders.push(responderToAdd);
    567   },
    568 
    569   unregister: function(responderToRemove) {
    570     this.responders = this.responders.without(responderToRemove);
     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);
    571779  },
    572780
    573781  dispatch: function(callback, request, transport, json) {
    574782    this.each(function(responder) {
    575       if (responder[callback] && typeof responder[callback] == 'function') {
     783      if (typeof responder[callback] == 'function') {
    576784        try {
    577785          responder[callback].apply(responder, [request, transport, json]);
     
    588796    Ajax.activeRequestCount++;
    589797  },
    590 
    591798  onComplete: function() {
    592799    Ajax.activeRequestCount--;
     
    600807      method:       'post',
    601808      asynchronous: true,
     809      contentType:  'application/x-www-form-urlencoded',
     810      encoding:     'UTF-8',
    602811      parameters:   ''
    603812    }
    604813    Object.extend(this.options, options || {});
    605   },
    606 
    607   responseIsSuccess: function() {
    608     return this.transport.status == undefined
    609         || this.transport.status == 0
    610         || (this.transport.status >= 200 && this.transport.status < 300);
    611   },
    612 
    613   responseIsFailure: function() {
    614     return !this.responseIsSuccess();
     814
     815    this.options.method = this.options.method.toLowerCase();
     816    if (typeof this.options.parameters == 'string')
     817      this.options.parameters = this.options.parameters.toQueryParams();
    615818  }
    616819}
     
    621824
    622825Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
     826  _complete: false,
     827
    623828  initialize: function(url, options) {
    624829    this.transport = Ajax.getTransport();
     
    628833
    629834  request: function(url) {
    630     var parameters = this.options.parameters || '';
    631     if (parameters.length > 0) parameters += '&_=';
     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;
    632851
    633852    try {
    634       this.url = url;
    635       if (this.options.method == 'get' && parameters.length > 0)
    636         this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;
    637 
    638853      Ajax.Responders.dispatch('onCreate', this, this.transport);
    639854
    640       this.transport.open(this.options.method, this.url,
     855      this.transport.open(this.method.toUpperCase(), this.url,
    641856        this.options.asynchronous);
    642857
    643       if (this.options.asynchronous) {
    644         this.transport.onreadystatechange = this.onStateChange.bind(this);
    645         setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
     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);
    646936      }
    647937
    648       this.setRequestHeaders();
    649 
    650       var body = this.options.postBody ? this.options.postBody : parameters;
    651       this.transport.send(this.options.method == 'post' ? body : null);
    652 
     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);
    653946    } catch (e) {
    654947      this.dispatchException(e);
    655948    }
    656   },
    657 
    658   setRequestHeaders: function() {
    659     var requestHeaders =
    660       ['X-Requested-With', 'XMLHttpRequest',
    661        'X-Prototype-Version', Prototype.Version];
    662 
    663     if (this.options.method == 'post') {
    664       requestHeaders.push('Content-type',
    665         'application/x-www-form-urlencoded');
    666 
    667       /* Force "Connection: close" for Mozilla browsers to work around
    668        * a bug where XMLHttpReqeuest sends an incorrect Content-length
    669        * header. See Mozilla Bugzilla #246651.
    670        */
    671       if (this.transport.overrideMimeType)
    672         requestHeaders.push('Connection', 'close');
    673     }
    674 
    675     if (this.options.requestHeaders)
    676       requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);
    677 
    678     for (var i = 0; i < requestHeaders.length; i += 2)
    679       this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
    680   },
    681 
    682   onStateChange: function() {
    683     var readyState = this.transport.readyState;
    684     if (readyState != 1)
    685       this.respondToReadyState(this.transport.readyState);
    686   },
    687 
    688   header: function(name) {
     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) {
    689957    try {
    690958      return this.transport.getResponseHeader(name);
    691     } catch (e) {}
     959    } catch (e) { return null }
    692960  },
    693961
    694962  evalJSON: function() {
    695963    try {
    696       return eval(this.header('X-JSON'));
    697     } catch (e) {}
     964      var json = this.getHeader('X-JSON');
     965      return json ? eval('(' + json + ')') : null;
     966    } catch (e) { return null }
    698967  },
    699968
     
    706975  },
    707976
    708   respondToReadyState: function(readyState) {
    709     var event = Ajax.Request.Events[readyState];
    710     var transport = this.transport, json = this.evalJSON();
    711 
    712     if (event == 'Complete') {
    713       try {
    714         (this.options['on' + this.transport.status]
    715          || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
    716          || Prototype.emptyFunction)(transport, json);
    717       } catch (e) {
    718         this.dispatchException(e);
    719       }
    720 
    721       if ((this.header('Content-type') || '').match(/^text\/javascript/i))
    722         this.evalResponse();
    723     }
    724 
    725     try {
    726       (this.options['on' + event] || Prototype.emptyFunction)(transport, json);
    727       Ajax.Responders.dispatch('on' + event, this, transport, json);
    728     } catch (e) {
    729       this.dispatchException(e);
    730     }
    731 
    732     /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
    733     if (event == 'Complete')
    734       this.transport.onreadystatechange = Prototype.emptyFunction;
    735   },
    736 
    737977  dispatchException: function(exception) {
    738978    (this.options.onException || Prototype.emptyFunction)(this, exception);
     
    745985Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
    746986  initialize: function(container, url, options) {
    747     this.containers = {
    748       success: container.success ? $(container.success) : $(container),
    749       failure: container.failure ? $(container.failure) :
    750         (container.success ? null : $(container))
     987    this.container = {
     988      success: (container.success || container),
     989      failure: (container.failure || (container.success ? null : container))
    751990    }
    752991
     
    755994
    756995    var onComplete = this.options.onComplete || Prototype.emptyFunction;
    757     this.options.onComplete = (function(transport, object) {
     996    this.options.onComplete = (function(transport, param) {
    758997      this.updateContent();
    759       onComplete(transport, object);
     998      onComplete(transport, param);
    760999    }).bind(this);
    7611000
     
    7641003
    7651004  updateContent: function() {
    766     var receiver = this.responseIsSuccess() ?
    767       this.containers.success : this.containers.failure;
     1005    var receiver = this.container[this.success() ? 'success' : 'failure'];
    7681006    var response = this.transport.responseText;
    7691007
    770     if (!this.options.evalScripts)
    771       response = response.stripScripts();
    772 
    773     if (receiver) {
    774       if (this.options.insertion) {
     1008    if (!this.options.evalScripts) response = response.stripScripts();
     1009
     1010    if (receiver = $(receiver)) {
     1011      if (this.options.insertion)
    7751012        new this.options.insertion(receiver, response);
    776       } else {
    777         Element.update(receiver, response);
    778       }
    779     }
    780 
    781     if (this.responseIsSuccess()) {
     1013      else
     1014        receiver.update(response);
     1015    }
     1016
     1017    if (this.success()) {
    7821018      if (this.onComplete)
    7831019        setTimeout(this.onComplete.bind(this), 10);
     
    8081044
    8091045  stop: function() {
    810     this.updater.onComplete = undefined;
     1046    this.updater.options.onComplete = undefined;
    8111047    clearTimeout(this.timer);
    8121048    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
     
    8281064  }
    8291065});
     1066function $(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);
     1075}
     1076
     1077if (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
    8301088document.getElementsByClassName = function(className, parentElement) {
    831   var children = ($(parentElement) || document.body).getElementsByTagName('*');
    832   return $A(children).inject([], function(elements, child) {
    833     if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
    834       elements.push(child);
     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    }
    8351100    return elements;
    836   });
    837 }
     1101  }
     1102};
    8381103
    8391104/*--------------------------------------------------------------------------*/
    8401105
    841 if (!window.Element) {
     1106if (!window.Element)
    8421107  var Element = new Object();
    843 }
    844 
    845 Object.extend(Element, {
     1108
     1109Element.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;
     1131};
     1132
     1133Element.extend.cache = {
     1134  findOrStore: function(value) {
     1135    return this[value] = this[value] || function() {
     1136      return value.apply(null, [this].concat($A(arguments)));
     1137    }
     1138  }
     1139};
     1140
     1141Element.Methods = {
    8461142  visible: function(element) {
    8471143    return $(element).style.display != 'none';
    8481144  },
    8491145
    850   toggle: function() {
    851     for (var i = 0; i < arguments.length; i++) {
    852       var element = $(arguments[i]);
    853       Element[Element.visible(element) ? 'hide' : 'show'](element);
    854     }
    855   },
    856 
    857   hide: function() {
    858     for (var i = 0; i < arguments.length; i++) {
    859       var element = $(arguments[i]);
    860       element.style.display = 'none';
    861     }
    862   },
    863 
    864   show: function() {
    865     for (var i = 0; i < arguments.length; i++) {
    866       var element = $(arguments[i]);
    867       element.style.display = '';
    868     }
     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;
    8691160  },
    8701161
     
    8721163    element = $(element);
    8731164    element.parentNode.removeChild(element);
     1165    return element;
    8741166  },
    8751167
    8761168  update: function(element, html) {
     1169    html = typeof html == 'undefined' ? '' : html.toString();
    8771170    $(element).innerHTML = html.stripScripts();
    8781171    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);
    8791279  },
    8801280
    8811281  getHeight: function(element) {
    882     element = $(element);
    883     return element.offsetHeight;
     1282    return $(element).getDimensions().height;
     1283  },
     1284
     1285  getWidth: function(element) {
     1286    return $(element).getDimensions().width;
    8841287  },
    8851288
     
    8901293  hasClassName: function(element, className) {
    8911294    if (!(element = $(element))) return;
    892     return Element.classNames(element).include(className);
     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;
    8931301  },
    8941302
    8951303  addClassName: function(element, className) {
    8961304    if (!(element = $(element))) return;
    897     return Element.classNames(element).add(className);
     1305    Element.classNames(element).add(className);
     1306    return element;
    8981307  },
    8991308
    9001309  removeClassName: function(element, className) {
    9011310    if (!(element = $(element))) return;
    902     return Element.classNames(element).remove(className);
     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();
    9031329  },
    9041330
     
    9061332  cleanWhitespace: function(element) {
    9071333    element = $(element);
    908     for (var i = 0; i < element.childNodes.length; i++) {
    909       var node = element.childNodes[i];
     1334    var node = element.firstChild;
     1335    while (node) {
     1336      var nextNode = node.nextSibling;
    9101337      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
    911         Element.remove(node);
    912     }
     1338        element.removeChild(node);
     1339      node = nextNode;
     1340    }
     1341    return element;
    9131342  },
    9141343
     
    9171346  },
    9181347
     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
    9191355  scrollTo: function(element) {
    9201356    element = $(element);
    921     var x = element.x ? element.x : element.offsetLeft,
    922         y = element.y ? element.y : element.offsetTop;
    923     window.scrollTo(x, y);
     1357    var pos = Position.cumulativeOffset(element);
     1358    window.scrollTo(pos[0], pos[1]);
     1359    return element;
    9241360  },
    9251361
    9261362  getStyle: function(element, style) {
    9271363    element = $(element);
    928     var value = element.style[style.camelize()];
     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];
    9291368    if (!value) {
    9301369      if (document.defaultView && document.defaultView.getComputedStyle) {
    9311370        var css = document.defaultView.getComputedStyle(element, null);
    932         value = css ? css.getPropertyValue(style) : null;
     1371        value = css ? css[style] : null;
    9331372      } else if (element.currentStyle) {
    934         value = element.currentStyle[style.camelize()];
     1373        value = element.currentStyle[style];
    9351374      }
    9361375    }
     1376
     1377    if((value == 'auto') && ['width','height'].include(style) && (element.getStyle('display') != 'none'))
     1378      value = element['offset'+style.capitalize()] + 'px';
    9371379
    9381380    if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
    9391381      if (Element.getStyle(element, 'position') == 'static') value = 'auto';
    940 
     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    }
    9411388    return value == 'auto' ? null : value;
    9421389  },
     
    9441391  setStyle: function(element, style) {
    9451392    element = $(element);
    946     for (name in style)
    947       element.style[name.camelize()] = style[name];
     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;
    9481414  },
    9491415
    9501416  getDimensions: function(element) {
    9511417    element = $(element);
    952     if (Element.getStyle(element, 'display') != 'none')
     1418    var display = $(element).getStyle('display');
     1419    if (display != 'none' && display != null) // Safari bug
    9531420      return {width: element.offsetWidth, height: element.offsetHeight};
    9541421
     
    9581425    var originalVisibility = els.visibility;
    9591426    var originalPosition = els.position;
     1427    var originalDisplay = els.display;
    9601428    els.visibility = 'hidden';
    9611429    els.position = 'absolute';
    962     els.display = '';
     1430    els.display = 'block';
    9631431    var originalWidth = element.clientWidth;
    9641432    var originalHeight = element.clientHeight;
    965     els.display = 'none';
     1433    els.display = originalDisplay;
    9661434    els.position = originalPosition;
    9671435    els.visibility = originalVisibility;
     
    9821450      }
    9831451    }
     1452    return element;
    9841453  },
    9851454
     
    9941463        element.style.right = '';
    9951464    }
     1465    return element;
    9961466  },
    9971467
    9981468  makeClipping: function(element) {
    9991469    element = $(element);
    1000     if (element._overflow) return;
    1001     element._overflow = element.style.overflow;
     1470    if (element._overflow) return element;
     1471    element._overflow = element.style.overflow || 'auto';
    10021472    if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
    10031473      element.style.overflow = 'hidden';
     1474    return element;
    10041475  },
    10051476
    10061477  undoClipping: function(element) {
    10071478    element = $(element);
    1008     if (element._overflow) return;
    1009     element.style.overflow = element._overflow;
    1010     element._overflow = undefined;
    1011   }
     1479    if (!element._overflow) return element;
     1480    element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
     1481    element._overflow = null;
     1482    return element;
     1483  }
     1484};
     1485
     1486Object.extend(Element.Methods, {childOf: Element.Methods.descendantOf});
     1487
     1488Element._attributeTranslations = {};
     1489
     1490Element._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"
     1501};
     1502
     1503Element._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  }
     1520};
     1521
     1522Object.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
    10121529});
     1530
     1531Element.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  }
     1537};
     1538
     1539// IE is missing .innerHTML support for TABLE-related elements
     1540if (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  }
     1574};
     1575
     1576Object.extend(Element, Element.Methods);
     1577
     1578var _nativeExtensions = false;
     1579
     1580if(/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
     1588Element.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}
    10131611
    10141612var Toggle = new Object();
     
    10301628        this.element.insertAdjacentHTML(this.adjacency, this.content);
    10311629      } catch (e) {
    1032         if (this.element.tagName.toLowerCase() == 'tbody') {
     1630        var tagName = this.element.tagName.toUpperCase();
     1631        if (['TBODY', 'TR'].include(tagName)) {
    10331632          this.insertContent(this.contentFromAnonymousTable());
    10341633        } else {
     
    11291728  add: function(classNameToAdd) {
    11301729    if (this.include(classNameToAdd)) return;
    1131     this.set(this.toArray().concat(classNameToAdd).join(' '));
     1730    this.set($A(this).concat(classNameToAdd).join(' '));
    11321731  },
    11331732
    11341733  remove: function(classNameToRemove) {
    11351734    if (!this.include(classNameToRemove)) return;
    1136     this.set(this.select(function(className) {
    1137       return className != classNameToRemove;
    1138     }).join(' '));
     1735    this.set($A(this).without(classNameToRemove).join(' '));
    11391736  },
    11401737
    11411738  toString: function() {
    1142     return this.toArray().join(' ');
    1143   }
    1144 }
     1739    return $A(this).join(' ');
     1740  }
     1741};
    11451742
    11461743Object.extend(Element.ClassNames.prototype, Enumerable);
    1147 var Field = {
    1148   clear: function() {
    1149     for (var i = 0; i < arguments.length; i++)
    1150       $(arguments[i]).value = '';
    1151   },
    1152 
     1744var Selector = Class.create();
     1745Selector.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}
     1846
     1847Object.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  }
     1868});
     1869
     1870function $$() {
     1871  return Selector.findChildElements(document, $A(arguments));
     1872}
     1873var 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  }
     1896};
     1897
     1898Form.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
     1960Object.extend(Form, Form.Methods);
     1961
     1962/*--------------------------------------------------------------------------*/
     1963
     1964Form.Element = {
    11531965  focus: function(element) {
    11541966    $(element).focus();
    1155   },
    1156 
    1157   present: function() {
    1158     for (var i = 0; i < arguments.length; i++)
    1159       if ($(arguments[i]).value == '') return false;
    1160     return true;
     1967    return element;
    11611968  },
    11621969
    11631970  select: function(element) {
    11641971    $(element).select();
     1972    return element;
     1973  }
     1974}
     1975
     1976Form.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 != '';
    11652003  },
    11662004
     
    11682006    element = $(element);
    11692007    element.focus();
    1170     if (element.select)
     2008    if (element.select && ( element.tagName.toLowerCase() != 'input' ||
     2009      !['button', 'reset', 'submit'].include(element.type) ) )
    11712010      element.select();
    1172   }
    1173 }
     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
     2028Object.extend(Form.Element, Form.Element.Methods);
     2029var Field = Form.Element;
     2030var $F = Form.Element.getValue;
    11742031
    11752032/*--------------------------------------------------------------------------*/
    1176 
    1177 var Form = {
    1178   serialize: function(form) {
    1179     var elements = Form.getElements($(form));
    1180     var queryComponents = new Array();
    1181 
    1182     for (var i = 0; i < elements.length; i++) {
    1183       var queryComponent = Form.Element.serialize(elements[i]);
    1184       if (queryComponent)
    1185         queryComponents.push(queryComponent);
    1186     }
    1187 
    1188     return queryComponents.join('&');
    1189   },
    1190 
    1191   getElements: function(form) {
    1192     form = $(form);
    1193     var elements = new Array();
    1194 
    1195     for (tagName in Form.Element.Serializers) {
    1196       var tagElements = form.getElementsByTagName(tagName);
    1197       for (var j = 0; j < tagElements.length; j++)
    1198         elements.push(tagElements[j]);
    1199     }
    1200     return elements;
    1201   },
    1202 
    1203   getInputs: function(form, typeName, name) {
    1204     form = $(form);
    1205     var inputs = form.getElementsByTagName('input');
    1206 
    1207     if (!typeName && !name)
    1208       return inputs;
    1209 
    1210     var matchingInputs = new Array();
    1211     for (var i = 0; i < inputs.length; i++) {
    1212       var input = inputs[i];
    1213       if ((typeName && input.type != typeName) ||
    1214           (name && input.name != name))
    1215         continue;
    1216       matchingInputs.push(input);
    1217     }
    1218 
    1219     return matchingInputs;
    1220   },
    1221 
    1222   disable: function(form) {
    1223     var elements = Form.getElements(form);
    1224     for (var i = 0; i < elements.length; i++) {
    1225       var element = elements[i];
    1226       element.blur();
    1227       element.disabled = 'true';
    1228     }
    1229   },
    1230 
    1231   enable: function(form) {
    1232     var elements = Form.getElements(form);
    1233     for (var i = 0; i < elements.length; i++) {
    1234       var element = elements[i];
    1235       element.disabled = '';
    1236     }
    1237   },
    1238 
    1239   findFirstElement: function(form) {
    1240     return Form.getElements(form).find(function(element) {
    1241       return element.type != 'hidden' && !element.disabled &&
    1242         ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
    1243     });
    1244   },
    1245 
    1246   focusFirstElement: function(form) {
    1247     Field.activate(Form.findFirstElement(form));
    1248   },
    1249 
    1250   reset: function(form) {
    1251     $(form).reset();
    1252   }
    1253 }
    1254 
    1255 Form.Element = {
    1256   serialize: function(element) {
    1257     element = $(element);
    1258     var method = element.tagName.toLowerCase();
    1259     var parameter = Form.Element.Serializers[method](element);
    1260 
    1261     if (parameter) {
    1262       var key = encodeURIComponent(parameter[0]);
    1263       if (key.length == 0) return;
    1264 
    1265       if (parameter[1].constructor != Array)
    1266         parameter[1] = [parameter[1]];
    1267 
    1268       return parameter[1].map(function(value) {
    1269         return key + '=' + encodeURIComponent(value);
    1270       }).join('&');
    1271     }
    1272   },
    1273 
    1274   getValue: function(element) {
    1275     element = $(element);
    1276     var method = element.tagName.toLowerCase();
    1277     var parameter = Form.Element.Serializers[method](element);
    1278 
    1279     if (parameter)
    1280       return parameter[1];
    1281   }
    1282 }
    12832033
    12842034Form.Element.Serializers = {
    12852035  input: function(element) {
    12862036    switch (element.type.toLowerCase()) {
    1287       case 'submit':
    1288       case 'hidden':
    1289       case 'password':
    1290       case 'text':
    1291         return Form.Element.Serializers.textarea(element);
    12922037      case 'checkbox':
    12932038      case 'radio':
    12942039        return Form.Element.Serializers.inputSelector(element);
    1295     }
    1296     return false;
     2040      default:
     2041        return Form.Element.Serializers.textarea(element);
     2042    }
    12972043  },
    12982044
    12992045  inputSelector: function(element) {
    1300     if (element.checked)
    1301       return [element.name, element.value];
     2046    return element.checked ? element.value : null;
    13022047  },
    13032048
    13042049  textarea: function(element) {
    1305     return [element.name, element.value];
     2050    return element.value;
    13062051  },
    13072052
    13082053  select: function(element) {
    1309     return Form.Element.Serializers[element.type == 'select-one' ?
     2054    return this[element.type == 'select-one' ?
    13102055      'selectOne' : 'selectMany'](element);
    13112056  },
    13122057
    13132058  selectOne: function(element) {
    1314     var value = '', opt, index = element.selectedIndex;
    1315     if (index >= 0) {
    1316       opt = element.options[index];
    1317       value = opt.value;
    1318       if (!value && !('value' in opt))
    1319         value = opt.text;
    1320     }
    1321     return [element.name, value];
     2059    var index = element.selectedIndex;
     2060    return index >= 0 ? this.optionValue(element.options[index]) : null;
    13222061  },
    13232062
    13242063  selectMany: function(element) {
    1325     var value = new Array();
    1326     for (var i = 0; i < element.length; i++) {
     2064    var values, length = element.length;
     2065    if (!length) return null;
     2066
     2067    for (var i = 0, values = []; i < length; i++) {
    13272068      var opt = element.options[i];
    1328       if (opt.selected) {
    1329         var optValue = opt.value;
    1330         if (!optValue && !('value' in opt))
    1331           optValue = opt.text;
    1332         value.push(optValue);
    1333       }
    1334     }
    1335     return [element.name, value];
    1336   }
    1337 }
    1338 
    1339 /*--------------------------------------------------------------------------*/
    1340 
    1341 var $F = Form.Element.getValue;
     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  }
     2078}
    13422079
    13432080/*--------------------------------------------------------------------------*/
     
    13602097  onTimerEvent: function() {
    13612098    var value = this.getValue();
    1362     if (this.lastValue != value) {
     2099    var changed = ('string' == typeof this.lastValue && 'string' == typeof value
     2100      ? this.lastValue != value : String(this.lastValue) != String(value));
     2101    if (changed) {
    13632102      this.callback(this.element, value);
    13642103      this.lastValue = value;
     
    14052144
    14062145  registerFormCallbacks: function() {
    1407     var elements = Form.getElements(this.element);
    1408     for (var i = 0; i < elements.length; i++)
    1409       this.registerCallback(elements[i]);
     2146    Form.getElements(this.element).each(this.registerCallback.bind(this));
    14102147  },
    14112148
     
    14172154          Event.observe(element, 'click', this.onElementEvent.bind(this));
    14182155          break;
    1419         case 'password':
    1420         case 'text':
    1421         case 'textarea':
    1422         case 'select-one':
    1423         case 'select-multiple':
     2156        default:
    14242157          Event.observe(element, 'change', this.onElementEvent.bind(this));
    14252158          break;
     
    14562189  KEY_DOWN:     40,
    14572190  KEY_DELETE:   46,
     2191  KEY_HOME:     36,
     2192  KEY_END:      35,
     2193  KEY_PAGEUP:   33,
     2194  KEY_PAGEDOWN: 34,
    14582195
    14592196  element: function(event) {
     
    15112248  unloadCache: function() {
    15122249    if (!Event.observers) return;
    1513     for (var i = 0; i < Event.observers.length; i++) {
     2250    for (var i = 0, length = Event.observers.length; i < length; i++) {
    15142251      Event.stopObserving.apply(this, Event.observers[i]);
    15152252      Event.observers[i][0] = null;
     
    15192256
    15202257  observe: function(element, name, observer, useCapture) {
    1521     var element = $(element);
     2258    element = $(element);
    15222259    useCapture = useCapture || false;
    15232260
     
    15272264      name = 'keydown';
    15282265
    1529     this._observeAndCache(element, name, observer, useCapture);
     2266    Event._observeAndCache(element, name, observer, useCapture);
    15302267  },
    15312268
    15322269  stopObserving: function(element, name, observer, useCapture) {
    1533     var element = $(element);
     2270    element = $(element);
    15342271    useCapture = useCapture || false;
    15352272
     
    15422279      element.removeEventListener(name, observer, useCapture);
    15432280    } else if (element.detachEvent) {
    1544       element.detachEvent('on' + name, observer);
     2281      try {
     2282        element.detachEvent('on' + name, observer);
     2283      } catch (e) {}
    15452284    }
    15462285  }
     
    15482287
    15492288/* prevent memory leaks in IE */
    1550 Event.observe(window, 'unload', Event.unloadCache, false);
     2289if (navigator.appVersion.match(/\bMSIE\b/))
     2290  Event.observe(window, 'unload', Event.unloadCache, false);
    15512291var Position = {
    15522292  // set to true if needed, warning: firefox performance problems
     
    15952335      element = element.offsetParent;
    15962336      if (element) {
    1597         p = Element.getStyle(element, 'position');
     2337        if(element.tagName=='BODY') break;
     2338        var p = Element.getStyle(element, 'position');
    15982339        if (p == 'relative' || p == 'absolute') break;
    15992340      }
     
    16512392  },
    16522393
    1653   clone: function(source, target) {
    1654     source = $(source);
    1655     target = $(target);
    1656     target.style.position = 'absolute';
    1657     var offsets = this.cumulativeOffset(source);
    1658     target.style.top    = offsets[1] + 'px';
    1659     target.style.left   = offsets[0] + 'px';
    1660     target.style.width  = source.offsetWidth + 'px';
    1661     target.style.height = source.offsetHeight + 'px';
    1662   },
    1663 
    16642394  page: function(forElement) {
    16652395    var valueT = 0, valueL = 0;
     
    16782408    element = forElement;
    16792409    do {
    1680       valueT -= element.scrollTop  || 0;
    1681       valueL -= element.scrollLeft || 0;
     2410      if (!window.opera || element.tagName=='BODY') {
     2411        valueT -= element.scrollTop  || 0;
     2412        valueL -= element.scrollLeft || 0;
     2413      }
    16822414    } while (element = element.parentNode);
    16832415
     
    17402472
    17412473    element.style.position = 'absolute';
    1742     element.style.top    = top + 'px';;
    1743     element.style.left   = left + 'px';;
    1744     element.style.width  = width + 'px';;
    1745     element.style.height = height + 'px';;
     2474    element.style.top    = top + 'px';
     2475    element.style.left   = left + 'px';
     2476    element.style.width  = width + 'px';
     2477    element.style.height = height + 'px';
    17462478  },
    17472479
     
    17802512  }
    17812513}
     2514
     2515Element.addMethods();
  • trunk/tools/ws.htm

    r1849 r1852  
    66
    77<script type="text/javascript">
    8 function setElementText(id, text)
    9 {
    10   if (!text) text="";
    11   var elt = document.getElementById(id);
    12   if (!elt) alert('setElementText '+id);
    13   elt.innerHTML = text;
    14 }
    158
    169function setVisibility(id, vis)
    1710{
    18   document.getElementById(id).style.visibility = vis;
    19 }
    20 
    21 function clearError()
    22 {
    23   setElementText("error", "");
     11  $(id).style.visibility = vis;
    2412}
    2513
     
    3826         }
    3927        }
    40         setElementText("error", s);
     28  $("error").update(s);
    4129}
    4230
    4331var gServiceUrl;
    44 var gCurrentMethodParams;
     32var gCachedMethods;
    4533
    4634Ajax.Responders.register({
     
    10189function pwgChangeUrl()
    10290{
    103   clearError();
     91  $("error").update("");
    10492  setVisibility("methodListWrapper", "hidden");
    105   setElementText("methodList", "");
     93  $("methodList").update("");
    10694  setVisibility("methodWrapper", "hidden");
     95  setVisibility("methodDetailWrapper", "hidden");
    10796
    10897  gServiceUrl = $F('ws_url');
    109   gCurrentMethodParams = null;
     98  gCachedMethods = new Hash();
    11099
    111100  try {
     
    131120                ml += '<li><a href="#" onclick="return pwgSelectMethod(this.innerHTML)">'+ result.methods[i]+'</a></li>';
    132121        }
    133         setElementText("methodList", ml);
     122        $("methodList").update(ml);
    134123        setVisibility("methodListWrapper", "visible");
    135124}
    136125
    137 function pwgSelectMethod(method)
    138 {
    139   clearError();
    140   setElementText("methodName", method);
     126function pwgSelectMethod(methodName)
     127{
     128  $("error").update("");
     129  $("methodName").update(methodName);
    141130  setVisibility("methodDetailWrapper", "hidden");
    142131  setVisibility("methodWrapper", "visible");
    143   gCurrentMethodParams = null;
    144 
    145   try {
    146 
    147                 var ajaxReq = new Ajax.Request(
    148                                 gServiceUrl,
    149                                 {method:'get', parameters:'format=json&method=reflection.getMethodDetails&methodName='+method,
    150                                  onSuccess: function (r) { onSuccess_getMethodDetails(r); }
    151                                 }
    152                         )
    153   }catch (e)
     132
     133  if ( gCachedMethods[methodName] )
     134    fillNewMethod( gCachedMethods[methodName] );
     135  else
    154136  {
    155     dumpError( e );
     137    try {
     138                var ajaxReq = new Ajax.Request(
     139                                gServiceUrl,
     140                                {method:'get', parameters:'format=json&method=reflection.getMethodDetails&methodName='+methodName,
     141                                 onSuccess: function (r) { onSuccess_getMethodDetails(r); }
     142                                }
     143                        )
     144    }catch (e)
     145    {
     146      dumpError( e );
     147    }
    156148  }
    157149  return false;
     
    161153{
    162154        var result = pwgGetJsonResult(transport);
     155  fillNewMethod( gCachedMethods[result.name] = $H(result) );
     156}
     157
     158function fillNewMethod(method)
     159{
    163160        var methodParamsElt = $("methodParams");
    164161        while (methodParamsElt.tBodies[0].rows.length)
    165162                methodParamsElt.tBodies[0].deleteRow(methodParamsElt.tBodies[0].rows.length-1);
    166163
    167         if (result.params)
    168   {
    169     gCurrentMethodParams = result.params;
    170     if (result.params.length>0)
    171           {
    172                 for (var i=0; i<result.params.length; i++)
     164        if (method.params && method.params.length>0)
     165        {
     166                for (var i=0; i<method.params.length; i++)
    173167                {
    174168                        var row = methodParamsElt.tBodies[0].insertRow(-1);
    175                         var isOptional = result.params[i].optional;
    176                         var defaultValue = result.params[i].defaultValue == null ? '' : result.params[i].defaultValue;
     169                        var isOptional = method.params[i].optional;
     170                        var acceptArray = method.params[i].acceptArray;
     171                        var defaultValue = method.params[i].defaultValue == null ? '' : method.params[i].defaultValue;
    177172 
    178                         row.insertCell(0).innerHTML = result.params[i].name;
    179                         row.insertCell(1).innerHTML = (isOptional ? 'optional':'required');
     173                        row.insertCell(0).innerHTML = method.params[i].name;
     174                        row.insertCell(1).innerHTML = '<span title="parameter is '+(isOptional ? 'optional':'required') +'">'+(isOptional ? '?':'*')+'</span>'
     175          + (method.params[i].acceptArray ? ' <span title="parameter can be an array; use | (pipe) character to split values">[ ]</span>':'');
    180176                        row.insertCell(2).innerHTML = '<input id="methodParameterSend_'+i+'" type="checkbox" '+(isOptional ? '':'checked="checked"')+'/>';
    181177                        row.insertCell(3).innerHTML = '<input id="methodParameterValue_'+i+'"" value="'+defaultValue+'" style="width:99%" onchange="$(\'methodParameterSend_'+i+'\').checked=true;"/>';
    182178                }
    183         }
    184         }
    185         setElementText("methodDescription", result.description);
     179        }
     180        $("methodDescription").update(method.description);
    186181        setVisibility("methodDetailWrapper", "visible");
    187182}
     
    189184function pwgInvokeMethod( newWindow )
    190185{
    191         var method = document.getElementById('methodName').innerHTML;
     186        var methodName = $('methodName').innerHTML;
     187        var method = gCachedMethods[methodName];
    192188
    193189  var reqUrl = gServiceUrl;
    194190  reqUrl += "?format="+$F('responseFormat');
    195191
    196   if (document.getElementById('requestFormat').value == 'get')
     192  if ($('requestFormat').value == 'get')
    197193  {
    198     reqUrl += "&method="+method;
    199     for ( var i=0; i<gCurrentMethodParams.length; i++)
     194    reqUrl += "&method="+methodName;
     195    for ( var i=0; i<method.params.length; i++)
    200196    {
    201       if (document.getElementById('methodParameterSend_'+i).checked)
    202         reqUrl += '&'+gCurrentMethodParams[i].name+'='+$F('methodParameterValue_'+i);
     197      if (! $('methodParameterSend_'+i).checked)
     198        continue;
     199
     200      if ( method.params[i].acceptArray &&  $F('methodParameterValue_'+i).split('|').length > 1 )
     201      {
     202        $F('methodParameterValue_'+i).split('|').each(
     203            function(v) {
     204              reqUrl += '&'+method.params[i].name+'[]='+v;
     205            }
     206          );
     207      }
     208      else
     209        reqUrl += '&'+method.params[i].name+'='+$F('methodParameterValue_'+i);
    203210    }
    204211    if ( !newWindow )
    205       document.getElementById("invokeFrame").src = reqUrl;
     212      $("invokeFrame").src = reqUrl;
    206213    else
    207214      window.open(reqUrl);
     
    209216  else
    210217  {
    211     var form = document.getElementById("invokeForm");
     218    var form = $("invokeForm");
    212219    form.action = reqUrl;
    213     var t = '<input type="hidden" name="'+'method'+'" value="'+method+'"/>';
    214     for ( var i=0; i<gCurrentMethodParams.length; i++)
     220    var t = '<input type="hidden" name="'+'method'+'" value="'+methodName+'"/>';
     221    for ( var i=0; i<method.params.length; i++)
    215222    {
    216       if (document.getElementById('methodParameterSend_'+i).checked)
    217         t += '<input type="hidden" name="'+gCurrentMethodParams[i].name+'" value="'+$F('methodParameterValue_'+i)+'"/>';
     223      if (! $('methodParameterSend_'+i).checked)
     224        continue;
     225
     226      if ( method.params[i].acceptArray &&  $F('methodParameterValue_'+i).split('|').length > 1 )
     227      {
     228        $F('methodParameterValue_'+i).split('|').each(
     229            function(v) {
     230              t += '<input type="hidden" name="'+method.params[i].name+'[]" value="'+v+'"/>';
     231            }
     232          );
     233      }
     234      else
     235        t += '<input type="hidden" name="'+method.params[i].name+'" value="'+$F('methodParameterValue_'+i)+'"/>';
    218236    }
    219237    form.innerHTML = t;
    220     if ( !newWindow )
    221       form.target = "invokeFrame";
    222     else
    223       form.target = "_blank";
     238    form.target = newWindow ? "_blank" : "invokeFrame";
    224239    form.submit();
    225240  }
     
    357372          <tr>
    358373            <td style="width:150px">Parameter</td>
    359             <td>Optional</td>
     374            <td>Extra</td>
    360375            <td>Send</td>
    361376            <td style="width:160px">Value</td>
Note: See TracChangeset for help on using the changeset viewer.