summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitattributes16
-rw-r--r--buildscripts/jsbuilder/build.php63
-rw-r--r--buildscripts/jsbuilder/jsmin.php537
-rw-r--r--framework/3rdParty/TinyMCE/tiny_mce.md52
-rw-r--r--framework/3rdParty/TinyMCE/tiny_mce.tarbin3502080 -> 3020800 bytes
-rw-r--r--framework/Web/Javascripts/effects/CHANGELOG204
-rw-r--r--framework/Web/Javascripts/effects/MIT-LICENSE2
-rw-r--r--framework/Web/Javascripts/effects/builder.js42
-rw-r--r--framework/Web/Javascripts/effects/controls.js56
-rw-r--r--framework/Web/Javascripts/effects/dragdrop.js187
-rw-r--r--framework/Web/Javascripts/effects/effects.js449
-rw-r--r--framework/Web/Javascripts/effects/slider.js44
-rw-r--r--framework/Web/Javascripts/js/compressed/effects.js82
-rw-r--r--framework/Web/Javascripts/js/compressed/prado.js447
-rw-r--r--framework/Web/Javascripts/js/debug/effects.js457
-rw-r--r--framework/Web/Javascripts/js/debug/prado.js4093
-rw-r--r--framework/Web/Javascripts/prototype/AUTHORS12
-rw-r--r--framework/Web/Javascripts/prototype/LICENSE16
-rw-r--r--framework/Web/Javascripts/prototype/README64
-rw-r--r--framework/Web/Javascripts/prototype/THANKS21
-rw-r--r--framework/Web/Javascripts/prototype/ajax.js286
-rw-r--r--framework/Web/Javascripts/prototype/array.js70
-rw-r--r--framework/Web/Javascripts/prototype/base.js102
-rw-r--r--framework/Web/Javascripts/prototype/dom.js398
-rw-r--r--framework/Web/Javascripts/prototype/enumerable.js182
-rw-r--r--framework/Web/Javascripts/prototype/event.js109
-rw-r--r--framework/Web/Javascripts/prototype/form.js294
-rw-r--r--framework/Web/Javascripts/prototype/hash.js57
-rw-r--r--framework/Web/Javascripts/prototype/position.js233
-rw-r--r--framework/Web/Javascripts/prototype/prototype.js3214
-rw-r--r--framework/Web/Javascripts/prototype/range.js29
-rw-r--r--framework/Web/Javascripts/prototype/selector.js110
-rw-r--r--framework/Web/Javascripts/prototype/string.js134
-rw-r--r--framework/Web/UI/WebControls/THtmlArea.php3
-rw-r--r--framework/Web/UI/WebControls/TImageButton.php2
-rw-r--r--tests/test_tools/selenium/core/SeleniumLog.html14
-rw-r--r--tests/test_tools/selenium/core/TestRunner-splash.html1
-rw-r--r--tests/test_tools/selenium/core/scripts/find_matching_child.js16
-rw-r--r--tests/test_tools/selenium/core/scripts/htmlutils.js411
-rw-r--r--tests/test_tools/selenium/core/scripts/injection.html19
-rw-r--r--tests/test_tools/selenium/core/scripts/selenium-api.js1454
-rw-r--r--tests/test_tools/selenium/core/scripts/selenium-browserbot.js1456
-rw-r--r--tests/test_tools/selenium/core/scripts/selenium-browserdetect.js48
-rw-r--r--tests/test_tools/selenium/core/scripts/selenium-commandhandlers.js375
-rw-r--r--tests/test_tools/selenium/core/scripts/selenium-executionloop.js63
-rw-r--r--tests/test_tools/selenium/core/scripts/selenium-logging.js30
-rw-r--r--tests/test_tools/selenium/core/scripts/selenium-testrunner.js516
-rw-r--r--tests/test_tools/selenium/core/scripts/selenium-version.js4
-rw-r--r--tests/test_tools/selenium/core/selenium.css44
-rw-r--r--tests/test_tools/selenium/php/TestRunner.php3
-rw-r--r--tests/test_tools/selenium/php/selenium.php2
-rw-r--r--tests/test_tools/selenium/prado-functional-test.js74
52 files changed, 10594 insertions, 5953 deletions
diff --git a/.gitattributes b/.gitattributes
index 95d156c7..8ef7b682 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1966,23 +1966,7 @@ framework/Web/Javascripts/prado/form.js -text
framework/Web/Javascripts/prado/inlineeditor.js -text
framework/Web/Javascripts/prado/prado.js -text
framework/Web/Javascripts/prado/validation3.js -text
-framework/Web/Javascripts/prototype/AUTHORS -text
-framework/Web/Javascripts/prototype/LICENSE -text
-framework/Web/Javascripts/prototype/README -text
-framework/Web/Javascripts/prototype/THANKS -text
-framework/Web/Javascripts/prototype/ajax.js -text
-framework/Web/Javascripts/prototype/array.js -text
-framework/Web/Javascripts/prototype/base.js -text
-framework/Web/Javascripts/prototype/dom.js -text
-framework/Web/Javascripts/prototype/enumerable.js -text
-framework/Web/Javascripts/prototype/event.js -text
-framework/Web/Javascripts/prototype/form.js -text
-framework/Web/Javascripts/prototype/hash.js -text
-framework/Web/Javascripts/prototype/position.js -text
framework/Web/Javascripts/prototype/prototype.js -text
-framework/Web/Javascripts/prototype/range.js -text
-framework/Web/Javascripts/prototype/selector.js -text
-framework/Web/Javascripts/prototype/string.js -text
framework/Web/Javascripts/ratings/blocks.css -text
framework/Web/Javascripts/ratings/blocks.png -text
framework/Web/Javascripts/ratings/blocks_blank.gif -text
diff --git a/buildscripts/jsbuilder/build.php b/buildscripts/jsbuilder/build.php
index 30c99ac1..19824932 100644
--- a/buildscripts/jsbuilder/build.php
+++ b/buildscripts/jsbuilder/build.php
@@ -40,7 +40,9 @@ define('DOC_OUTPUT_DIR', realpath(dirname(__FILE__).'/../../docs/Javascript'));
*/
define('BUILD_DOC', sprintf('perl "%s" --no-sources -d "%s" ', JS_DOC, DOC_OUTPUT_DIR).'%s');
-include_once(dirname(__FILE__).'/jsmin.php');
+define('JSMIN_AS_LIB',true);
+
+include(dirname(__FILE__).'/jsmin.php');
if(SOURCE_DIR===false || TARGET_DIR===false)
die('Unable to determine the build path.');
@@ -55,33 +57,14 @@ $libraries = array(
//base javascript functions
'prototype/prototype.js',
- 'prototype/base.js',
+ 'effects/builder.js',
+
'extended/base.js',
- 'prototype/string.js',
'extended/string.js',
- 'prototype/enumerable.js',
- 'prototype/array.js',
- 'prototype/hash.js',
- 'prototype/range.js',
-
- //dom functions
- 'prototype/dom.js',
- 'prototype/form.js',
- 'prototype/event.js',
'extended/event.js',
- 'prototype/position.js',
-
- //element selectors
- 'prototype/selector.js',
-
- //build dom elements with DIV, A, UL, etc functions
- 'effects/builder.js',
+ 'extended/date.js',
'extended/builder.js',
-// 'extra/getElementsBySelector.js',
-// 'extra/behaviour.js',
-
- 'extended/date.js',
//prado core
'prado/prado.js',
@@ -89,42 +72,20 @@ $libraries = array(
'prado/element.js',
//controls
- 'prado/controls.js'
+ 'prado/controls.js',
+ 'ratings/ratings.js'
),
//effects
'effects.js' => array(
- 'effects/effects.js',
- 'extended/effects.js'
- ),
- //active controls
- 'ajax.js' => array(
- 'prototype/ajax.js',
- 'prado/ajax3.js',
- 'extra/json.js',
- 'effects/controls.js',
- 'prado/activecontrols3.js',
- 'prado/inlineeditor.js',
- 'ratings/ratings.js'
-// 'effects/dragdrop.js',
-// 'effects/slider.js',
-// 'prado/activecontrols.js'
+ 'effects/effects.js'
),
+
//logging
'logger.js' => array(
'extra/logger.js',
),
- 'containers.js' => array(
- 'effects/dragdrop.js',
- ),
-
- //rico
- 'rico.js' => array(
- 'rico/rico.js',
- 'rico/extension.js'
- ),
-
//validator
'validator.js' => array(
'prado/validation3.js'
@@ -176,7 +137,7 @@ foreach($libraries as $jsFile => $sourceFiles)
$jsMin = new JSMin($debugFile, $compressFile);
$jsMin -> minify();
unset($jsMin);
- @unlink($tempFile);
+ //@unlink($tempFile);
echo "Saving file {$jsFile}\n";
$builds++;
}
@@ -197,4 +158,4 @@ else if($builds > 0)
else
echo "No files to build.";
-?>
+?> \ No newline at end of file
diff --git a/buildscripts/jsbuilder/jsmin.php b/buildscripts/jsbuilder/jsmin.php
index 4abf1b34..bc63dd4e 100644
--- a/buildscripts/jsbuilder/jsmin.php
+++ b/buildscripts/jsbuilder/jsmin.php
@@ -1,7 +1,6 @@
<?php
-
/**
-* JSMin.php (for PHP 5)
+* JSMin_lib.php (for PHP 4, 5)
*
* PHP adaptation of JSMin, published by Douglas Crockford as jsmin.c, also based
* on its Java translation by John Reilly.
@@ -37,14 +36,14 @@
*
* @copyright No new copyright ; please keep above and following information.
* @author David Holmes <dholmes@cfdsoftware.net> of CFD Labs, France
-* @version 0.1 (PHP translation) 2006-04-11
+* @author Gaetano Giunta
+* @version $Id: $
*
* Please note, this is a brutal and simple conversion : it could undoubtedly be
* improved, as a PHP implementation, by applying more PHP-specific programming
* features.
*
-* PHP 5 is required because OO style of programming is used, as well as classes
-* from the Standard PHP Library (SPL).
+* Exceptions and all PHP 5 - only features ahve been removed for compat with PHP 4
*
* Note : whereas jsmin.c works specifically with the standard input and output
* streams, this implementation only falls back on them if file pathnames are
@@ -66,13 +65,24 @@
* (etc...)
*
* See JSMin.php -h (or --help) for command-line documentation.
+*
+* NEW AND IMPROVED in version 0.2:
+* to take advantage of this file in your own code, you can do the following:
+*
+* define('JSMIN_AS_LIB', true); // prevents auto-run on include
+* include('jsmin.php');
+* // the JSMin class now works on php strings, too
+* $jsMin = new JSMin(file_get_contents('e:/htdocs/awstats_misc_tracker.js'), false);
+* // in that case, the modifies string is returned by minify():
+* $out = $jsMin->minify();
+*
*/
/**
* Version of this PHP translation.
*/
-define('VERSION', '0.1');
+define('JSMIN_VERSION', '0.2');
/**
* How fgetc() reports an End Of File.
@@ -98,9 +108,12 @@ define('ORD_9', ord('9'));
/**
* Generic exception class related to JSMin.
*/
-
+/*
class JSMinException extends Exception {
}
+*/
+class JSMinException {
+}
/**
* A JSMin exception indicating that a file provided for input or output could not be properly opened.
@@ -131,12 +144,30 @@ class UnterminatedRegExpLiteralJSMinException extends JSMinException {
}
/**
+ * Constant describing an {@link action()} : Output A. Copy B to A. Get the next B.
+ */
+
+define ('JSMIN_ACT_FULL', 1);
+
+/**
+ * Constant describing an {@link action()} : Copy B to A. Get the next B. (Delete A).
+ */
+
+define ('JSMIN_ACT_BUF', 2);
+
+/**
+ * Constant describing an {@link action()} : Get the next B. (Delete B).
+ */
+
+define ('JSMIN_ACT_IMM', 3);
+
+/**
* Main JSMin application class.
*
* Example of use :
*
* $jsMin = new JSMin(...input..., ...output...);
-* $jsMin -> minify();
+* $jsMin->minify();
*
* Do not specify input and/or output (or default to '-') to use stdin and/or stdout.
*/
@@ -144,50 +175,35 @@ class UnterminatedRegExpLiteralJSMinException extends JSMinException {
class JSMin {
/**
- * Constant describing an {@link action()} : Output A. Copy B to A. Get the next B.
- */
-
- const ACT_FULL = 1;
-
- /**
- * Constant describing an {@link action()} : Copy B to A. Get the next B. (Delete A).
- */
-
- const ACT_BUF = 2;
-
- /**
- * Constant describing an {@link action()} : Get the next B. (Delete B).
- */
-
- const ACT_IMM = 3;
-
- /**
* The input stream, from which to read a JS file to minimize. Obtained by fopen().
- * @var SplFileObject
+ * NB: might be a string instead of a stream
+ * @var SplFileObject | string
*/
-
- private $in;
+ var $in;
/**
* The output stream, in which to write the minimized JS file. Obtained by fopen().
- * @var SplFileObject
+ * NB: might be a string instead of a stream
+ * @var SplFileObject | string
*/
-
- private $out;
+ var $out;
/**
* Temporary I/O character (A).
* @var string
*/
-
- private $theA;
+ var $theA;
/**
* Temporary I/O character (B).
* @var string
*/
+ var $theB;
- private $theB;
+ /** variables used for string-based parsing **/
+ var $inLength = 0;
+ var $inPos = 0;
+ var $isString = false;
/**
* Indicates whether a character is alphanumeric or _, $, \ or non-ASCII.
@@ -195,8 +211,7 @@ class JSMin {
* @param string $c The single character to test.
* @return boolean Whether the char is a letter, digit, underscore, dollar, backslash, or non-ASCII.
*/
-
- private static function isAlphaNum($c) {
+ function isAlphaNum($c) {
// Get ASCII value of character for C-like comparisons
@@ -222,12 +237,21 @@ class JSMin {
* @see $in
* @see peek()
*/
-
- private function get() {
+ function get() {
// Get next input character and advance position in file
-
- $c = $this -> in -> fgetc();
+//var_dump($this);
+ if ($this->isString) {
+ if ($this->inPos < $this->inLength) {
+ $c = $this->in[$this->inPos];
+ ++$this->inPos;
+ }
+ else {
+ return EOF;
+ }
+ }
+ else
+ $c = $this->in->fgetc();
// Test for non-problematic characters
@@ -256,22 +280,45 @@ class JSMin {
* @see $in
* @see get()
*/
+ function peek() {
- private function peek() {
+ if ($this->isString) {
+ if ($this->inPos < $this->inLength) {
+ $c = $this->in[$this->inPos];
+ }
+ else {
+ return EOF;
+ }
+ }
+ else {
+ // Get next input character
- // Get next input character
+ $c = $this->in->fgetc();
- $c = $this -> in -> fgetc();
+ // Regress position in file
- // Regress position in file
+ $this->in->fseek(-1, SEEK_CUR);
- $this -> in -> fseek(-1, SEEK_CUR);
-
- // Return character obtained
+ // Return character obtained
+ }
return $c;
}
+ /**
+ * Adds a char to the output steram / string
+ * @see $out
+ */
+ function put($c)
+ {
+ if ($this->isString) {
+ $this->out .= $c;
+ }
+ else {
+ $this->out->fwrite($c);
+ }
+ }
+
/**
* Get the next character from the input stream, excluding comments.
*
@@ -281,12 +328,11 @@ class JSMin {
* @return string The next character from the specified input stream, skipping comments.
* @see $in
*/
-
- private function next() {
+ function next() {
// Get next char from input, translated if necessary
- $c = $this -> get();
+ $c = $this->get();
// Check comment possibility
@@ -294,16 +340,16 @@ class JSMin {
// Look ahead : a comment is two slashes or slashes followed by asterisk (to be closed)
- switch ($this -> peek()) {
+ switch ($this->peek()) {
case '/' :
// Comment is up to the end of the line
- // TOTEST : simple $this -> in -> fgets()
+ // TOTEST : simple $this->in->fgets()
while (true) {
- $c = $this -> get();
+ $c = $this->get();
if (ord($c) <= ORD_NL) {
return $c;
@@ -319,17 +365,17 @@ class JSMin {
// N.B. not using switch() because of having to test EOF with ===
- $c = $this -> get();
+ $c = $this->get();
if ($c == '*') {
// Comment termination if the char ahead is a slash
- if ($this -> peek() == '/') {
+ if ($this->peek() == '/') {
// Advance again and make into a single space
- $this -> get();
+ $this->get();
return ' ';
}
}
@@ -337,7 +383,8 @@ class JSMin {
// Whoopsie
- throw new UnterminatedCommentJSMinException();
+ //throw new UnterminatedCommentJSMinException();
+ trigger_error('UnterminatedComment', E_USER_ERROR);
}
}
@@ -359,34 +406,33 @@ class JSMin {
*
* The action to perform is determined by the argument :
*
- * JSMin :: ACT_FULL : Output A. Copy B to A. Get the next B.
- * JSMin :: ACT_BUF : Copy B to A. Get the next B. (Delete A).
- * JSMin :: ACT_IMM : Get the next B. (Delete B).
+ * JSMin::ACT_FULL : Output A. Copy B to A. Get the next B.
+ * JSMin::ACT_BUF : Copy B to A. Get the next B. (Delete A).
+ * JSMin::ACT_IMM : Get the next B. (Delete B).
*
* A string is treated as a single character. Also, regular expressions are recognized if preceded
* by '(', ',' or '='.
*
- * @param int $action The action to perform : one of the JSMin :: ACT_* constants.
+ * @param int $action The action to perform : one of the JSMin::ACT_* constants.
*/
-
- private function action($action) {
+ function action($action) {
// Choice of possible actions
// Note the frequent fallthroughs : the actions are decrementally "long"
switch ($action) {
- case self :: ACT_FULL :
+ case JSMIN_ACT_FULL :
// Write A to output, then fall through
- $this -> out -> fwrite($this -> theA);
+ $this->put($this->theA);
- case self :: ACT_BUF : // N.B. possible fallthrough from above
+ case JSMIN_ACT_BUF : // N.B. possible fallthrough from above
// Copy B to A
- $tmpA = $this -> theA = $this -> theB;
+ $tmpA = $this->theA = $this->theB;
// Treating a string as a single char : outputting it whole
// Note that the string-opening char (" or ') is memorized in B
@@ -397,15 +443,15 @@ class JSMin {
// Output string contents
- $this -> out -> fwrite($tmpA);
+ $this->put($tmpA);
// Get next character, watching out for termination of the current string,
// new line & co (then the string is not terminated !), or a backslash
// (upon which the following char is directly output to serve the escape mechanism)
- $tmpA = $this -> theA = $this -> get();
+ $tmpA = $this->theA = $this->get();
- if ($tmpA == $this -> theB) {
+ if ($tmpA == $this->theB) {
// String terminated
@@ -418,7 +464,8 @@ class JSMin {
// Whoopsie
- throw new UnterminatedStringLiteralJSMinException();
+ //throw new UnterminatedStringLiteralJSMinException();
+ trigger_error('UnterminatedStringLiteral', E_USER_ERROR);
}
// else
@@ -427,36 +474,36 @@ class JSMin {
// Escape next char immediately
- $this -> out -> fwrite($tmpA);
- $tmpA = $this -> theA = $this -> get();
+ $this->put($tmpA);
+ $tmpA = $this->theA = $this->get();
}
}
}
- case self :: ACT_IMM : // N.B. possible fallthrough from above
+ case JSMIN_ACT_IMM : // N.B. possible fallthrough from above
// Get the next B
- $this -> theB = $this -> next();
+ $this->theB = $this->next();
// Special case of recognising regular expressions (beginning with /) that are
// preceded by '(', ',' or '='
- $tmpA = $this -> theA;
+ $tmpA = $this->theA;
- if ($this -> theB == '/' && ($tmpA == '(' || $tmpA == ',' || $tmpA == '=')) {
+ if ($this->theB == '/' && ($tmpA == '(' || $tmpA == ',' || $tmpA == '=')) {
// Output the two successive chars
- $this -> out -> fwrite($tmpA);
- $this -> out -> fwrite($this -> theB);
+ $this->put($tmpA);
+ $this->put($this->theB);
// Look for the end of the RE literal, watching out for escaped chars or a control /
// end of line char (the RE literal then being unterminated !)
while (true) {
- $tmpA = $this -> theA = $this -> get();
+ $tmpA = $this->theA = $this->get();
if ($tmpA == '/') {
@@ -471,28 +518,31 @@ class JSMin {
// Escape next char immediately
- $this -> out -> fwrite($tmpA);
- $tmpA = $this -> theA = $this -> get();
+ $this->put($tmpA);
+ $tmpA = $this->theA = $this->get();
}
else if (ord($tmpA) <= ORD_NL) {
// Whoopsie
- throw new UnterminatedRegExpLiteralJSMinException();
+ //throw new UnterminatedRegExpLiteralJSMinException();
+ trigger_error('UnterminatedRegExpLiteral', E_USER_ERROR);
}
// Output RE characters
- $this -> out -> fwrite($tmpA);
+ $this->put($tmpA);
}
// Move forward after the RE literal
- $this -> theB = $this -> next();
+ $this->theB = $this->next();
}
break;
- default : throw new JSMinException('Expected a JSMin :: ACT_* constant in action().');
+ default :
+ //throw new JSMinException('Expected a JSMin::ACT_* constant in action().');
+ trigger_error('Expected a JSMin::ACT_* constant in action()', E_USER_ERROR);
}
}
@@ -500,6 +550,7 @@ class JSMin {
* Run the JSMin application : minify some JS code.
*
* The code is read from the input stream, and its minified version is written to the output one.
+ * In case input is a string, minified vesrions is also returned by this function as string.
* That is : characters which are insignificant to JavaScript are removed, as well as comments ;
* tabs are replaced with spaces ; carriage returns are replaced with linefeeds, and finally most
* spaces and linefeeds are deleted.
@@ -507,54 +558,54 @@ class JSMin {
* Note : name was changed from jsmin() because PHP identifiers are case-insensitive, and it is already
* the name of this class.
*
- * @see __construct()
+ * @see JSMin()
+ * @return null | string
*/
-
- public function minify() {
+ function minify() {
// Initialize A and run the first (minimal) action
- $this -> theA = "\n";
- $this -> action(self :: ACT_IMM);
+ $this->theA = "\n";
+ $this->action(JSMIN_ACT_IMM);
// Proceed all the way to the end of the input file
- while ($this -> theA !== EOF) {
+ while ($this->theA !== EOF) {
- switch ($this -> theA) {
+ switch ($this->theA) {
case ' ' :
- if (self :: isAlphaNum($this -> theB)) {
- $this -> action(self :: ACT_FULL);
+ if (JSMin::isAlphaNum($this->theB)) {
+ $this->action(JSMIN_ACT_FULL);
}
else {
- $this -> action(self :: ACT_BUF);
+ $this->action(JSMIN_ACT_BUF);
}
break;
case "\n" :
- switch ($this -> theB) {
+ switch ($this->theB) {
case '{' : case '[' : case '(' :
case '+' : case '-' :
- $this -> action(self :: ACT_FULL);
+ $this->action(JSMIN_ACT_FULL);
break;
case ' ' :
- $this -> action(self :: ACT_IMM);
+ $this->action(JSMIN_ACT_IMM);
break;
default :
- if (self :: isAlphaNum($this -> theB)) {
- $this -> action(self :: ACT_FULL);
+ if (JSMin::isAlphaNum($this->theB)) {
+ $this->action(JSMIN_ACT_FULL);
}
else {
- $this -> action(self :: ACT_BUF);
+ $this->action(JSMIN_ACT_BUF);
}
break;
@@ -563,38 +614,38 @@ class JSMin {
break;
default :
- switch ($this -> theB) {
+ switch ($this->theB) {
case ' ' :
- if (self :: isAlphaNum($this -> theA)) {
+ if (JSMin::isAlphaNum($this->theA)) {
- $this -> action(self :: ACT_FULL);
+ $this->action(JSMIN_ACT_FULL);
break;
}
// else
- $this -> action(self :: ACT_IMM);
+ $this->action(JSMIN_ACT_IMM);
break;
case "\n" :
- switch ($this -> theA) {
+ switch ($this->theA) {
case '}' : case ']' : case ')' : case '+' :
case '-' : case '"' : case '\'' :
- $this -> action(self :: ACT_FULL);
+ $this->action(JSMIN_ACT_FULL);
break;
default :
- if (self :: isAlphaNum($this -> theA)) {
- $this -> action(self :: ACT_FULL);
+ if (JSMin::isAlphaNum($this->theA)) {
+ $this->action(JSMIN_ACT_FULL);
}
else {
- $this -> action(self :: ACT_IMM);
+ $this->action(JSMIN_ACT_IMM);
}
break;
@@ -603,7 +654,7 @@ class JSMin {
break;
default :
- $this -> action(self :: ACT_FULL);
+ $this->action(JSMIN_ACT_FULL);
break;
}
@@ -611,6 +662,11 @@ class JSMin {
break;
}
}
+
+ if ($this->isString) {
+ return $this->out;
+
+ }
}
/**
@@ -620,49 +676,256 @@ class JSMin {
*
* @param string $inFileName The pathname of the input (unminified JS) file. STDIN if '-' or absent.
* @param string $outFileName The pathname of the output (minified JS) file. STDOUT if '-' or absent.
+ * If outFileName === FALSE, we assume that inFileName is in fact the string to be minified!!!
* @param array $comments Optional lines to present as comments at the beginning of the output.
- * @throws FileOpenFailedJSMinException If the input and/or output file pathname is not provided, and
- * respectively STDIN and/or STDOUT are not available (ie, script is not being used in CLI).
*/
+ function JSMin($inFileName = '-', $outFileName = '-', $comments = NULL) {
+ if ($outFileName === FALSE) {
+ $this->JSMin_String($inFileName, $comments);
+ }
+ else {
+ $this->JSMin_File($inFileName, $outFileName, $comments);
+ }
+ }
- public function __construct($inFileName = '-', $outFileName = '-', $comments = NULL) {
+ function JSMin_File($inFileName = '-', $outFileName = '-', $comments = NULL) {
// Recuperate input and output streams.
// Use STDIN and STDOUT by default, if they are defined (CLI mode) and no file names are provided
- if ($inFileName == '-') $inFileName = 'php://stdin';
- if ($outFileName == '-') $outFileName = 'php://stdout';
+ if ($inFileName == '-') $inFileName = 'php://stdin';
+ if ($outFileName == '-') $outFileName = 'php://stdout';
- try {
+ try {
- $this -> in = new SplFileObject($inFileName, 'rb', TRUE);
- }
- catch (Exception $e) {
+ $this->in = new SplFileObject($inFileName, 'rb', TRUE);
+ }
+ catch (Exception $e) {
- throw new FileOpenFailedJSMinException(
- 'Failed to open "'.$inFileName.'" for reading only.'
- );
- }
+ throw new FileOpenFailedJSMinException(
+ 'Failed to open "'.$inFileName.'" for reading only.'
+ );
+ }
- try {
+ try {
- $this -> out = new SplFileObject($outFileName, 'wb', TRUE);
- }
- catch (Exception $e) {
+ $this->out = new SplFileObject($outFileName, 'wb', TRUE);
+ }
+ catch (Exception $e) {
- throw new FileOpenFailedJSMinException(
- 'Failed to open "'.$outFileName.'" for writing only.'
- );
- }
+ throw new FileOpenFailedJSMinException(
+ 'Failed to open "'.$outFileName.'" for writing only.'
+ );
+ }
- // Present possible initial comments
+ /*$this->in = fopen($inFileName, 'rb');
+ if (!$this->in) {
+ trigger_error('Failed to open "'.$inFileName, E_USER_ERROR);
+ }
+ $this->out = fopen($outFileName, 'wb');
+ if (!$this->out) {
+ trigger_error('Failed to open "'.$outFileName, E_USER_ERROR);
+ }*/
+
+ // Present possible initial comments
+
+ if ($comments !== NULL) {
+ foreach ($comments as $comm) {
+ $this->out->fwrite('// '.str_replace("\n", " ", $comm)."\n");
+ }
+ }
+
+ }
+
+ function JSMin_String($inString, $comments = NULL) {
+ $this->in = $inString;
+ $this->out = '';
+ $this->inLength = strlen($inString);
+ $this->inPos = 0;
+ $this->isString = true;
if ($comments !== NULL) {
foreach ($comments as $comm) {
- $this -> out -> fwrite('// '.$comm."\n");
+ $this->out .= '// '.str_replace("\n", " ", $comm)."\n";
}
- }
+ }
+ }
+}
+
+//
+// OTHER FUNCTIONS
+//
+
+/**
+* Displays inline help for the application.
+*/
+
+function printHelp() {
+
+ // All the inline help
+
+ echo "\n";
+ echo "Usage : JSMin.php [inputFile] [outputFile] [[-c] comm1 comm2 ...]\n";
+ echo " JSMin.php [-v|-h]\n";
+ echo "\n";
+ echo "Minify JavaScript code using JSMin, the JavaScript Minifier.\n";
+ echo "\n";
+ echo "JSMin is a filter which removes comments and unnecessary whitespace\n";
+ echo "from a script read in the inputFile (stdin by default), as well as\n";
+ echo "omitting or modifying some characters, before writing the results to\n";
+ echo "the outputFile (stdout by default).\n";
+ echo "It does not change the behaviour of the program that is minifies.\n";
+ echo "The result may be harder to debug. It will definitely be harder to\n";
+ echo "read. It typically reduces filesize by half, resulting in faster\n";
+ echo "downloads. It also encourages a more expressive programming style\n";
+ echo "because it eliminates the download cost of clean, literate self-\n";
+ echo "documentation.\n";
+ echo "\n";
+ echo "The '-' character can be used to explicitely specify a standard\n";
+ echo "stream for input or output.\n";
+ echo "\n";
+ echo "With the optional -c (--comm) option, all following parameters will\n";
+ echo "be listed at the beginning of the output as comments. This is a\n";
+ echo "convenient way of replacing copyright messages and other info. The\n";
+ echo "option is unnecessary if an input and output file are specified :\n";
+ echo "following parameters will then automatically be treated thus.\n";
+ echo "\n";
+ echo "Options :\n";
+ echo "\n";
+ echo " -c, --comm Present following parameters as initial comments.\n";
+ echo " -h, --help Display this information.\n";
+ echo " -v, --version Display short version information.\n";
+ echo "\n";
+ echo "The JavaScript Minifier is copyright (c) 2002 by Douglas Crockford\n";
+ echo "and available online at http://javascript.crockford.com/jsmin.html.\n";
+ echo "This PHP translation is by David Holmes of CFD Labs, France, 2006.\n";
+ echo "\n";
+}
+
+/**
+* Displays version information for the application.
+*/
+
+function printVersion() {
+
+ // Minimum info
+
+ echo "JSMin, the JavaScript Minifier, copyright (c) 2002 by Douglas Crockford.\n";
+ echo "PHP translation version ".JSMIN_VERSION." by David Holmes, CFD Labs.\n";
+}
+
+//
+// ENTRY POINT
+//
+
+// Allow user to include this file without having it run atomatically, ie. as if it was a lib
+if (!defined('JSMIN_AS_LIB')) {
+
+define('EXIT_SUCCESS', 0);
+define('EXIT_FAILURE', 1);
+
+// Examine command-line parameters
+// First shift off the first parameter, the executable's name
+
+if (!isset($argv)) {
+ die("Please run this utility from a command line, or set php.ini setting 'register_argc_argv' to true.\nTo use this file in your own code, define 'JSMIN_AS_LIB' before inclusion.");
+}
+
+array_shift($argv);
+
+$inFileName = NULL;
+$outFileName = NULL;
+
+$haveCommentParams = FALSE;
+$comments = array();
+
+foreach ($argv as $arg) {
+
+ // Bypass the rest if we are now considering initial comments
+
+ if ($haveCommentParams) {
+
+ $comments[] = $arg;
+ continue;
+ }
+
+ // else
+ // Look for an option (length > 1 because of '-' for indicating stdin or
+ // stdout)
+
+ if ($arg[0] == '-' && strlen($arg) > 1) {
+
+ switch ($arg) {
+
+ case '-c' : case '--comm' :
+
+ // Following parameters will be initial comments
+
+ $haveCommentParams = TRUE;
+
+ break;
+ case '-h' : case '--help' :
+
+ // Display inline help and exit normally
+
+ printHelp();
+ exit(EXIT_SUCCESS);
+
+ case '-v' : case '--version' :
+
+ // Display short version information and exit normally
+
+ printVersion();
+ exit(EXIT_SUCCESS);
+
+ default :
+
+ // Reject any other (unknown) option
+
+ echo "\n";
+ echo "ERROR : unknown option \"$arg\", sorry.\n";
+
+ printHelp();
+ exit(EXIT_FAILURE);
+ }
+
+ continue;
+ }
+
+ // else
+ // At this point, parameter is neither to be considered as an initial
+ // comment, nor is it an option. It is an input or output file.
+
+ if ($inFileName === NULL) {
+
+ // No input file yet, this is it
+
+ $inFileName = $arg;
}
+ else if ($outFileName === NULL) {
+
+ // An input file but no output file yet, this is it
+
+ $outFileName = $arg;
+ }
+ else {
+
+ // Already have input and output file, this is a first initial comment
+
+ $haveCommentParams = TRUE;
+ $comments[] = $arg;
+ }
+}
+
+if ($inFileName === NULL) $inFileName = '-';
+if ($outFileName === NULL) $outFileName = '-';
+
+// Prepare and run the JSMin application
+// If pathnames are not provided or '-', standard input/output streams are used
+
+$jsMin = new JSMin($inFileName, $outFileName, $comments);
+$jsMin->minify();
+
}
?> \ No newline at end of file
diff --git a/framework/3rdParty/TinyMCE/tiny_mce.md5 b/framework/3rdParty/TinyMCE/tiny_mce.md5
index b853cd60..15779479 100644
--- a/framework/3rdParty/TinyMCE/tiny_mce.md5
+++ b/framework/3rdParty/TinyMCE/tiny_mce.md5
@@ -1 +1 @@
-dcd33d2779fc68523fe73ab54b9e1034 *tiny_mce.tar
+7baac49d27cfe07e123a446f5ef74c67 *tiny_mce.tar
diff --git a/framework/3rdParty/TinyMCE/tiny_mce.tar b/framework/3rdParty/TinyMCE/tiny_mce.tar
index ddbbd1ac..95419d5d 100644
--- a/framework/3rdParty/TinyMCE/tiny_mce.tar
+++ b/framework/3rdParty/TinyMCE/tiny_mce.tar
Binary files differ
diff --git a/framework/Web/Javascripts/effects/CHANGELOG b/framework/Web/Javascripts/effects/CHANGELOG
index ab247a37..323ca859 100644
--- a/framework/Web/Javascripts/effects/CHANGELOG
+++ b/framework/Web/Javascripts/effects/CHANGELOG
@@ -1,3 +1,207 @@
+*V1.7.0* (January 19, 2007)
+
+* Cleanups for Effect.Morph
+
+* Monkeypatch Prototype 1.5.0 to incorporate [6002], fixes setStyle({opacity:0}) for IE
+
+* Fix Effect.inspect() for Prototype 1.5.0 final hash changes
+
+* Update to Prototype 1.5.0 final
+
+* New option keepBackgroundImage: true for Effect.Highlight, fixes #5037 [docwhat, tomg]
+
+* Minor tweaks for issues with application/xhtml+xml documents on Firefox, fixes #6836 [sjinks]
+
+* Fix a possible exception with Sortables, fixes #6828 [craiggwilson]
+
+* Update to Prototype 1.5.0_rc2 revision [5844] (as of Rails 1.2 RC 2)
+
+*V1.7.0 beta 2* (December 18, 2006)
+
+* Change the default setting for effects to support up to 60fps, if renderable by the browser. Add performance info to effects unit test.
+
+* Change get/setOpacity to use Prototype's new support for cross-browser opacity.
+
+* Update to Prototype 1.5.0_rc2 revision [5741], which fixes some of the reported issues with beta 1 (see Prototype's CHANGELOG for more):
+ * Opera 9, while not officially supported, should work now with Prototype and script.aculo.us
+ * Fixes issue with Safari when using Prototype's Ajax helpers with HTTP authorization
+
+* Add hash and CSS className support to Effect.Morph, fixes #6674 [Tobie]
+ Examples:
+ $(element).morph('myClass')
+ // will morph to all the properties specified
+ // in .className (in your external stylesheet).
+ // All properties which cannot be morphed (such as font-style)
+ // will be applied AfterFinish
+ $(element).morph('font-size: 10px') // or
+ $(element).morph({fontSize: '10px'}) // will morph the font-size to 10px
+
+*V1.7.0 beta 1* (November 21, 2006)
+
+* Add Element.morph() and Element.Methods.morph() as a shortcut to Effect.Morph
+ Example:
+ // basic Effect.Morph
+ $('error_message').morph('color:#f00;font-size:17px');
+ // fade out after a while
+ $('error_message').show().morph('font-size:17px').morph('opacity:0',{delay:4});
+
+* Update to Prototype 1.5.0_rc2 revision [5580]
+
+* Add a paramName option to the inplace editor for overriding the default parameter name of "value"
+
+* Add Effect.Transform that generates parallel executing Effect.Morph sets
+ Example:
+ // set up transformation
+ var transformation = new Effect.Transform([
+ { 'div.morphing': 'font-size:20px;padding-left:40em' },
+ { 'blah' : 'width:480px;border-width:10px;border-right-width:20px;margin:200px;margin-bottom:-20px;font-size:30px' }
+ ],{ duration: 0.5 });
+ // play transformation (can be called more than once)
+ transformation.play();
+
+* Add Effect.Morph core effect that morphs to a given CSS style rule. Effect.Morph does take orginal styles given by CSS style rules or the style attribute into consideration when calculating the transforms. It works with all length and color based CSS properties, including margins, paddings, borders, opacity and text/background colors.
+ Example:
+ new Effect.Morph('mydiv',{
+ style: 'font-size:3em;color:#f00;border-width:2em',
+ duration: 2.0
+ });
+
+*V1.6.5* (November 8, 2006)
+
+* Update to Prototype 1.5.0_rc1 revision [5462]
+
+* Support the HTML 'for' attribute in Builder by using 'htmlFor', fixes #6472 [gjones, tdd]
+
+ var node = Builder.node('label', { htmlFor: 'myinput' });
+
+* Add support to run a specific failing unit test by clicking on the corresponding test result, fixes #6290 [leeo]
+
+* Add modifier key support to Event.simulateMouse, fixes #6391 [savetheclocktower]
+
+* Add rails-trunk update task, clean up references to MIT license
+
+* Add new 'with-last' queue position option to queue effects to occur in parallel with the last effect to start in the queue
+
+* Add new special core effect Effect.Event for one-shot events that follow timelines defined by effect queues
+
+ new Effect.Event({ afterFinish:function(){
+ // do some code here
+ }, position: 'end' });
+
+* Do some refactoring to make use of Prototype 1.5 functionalities and save LOC
+
+* Fix an possible crash of IE on Effect.SlideUp, fixes #3192 [thx nel]
+
+* Add Builder.build() to create nodes from strings containing HTML, [DHH]
+
+ var node = Builder.build("<p>this is <b>neat!</b></p>");
+
+* Add a pulses parameter to Effect.Pulsate to control the amount of pulses, fixes #6245 [leeo]
+
+ For example, Effect.Pulsate('d8', {pulses: 2}) would pulsate twice. If the option is not given, it defaults to five pulses.
+
+* Fix an issue with clicking on a slider span resulting in an exception, fixes #4707 [thx sergeykojin]
+
+* Fix an issue with Draggables when no options are supplied, fixes #6045 [thx tdd]
+
+*V1.6.4* (September 6, 2006)
+
+* Hotfix IE issues with patched Prototype V1.5.0_rc1
+
+*V1.6.3* (September 5, 2006)
+
+* Update Prototype to V1.5.0_rc1
+
+* Merge assertElementsMatch and assertElementMatches from Prototype's [4986] unittest.js [Sam Stephenson]
+
+* Update Prototype to revision [4930]
+
+* Fix various issues with IE detection and Opera, and setOpacity, fixes #3886, #5973
+
+* Make Sortable.serialize handle DOM IDs like "some_element_1" correctly, fixes #5324
+
+* Add assertRespondsTo and shouldRespondTo assertions
+
+* Add experimental alternate syntax for unit tests (Behaviour Driven Development-style)
+
+* Add support for onStart, onDrag and onEnd events directly on Draggables (invoked from the Draggables.notify), fixes #4747 [thx scriptkitchen]
+
+* Add element shortcuts to Builder that can be activated by calling Builder.dump() (see the unit test), fixes #4260 [thx napalm]
+
+* Fix selection of correct option in SELECT element generated by InPlaceCollectionEditor for indexed option arrays, fixes #4789 [thx steve]
+
+* Add autoSelect option to Autocompleters to auto select an entry if only one is returned, fixes #5183 [thx cassiano dandrea]
+
+* Added delay option to Draggables and Sortables, see test/functional/dragdrop_delay_test.html for usage, implements #3325 [thx lsimon, tomg]
+
+* Remove revert cache code obsoleted by #4706, fixes #3436 (again) [thx tomg]
+
+* Fix autoscrolling inside scrollable containers when window is scrolled too, fixes #5200 [thx wseitz]
+
+* Make Effect.Puff work correctly for floating elements, fixes #3777 [thx michael hartl]
+
+* Add version and timestamp to indvidual library files for easier identification (the files are preprocessed by the Rake fresh_scriptaculous task), fixes #3015 [thx Tobie]
+
+* Add assertIndentical and assertNotIdentical unit test assertions, which test for equality and common type, fixes #5822 [thx glazedginger]
+
+* Add integration test for Ajax autocompleter for results with no linebreaks, #4149
+
+* Fix an issue with redrawing ghosted draggables that are inside a scrolled container, fixes #3860 [thx gkupps, tsukue]
+
+* Added a custom exception to all base effects when used on non-existing DOM elements, added a assertRaise method to unit tests
+
+* Fix autoscrolling when dragging an element unto a scrollable container, fixes #5017 [thx tomg]
+
+* Fix a condition where overriding the endeffect on Draggables without overriding the starteffect too leads to a Javascript error [thx Javier Martinez]
+
+* Fix a possible error with the drag/drop logic (affects the solution to #4706)
+
+*V1.6.2*
+
+* Fix a problem in the drag and drop logic if an reverting/drag ending draggable was initialized for a new drag (for example by clicking repeatedly) for all cases where the default start/revert/end-effects are used, fixes #4706 [thx tecM0]
+
+* Fix possible memory leaks with Draggables, fixes #3436 [thx aal]
+
+* Throw nicer errors when requires script.aculo.us libraries are not loaded, fixes #5339
+
+* Make slider handles work when not displayed initially by using CSS width/height, fixes #4011 [thx foysavas]
+
+* Update sortable functional test with onUpdate counter
+
+* Make more Element extensions unit tests work on Safari
+
+* Add the assertMatch unit test assertion for asserts with RegExps [thx Ian Tyndall]
+
+* Fix a problem with Effect.Move causing "jumping" elements because of very low float numbers in some situations
+
+* Fix a missing semicolon in dragdrop.js, fixes #5569 [thx mackalicious]
+
+* Fix a slight inaccuracy with Effect.Scale that could lead the scaling to be one pixel off
+
+* Be more prototypish with Effect.Transitions.linear
+
+* Make Effect.Scale recognize font sizes that use the pt unit, fixes #4136 [thx aljoscha]
+
+* Fix IE hack in Effect.Opacity, fixes #5444 [thx nicholas]
+
+* Fix IFRAME layout fix for IE and Autocompleter, fixes #5192 [thx tommy skaue]
+
+* Fix only option in onEmptyHover, fiex #5348 [thx glenn nilsson]
+
+* Fix Effect.BlindDown and SwitchOff handling of supplied callbacks, fixes #5089 [thx martinstrom]
+
+* Fix a problem with field focus on Ajax.InPlaceEditor and loading external text, fixes #4988, #5244 [thx rob]
+
+* Do not attempt to scroll if scrollspeed is 0/0, fixes #5035 [thx tomg]
+
+* Fix a problem with Sortable Tree serialization, fixes #4939, #4688, #4767 [thx Sammi Williams]
+
+* Fix an endless loop with sliders, fixes #3226, #4051, #4765 [thx jeff]
+
+* Make autocompleter work with update DIVs that have scrollbars, fixes #4782 [thx Tommy Skaue]
+
+* Corrected options parsing on switchoff effect, fixes #4710 [thx haldini]
+
*V1.6.1*
* Update to Prototype 1.5.0_rc0
diff --git a/framework/Web/Javascripts/effects/MIT-LICENSE b/framework/Web/Javascripts/effects/MIT-LICENSE
index 36af55c2..30ff1248 100644
--- a/framework/Web/Javascripts/effects/MIT-LICENSE
+++ b/framework/Web/Javascripts/effects/MIT-LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/framework/Web/Javascripts/effects/builder.js b/framework/Web/Javascripts/effects/builder.js
index 5b15ba93..199afc12 100644
--- a/framework/Web/Javascripts/effects/builder.js
+++ b/framework/Web/Javascripts/effects/builder.js
@@ -1,6 +1,9 @@
-// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// script.aculo.us builder.js v1.7.0, Fri Jan 19 19:16:36 CET 2007
+
+// Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//
-// See scriptaculous.js for full license.
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
var Builder = {
NODEMAP: {
@@ -33,7 +36,7 @@ var Builder = {
var element = parentElement.firstChild || null;
// see if browser added wrapping tags
- if(element && (element.tagName != elementName))
+ if(element && (element.tagName.toUpperCase() != elementName))
element = element.getElementsByTagName(elementName)[0];
// fallback to createElement approach
@@ -61,7 +64,7 @@ var Builder = {
for(attr in arguments[1])
element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
}
- if(element.tagName != elementName)
+ if(element.tagName.toUpperCase() != elementName)
element = parentElement.getElementsByTagName(elementName)[0];
}
}
@@ -75,10 +78,16 @@ var Builder = {
_text: function(text) {
return document.createTextNode(text);
},
+
+ ATTR_MAP: {
+ 'className': 'class',
+ 'htmlFor': 'for'
+ },
+
_attributes: function(attributes) {
var attrs = [];
for(attribute in attributes)
- attrs.push((attribute=='className' ? 'class' : attribute) +
+ attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) +
'="' + attributes[attribute].toString().escapeHTML() + '"');
return attrs.join(" ");
},
@@ -97,5 +106,26 @@ var Builder = {
},
_isStringOrNumber: function(param) {
return(typeof param=='string' || typeof param=='number');
+ },
+ build: function(html) {
+ var element = this.node('div');
+ $(element).update(html.strip());
+ return element.down();
+ },
+ dump: function(scope) {
+ if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope
+
+ var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " +
+ "BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " +
+ "FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+
+ "KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+
+ "PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+
+ "TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/);
+
+ tags.each( function(tag){
+ scope[tag] = function() {
+ return Builder.node.apply(Builder, [tag].concat($A(arguments)));
+ }
+ });
}
-} \ No newline at end of file
+}
diff --git a/framework/Web/Javascripts/effects/controls.js b/framework/Web/Javascripts/effects/controls.js
index ea5ce6ea..46f2cc18 100644
--- a/framework/Web/Javascripts/effects/controls.js
+++ b/framework/Web/Javascripts/effects/controls.js
@@ -1,12 +1,15 @@
-// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
-// (c) 2005 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
-// (c) 2005 Jon Tirsen (http://www.tirsen.com)
+// script.aculo.us controls.js v1.7.0, Fri Jan 19 19:16:36 CET 2007
+
+// Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// (c) 2005, 2006 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
+// (c) 2005, 2006 Jon Tirsen (http://www.tirsen.com)
// Contributors:
// Richard Livsey
// Rahul Bhargava
// Rob Wills
//
-// See scriptaculous.js for full license.
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
// Autocompleter.Base handles all the autocompletion functionality
// that's independent of the data source for autocompletion. This
@@ -48,7 +51,7 @@ Autocompleter.Base.prototype = {
this.index = 0;
this.entryCount = 0;
- if (this.setOptions)
+ if(this.setOptions)
this.setOptions(options);
else
this.options = options || {};
@@ -58,17 +61,20 @@ Autocompleter.Base.prototype = {
this.options.frequency = this.options.frequency || 0.4;
this.options.minChars = this.options.minChars || 1;
this.options.onShow = this.options.onShow ||
- function(element, update){
- if(!update.style.position || update.style.position=='absolute') {
- update.style.position = 'absolute';
- Position.clone(element, update, {setHeight: false, offsetTop: element.offsetHeight});
- }
- Effect.Appear(update,{duration:0.15});
- };
+ function(element, update){
+ if(!update.style.position || update.style.position=='absolute') {
+ update.style.position = 'absolute';
+ Position.clone(element, update, {
+ setHeight: false,
+ offsetTop: element.offsetHeight
+ });
+ }
+ Effect.Appear(update,{duration:0.15});
+ };
this.options.onHide = this.options.onHide ||
- function(element, update){ new Effect.Fade(update,{duration:0.15}) };
+ function(element, update){ new Effect.Fade(update,{duration:0.15}) };
- if (typeof(this.options.tokens) == 'string')
+ if(typeof(this.options.tokens) == 'string')
this.options.tokens = new Array(this.options.tokens);
this.observer = null;
@@ -259,11 +265,11 @@ Autocompleter.Base.prototype = {
if(!this.changed && this.hasFocus) {
this.update.innerHTML = choices;
Element.cleanWhitespace(this.update);
- Element.cleanWhitespace(this.update.firstChild);
+ Element.cleanWhitespace(this.update.down());
- if(this.update.firstChild && this.update.firstChild.childNodes) {
+ if(this.update.firstChild && this.update.down().childNodes) {
this.entryCount =
- this.update.firstChild.childNodes.length;
+ this.update.down().childNodes.length;
for (var i = 0; i < this.entryCount; i++) {
var entry = this.getEntry(i);
entry.autocompleteIndex = i;
@@ -274,9 +280,14 @@ Autocompleter.Base.prototype = {
}
this.stopIndicator();
-
this.index = 0;
- this.render();
+
+ if(this.entryCount==1 && this.options.autoSelect) {
+ this.selectEntry();
+ this.hide();
+ } else {
+ this.render();
+ }
}
},
@@ -464,6 +475,7 @@ Ajax.InPlaceEditor.prototype = {
this.element = $(element);
this.options = Object.extend({
+ paramName: "value",
okButton: true,
okText: "ok",
cancelLink: true,
@@ -595,7 +607,7 @@ Ajax.InPlaceEditor.prototype = {
var textField = document.createElement("input");
textField.obj = this;
textField.type = "text";
- textField.name = "value";
+ textField.name = this.options.paramName;
textField.value = text;
textField.style.backgroundColor = this.options.highlightcolor;
textField.className = 'editor_field';
@@ -608,7 +620,7 @@ Ajax.InPlaceEditor.prototype = {
this.options.textarea = true;
var textArea = document.createElement("textarea");
textArea.obj = this;
- textArea.name = "value";
+ textArea.name = this.options.paramName;
textArea.value = this.convertHTMLLineBreaks(text);
textArea.rows = this.options.rows;
textArea.cols = this.options.cols || 40;
@@ -778,6 +790,8 @@ Object.extend(Ajax.InPlaceCollectionEditor.prototype, {
collection.each(function(e,i) {
optionTag = document.createElement("option");
optionTag.value = (e instanceof Array) ? e[0] : e;
+ if((typeof this.options.value == 'undefined') &&
+ ((e instanceof Array) ? this.element.innerHTML == e[1] : e == optionTag.value)) optionTag.selected = true;
if(this.options.value==optionTag.value) optionTag.selected = true;
optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e));
selectTag.appendChild(optionTag);
diff --git a/framework/Web/Javascripts/effects/dragdrop.js b/framework/Web/Javascripts/effects/dragdrop.js
index 1528eced..32c91bc3 100644
--- a/framework/Web/Javascripts/effects/dragdrop.js
+++ b/framework/Web/Javascripts/effects/dragdrop.js
@@ -1,9 +1,10 @@
-// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
-// (c) 2005 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
-//
-// See scriptaculous.js for full license.
+// script.aculo.us dragdrop.js v1.7.0, Fri Jan 19 19:16:36 CET 2007
-/*--------------------------------------------------------------------------*/
+// Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// (c) 2005, 2006 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
+//
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
if(typeof Effect == 'undefined')
throw("dragdrop.js requires including script.aculo.us' effects.js library");
@@ -148,8 +149,16 @@ var Draggables = {
},
activate: function(draggable) {
- window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
- this.activeDraggable = draggable;
+ if(draggable.options.delay) {
+ this._timeout = setTimeout(function() {
+ Draggables._timeout = null;
+ window.focus();
+ Draggables.activeDraggable = draggable;
+ }.bind(this), draggable.options.delay);
+ } else {
+ window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
+ this.activeDraggable = draggable;
+ }
},
deactivate: function() {
@@ -163,10 +172,15 @@ var Draggables = {
// the same coordinates, prevent needless redrawing (moz bug?)
if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
this._lastPointer = pointer;
+
this.activeDraggable.updateDrag(event, pointer);
},
endDrag: function(event) {
+ if(this._timeout) {
+ clearTimeout(this._timeout);
+ this._timeout = null;
+ }
if(!this.activeDraggable) return;
this._lastPointer = null;
this.activeDraggable.endDrag(event);
@@ -193,6 +207,7 @@ var Draggables = {
this.observers.each( function(o) {
if(o[eventName]) o[eventName](eventName, draggable, event);
});
+ if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
},
_cacheObserverCallbacks: function() {
@@ -207,30 +222,25 @@ var Draggables = {
/*--------------------------------------------------------------------------*/
var Draggable = Class.create();
-Draggable._revertCache = {};
Draggable._dragging = {};
Draggable.prototype = {
initialize: function(element) {
- var options = Object.extend({
+ var defaults = {
handle: false,
- starteffect: function(element) {
- element._opacity = Element.getOpacity(element);
- Draggable._dragging[element] = true;
- new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});
- },
reverteffect: function(element, top_offset, left_offset) {
var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
- Draggable._revertCache[element] =
- new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
- queue: {scope:'_draggable', position:'end'}
- });
+ new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
+ queue: {scope:'_draggable', position:'end'}
+ });
},
endeffect: function(element) {
var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0;
new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
queue: {scope:'_draggable', position:'end'},
- afterFinish: function(){ Draggable._dragging[element] = false }
+ afterFinish: function(){
+ Draggable._dragging[element] = false
+ }
});
},
zindex: 1000,
@@ -238,20 +248,33 @@ Draggable.prototype = {
scroll: false,
scrollSensitivity: 20,
scrollSpeed: 15,
- snap: false // false, or xy or [x,y] or function(x,y){ return [x,y] }
- }, arguments[1] || {});
+ snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] }
+ delay: 0
+ };
+
+ if(!arguments[1] || typeof arguments[1].endeffect == 'undefined')
+ Object.extend(defaults, {
+ starteffect: function(element) {
+ element._opacity = Element.getOpacity(element);
+ Draggable._dragging[element] = true;
+ new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});
+ }
+ });
+
+ var options = Object.extend(defaults, arguments[1] || {});
this.element = $(element);
- if(options.handle && (typeof options.handle == 'string')) {
- var h = Element.childrenWithClassName(this.element, options.handle, true);
- if(h.length>0) this.handle = h[0];
- }
+ if(options.handle && (typeof options.handle == 'string'))
+ this.handle = this.element.down('.'+options.handle, 0);
+
if(!this.handle) this.handle = $(options.handle);
if(!this.handle) this.handle = this.element;
- if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML)
+ if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
options.scroll = $(options.scroll);
+ this._isScrollChild = Element.childOf(this.element, options.scroll);
+ }
Element.makePositioned(this.element); // fix IE
@@ -277,23 +300,18 @@ Draggable.prototype = {
},
initDrag: function(event) {
- if(typeof Draggable._dragging[this.element] != undefined &&
+ if(typeof Draggable._dragging[this.element] != 'undefined' &&
Draggable._dragging[this.element]) return;
if(Event.isLeftClick(event)) {
// abort on form elements, fixes a Firefox issue
var src = Event.element(event);
- if(src.tagName && (
- src.tagName=='INPUT' ||
- src.tagName=='SELECT' ||
- src.tagName=='OPTION' ||
- src.tagName=='BUTTON' ||
- src.tagName=='TEXTAREA')) return;
+ if((tag_name = src.tagName.toUpperCase()) && (
+ tag_name=='INPUT' ||
+ tag_name=='SELECT' ||
+ tag_name=='OPTION' ||
+ tag_name=='BUTTON' ||
+ tag_name=='TEXTAREA')) return;
- if(Draggable._revertCache[this.element]) {
- Draggable._revertCache[this.element].cancel();
- Draggable._revertCache[this.element] = null;
- }
-
var pointer = [Event.pointerX(event), Event.pointerY(event)];
var pos = Position.cumulativeOffset(this.element);
this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
@@ -329,6 +347,7 @@ Draggable.prototype = {
}
Draggables.notify('onStart', this, event);
+
if(this.options.starteffect) this.options.starteffect(this.element);
},
@@ -337,6 +356,7 @@ Draggable.prototype = {
Position.prepare();
Droppables.show(pointer, this.element);
Draggables.notify('onDrag', this, event);
+
this.draw(pointer);
if(this.options.change) this.options.change(this);
@@ -348,8 +368,8 @@ Draggable.prototype = {
with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
} else {
p = Position.page(this.options.scroll);
- p[0] += this.options.scroll.scrollLeft;
- p[1] += this.options.scroll.scrollTop;
+ p[0] += this.options.scroll.scrollLeft + Position.deltaX;
+ p[1] += this.options.scroll.scrollTop + Position.deltaY;
p.push(p[0]+this.options.scroll.offsetWidth);
p.push(p[1]+this.options.scroll.offsetHeight);
}
@@ -395,7 +415,7 @@ Draggable.prototype = {
if(this.options.endeffect)
this.options.endeffect(this.element);
-
+
Draggables.deactivate(this);
Droppables.reset();
},
@@ -415,10 +435,15 @@ Draggable.prototype = {
draw: function(point) {
var pos = Position.cumulativeOffset(this.element);
+ if(this.options.ghosting) {
+ var r = Position.realOffset(this.element);
+ pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
+ }
+
var d = this.currentDelta();
pos[0] -= d[0]; pos[1] -= d[1];
- if(this.options.scroll && (this.options.scroll != window)) {
+ if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
}
@@ -445,6 +470,7 @@ Draggable.prototype = {
style.left = p[0] + "px";
if((!this.options.constraint) || (this.options.constraint=='vertical'))
style.top = p[1] + "px";
+
if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
},
@@ -482,14 +508,16 @@ Draggable.prototype = {
Position.prepare();
Droppables.show(Draggables._lastPointer, this.element);
Draggables.notify('onDrag', this);
- Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
- Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
- Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
- if (Draggables._lastScrollPointer[0] < 0)
- Draggables._lastScrollPointer[0] = 0;
- if (Draggables._lastScrollPointer[1] < 0)
- Draggables._lastScrollPointer[1] = 0;
- this.draw(Draggables._lastScrollPointer);
+ if (this._isScrollChild) {
+ Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
+ Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
+ Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
+ if (Draggables._lastScrollPointer[0] < 0)
+ Draggables._lastScrollPointer[0] = 0;
+ if (Draggables._lastScrollPointer[1] < 0)
+ Draggables._lastScrollPointer[1] = 0;
+ this.draw(Draggables._lastScrollPointer);
+ }
if(this.options.change) this.options.change(this);
},
@@ -541,10 +569,12 @@ SortableObserver.prototype = {
}
var Sortable = {
+ SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
+
sortables: {},
_findRootElement: function(element) {
- while (element.tagName != "BODY") {
+ while (element.tagName.toUpperCase() != "BODY") {
if(element.id && Sortable.sortables[element.id]) return element;
element = element.parentNode;
}
@@ -581,12 +611,13 @@ var Sortable = {
containment: element, // also takes array of elements (or id's); or false
handle: false, // or a CSS class
only: false,
+ delay: 0,
hoverclass: null,
ghosting: false,
scroll: false,
scrollSensitivity: 20,
scrollSpeed: 15,
- format: /^[^_]*_(.*)$/,
+ format: this.SERIALIZE_RULE,
onChange: Prototype.emptyFunction,
onUpdate: Prototype.emptyFunction
}, arguments[1] || {});
@@ -600,6 +631,7 @@ var Sortable = {
scroll: options.scroll,
scrollSpeed: options.scrollSpeed,
scrollSensitivity: options.scrollSensitivity,
+ delay: options.delay,
ghosting: options.ghosting,
constraint: options.constraint,
handle: options.handle };
@@ -628,7 +660,6 @@ var Sortable = {
tree: options.tree,
hoverclass: options.hoverclass,
onHover: Sortable.onHover
- //greedy: !options.dropOnEmpty
}
var options_for_tree = {
@@ -653,7 +684,7 @@ var Sortable = {
(this.findElements(element, options) || []).each( function(e) {
// handles are per-draggable
var handle = options.handle ?
- Element.childrenWithClassName(e, options.handle)[0] : e;
+ $(e).down('.'+options.handle,0) : e;
options.draggables.push(
new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
Droppables.add(e, options_for_droppable);
@@ -751,7 +782,7 @@ var Sortable = {
},
unmark: function() {
- if(Sortable._marker) Element.hide(Sortable._marker);
+ if(Sortable._marker) Sortable._marker.hide();
},
mark: function(dropon, position) {
@@ -760,23 +791,21 @@ var Sortable = {
if(sortable && !sortable.ghosting) return;
if(!Sortable._marker) {
- Sortable._marker = $('dropmarker') || document.createElement('DIV');
- Element.hide(Sortable._marker);
- Element.addClassName(Sortable._marker, 'dropmarker');
- Sortable._marker.style.position = 'absolute';
+ Sortable._marker =
+ ($('dropmarker') || Element.extend(document.createElement('DIV'))).
+ hide().addClassName('dropmarker').setStyle({position:'absolute'});
document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
}
var offsets = Position.cumulativeOffset(dropon);
- Sortable._marker.style.left = offsets[0] + 'px';
- Sortable._marker.style.top = offsets[1] + 'px';
+ Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});
if(position=='after')
if(sortable.overlap == 'horizontal')
- Sortable._marker.style.left = (offsets[0]+dropon.clientWidth) + 'px';
+ Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});
else
- Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px';
+ Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});
- Element.show(Sortable._marker);
+ Sortable._marker.show();
},
_tree: function(element, options, parent) {
@@ -791,9 +820,9 @@ var Sortable = {
id: encodeURIComponent(match ? match[1] : null),
element: element,
parent: parent,
- children: new Array,
+ children: [],
position: parent.children.length,
- container: Sortable._findChildrenElement(children[i], options.treeTag.toUpperCase())
+ container: $(children[i]).down(options.treeTag)
}
/* Get the element containing the children and recurse over it */
@@ -806,17 +835,6 @@ var Sortable = {
return parent;
},
- /* Finds the first element of the given tag type within a parent element.
- Used for finding the first LI[ST] within a L[IST]I[TEM].*/
- _findChildrenElement: function (element, containerTag) {
- if (element && element.hasChildNodes)
- for (var i = 0; i < element.childNodes.length; ++i)
- if (element.childNodes[i].tagName == containerTag)
- return element.childNodes[i];
-
- return null;
- },
-
tree: function(element) {
element = $(element);
var sortableOptions = this.options(element);
@@ -831,12 +849,12 @@ var Sortable = {
var root = {
id: null,
parent: null,
- children: new Array,
+ children: [],
container: element,
position: 0
}
- return Sortable._tree (element, options, root);
+ return Sortable._tree(element, options, root);
},
/* Construct a [i] index for a particular node */
@@ -896,12 +914,10 @@ var Sortable = {
}
}
-/* Returns true if child is contained within element */
+// Returns true if child is contained within element
Element.isParent = function(child, element) {
if (!child.parentNode || child == element) return false;
-
if (child.parentNode == element) return true;
-
return Element.isParent(child.parentNode, element);
}
@@ -924,8 +940,5 @@ Element.findChildren = function(element, only, recursive, tagName) {
}
Element.offsetSize = function (element, type) {
- if (type == 'vertical' || type == 'height')
- return element.offsetHeight;
- else
- return element.offsetWidth;
-} \ No newline at end of file
+ return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];
+}
diff --git a/framework/Web/Javascripts/effects/effects.js b/framework/Web/Javascripts/effects/effects.js
index 1f3d50bb..06f59b47 100644
--- a/framework/Web/Javascripts/effects/effects.js
+++ b/framework/Web/Javascripts/effects/effects.js
@@ -1,15 +1,18 @@
-// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// script.aculo.us effects.js v1.7.0, Fri Jan 19 19:16:36 CET 2007
+
+// Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// Contributors:
// Justin Palmer (http://encytemedia.com/)
// Mark Pilgrim (http://diveintomark.org/)
// Martin Bialasinki
//
-// See scriptaculous.js for full license.
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
// converts rgb() and #xxx to #xxxxxx format,
// returns self (or first argument) if not convertable
String.prototype.parseColor = function() {
- var color = '#';
+ var color = '#';
if(this.slice(0,4) == 'rgb(') {
var cols = this.slice(4,this.length-1).split(',');
var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
@@ -41,48 +44,21 @@ Element.collectTextNodesIgnoreClass = function(element, className) {
Element.setContentZoom = function(element, percent) {
element = $(element);
- Element.setStyle(element, {fontSize: (percent/100) + 'em'});
+ element.setStyle({fontSize: (percent/100) + 'em'});
if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
+ return element;
}
-Element.getOpacity = function(element){
- var opacity;
- if (opacity = Element.getStyle(element, 'opacity'))
- return parseFloat(opacity);
- if (opacity = (Element.getStyle(element, 'filter') || '').match(/alpha\(opacity=(.*)\)/))
- if(opacity[1]) return parseFloat(opacity[1]) / 100;
- return 1.0;
+Element.getOpacity = function(element){
+ return $(element).getStyle('opacity');
}
-Element.setOpacity = function(element, value){
- element= $(element);
- if (value == 1){
- Element.setStyle(element, { opacity:
- (/Gecko/.test(navigator.userAgent) && !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ?
- 0.999999 : null });
- if(/MSIE/.test(navigator.userAgent))
- Element.setStyle(element, {filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});
- } else {
- if(value < 0.00001) value = 0;
- Element.setStyle(element, {opacity: value});
- if(/MSIE/.test(navigator.userAgent))
- Element.setStyle(element,
- { filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'') +
- 'alpha(opacity='+value*100+')' });
- }
-}
-
-Element.getInlineOpacity = function(element){
- return $(element).style.opacity || '';
-}
+Element.setOpacity = function(element, value){
+ return $(element).setStyle({opacity:value});
+}
-Element.childrenWithClassName = function(element, className, findFirst) {
- var classNameRegExp = new RegExp("(^|\\s)" + className + "(\\s|$)");
- var results = $A($(element).getElementsByTagName('*'))[findFirst ? 'detect' : 'select']( function(c) {
- return (c.className && c.className.match(classNameRegExp));
- });
- if(!results) results = [];
- return results;
+Element.getInlineOpacity = function(element){
+ return $(element).style.opacity || '';
}
Element.forceRerendering = function(element) {
@@ -104,12 +80,17 @@ Array.prototype.call = function() {
/*--------------------------------------------------------------------------*/
var Effect = {
+ _elementDoesNotExistError: {
+ name: 'ElementDoesNotExistError',
+ message: 'The specified DOM element does not exist, but is required for this effect to operate'
+ },
tagifyText: function(element) {
if(typeof Builder == 'undefined')
throw("Effect.tagifyText requires including script.aculo.us' builder.js library");
var tagifyStyle = 'position:relative';
- if(/MSIE/.test(navigator.userAgent)) tagifyStyle += ';zoom:1';
+ if(/MSIE/.test(navigator.userAgent) && !window.opera) tagifyStyle += ';zoom:1';
+
element = $(element);
$A(element.childNodes).each( function(child) {
if(child.nodeType==3) {
@@ -162,32 +143,35 @@ var Effect2 = Effect; // deprecated
/* ------------- transitions ------------- */
-Effect.Transitions = {}
-
-Effect.Transitions.linear = Prototype.K;
-
-Effect.Transitions.sinoidal = function(pos) {
- return (-Math.cos(pos*Math.PI)/2) + 0.5;
-}
-Effect.Transitions.reverse = function(pos) {
- return 1-pos;
-}
-Effect.Transitions.flicker = function(pos) {
- return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
-}
-Effect.Transitions.wobble = function(pos) {
- return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
-}
-Effect.Transitions.pulse = function(pos) {
- return (Math.floor(pos*10) % 2 == 0 ?
- (pos*10-Math.floor(pos*10)) : 1-(pos*10-Math.floor(pos*10)));
-}
-Effect.Transitions.none = function(pos) {
- return 0;
-}
-Effect.Transitions.full = function(pos) {
- return 1;
-}
+Effect.Transitions = {
+ linear: Prototype.K,
+ sinoidal: function(pos) {
+ return (-Math.cos(pos*Math.PI)/2) + 0.5;
+ },
+ reverse: function(pos) {
+ return 1-pos;
+ },
+ flicker: function(pos) {
+ return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
+ },
+ wobble: function(pos) {
+ return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
+ },
+ pulse: function(pos, pulses) {
+ pulses = pulses || 5;
+ return (
+ Math.round((pos % (1/pulses)) * pulses) == 0 ?
+ ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) :
+ 1 - ((pos * pulses * 2) - Math.floor(pos * pulses * 2))
+ );
+ },
+ none: function(pos) {
+ return 0;
+ },
+ full: function(pos) {
+ return 1;
+ }
+};
/* ------------- core effects ------------- */
@@ -214,6 +198,9 @@ Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
e.finishOn += effect.finishOn;
});
break;
+ case 'with-last':
+ timestamp = this.effects.pluck('startOn').max() || timestamp;
+ break;
case 'end':
// start effect after last queued effect has finished
timestamp = this.effects.pluck('finishOn').max() || timestamp;
@@ -227,7 +214,7 @@ Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
this.effects.push(effect);
if(!this.interval)
- this.interval = setInterval(this.loop.bind(this), 40);
+ this.interval = setInterval(this.loop.bind(this), 15);
},
remove: function(effect) {
this.effects = this.effects.reject(function(e) { return e==effect });
@@ -238,7 +225,8 @@ Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
},
loop: function() {
var timePos = new Date().getTime();
- this.effects.invoke('loop', timePos);
+ for(var i=0, len=this.effects.length;i<len;i++)
+ if(this.effects[i]) this.effects[i].loop(timePos);
}
});
@@ -258,7 +246,7 @@ Effect.Queue = Effect.Queues.get('global');
Effect.DefaultOptions = {
transition: Effect.Transitions.sinoidal,
duration: 1.0, // seconds
- fps: 25.0, // max. 25fps due to Effect.Queue implementation
+ fps: 60.0, // max. 60fps due to Effect.Queue implementation
sync: false, // true for combining
from: 0.0,
to: 1.0,
@@ -326,7 +314,10 @@ Effect.Base.prototype = {
if(this.options[eventName]) this.options[eventName](this);
},
inspect: function() {
- return '#<Effect:' + $H(this).inspect() + ',options:' + $H(this.options).inspect() + '>';
+ var data = $H();
+ for(property in this)
+ if(typeof this[property] != 'function') data[property] = this[property];
+ return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
}
}
@@ -350,12 +341,24 @@ Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
}
});
+Effect.Event = Class.create();
+Object.extend(Object.extend(Effect.Event.prototype, Effect.Base.prototype), {
+ initialize: function() {
+ var options = Object.extend({
+ duration: 0
+ }, arguments[0] || {});
+ this.start(options);
+ },
+ update: Prototype.emptyFunction
+});
+
Effect.Opacity = Class.create();
Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
initialize: function(element) {
this.element = $(element);
+ if(!this.element) throw(Effect._elementDoesNotExistError);
// make this work on IE on elements without 'layout'
- if(/MSIE/.test(navigator.userAgent) && (!this.element.currentStyle.hasLayout))
+ if(/MSIE/.test(navigator.userAgent) && !window.opera && (!this.element.currentStyle.hasLayout))
this.element.setStyle({zoom: 1});
var options = Object.extend({
from: this.element.getOpacity() || 0.0,
@@ -372,6 +375,7 @@ Effect.Move = Class.create();
Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
initialize: function(element) {
this.element = $(element);
+ if(!this.element) throw(Effect._elementDoesNotExistError);
var options = Object.extend({
x: 0,
y: 0,
@@ -410,7 +414,8 @@ Effect.MoveBy = function(element, toTop, toLeft) {
Effect.Scale = Class.create();
Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
initialize: function(element, percent) {
- this.element = $(element)
+ this.element = $(element);
+ if(!this.element) throw(Effect._elementDoesNotExistError);
var options = Object.extend({
scaleX: true,
scaleY: true,
@@ -460,7 +465,7 @@ Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
},
finish: function(position) {
- if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
+ if(this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
},
setDimensions: function(height, width) {
var d = {};
@@ -485,6 +490,7 @@ Effect.Highlight = Class.create();
Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
initialize: function(element) {
this.element = $(element);
+ if(!this.element) throw(Effect._elementDoesNotExistError);
var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {});
this.start(options);
},
@@ -492,9 +498,11 @@ Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype),
// Prevent executing on elements not in the layout flow
if(this.element.getStyle('display')=='none') { this.cancel(); return; }
// Disable background image during the effect
- this.oldStyle = {
- backgroundImage: this.element.getStyle('background-image') };
- this.element.setStyle({backgroundImage: 'none'});
+ this.oldStyle = {};
+ if (!this.options.keepBackgroundImage) {
+ this.oldStyle.backgroundImage = this.element.getStyle('background-image');
+ this.element.setStyle({backgroundImage: 'none'});
+ }
if(!this.options.endcolor)
this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
if(!this.options.restorecolor)
@@ -549,8 +557,7 @@ Effect.Fade = function(element) {
to: 0.0,
afterFinishInternal: function(effect) {
if(effect.options.to!=0) return;
- effect.element.hide();
- effect.element.setStyle({opacity: oldOpacity});
+ effect.element.hide().setStyle({opacity: oldOpacity});
}}, arguments[1] || {});
return new Effect.Opacity(element,options);
}
@@ -565,25 +572,31 @@ Effect.Appear = function(element) {
effect.element.forceRerendering();
},
beforeSetup: function(effect) {
- effect.element.setOpacity(effect.options.from);
- effect.element.show();
+ effect.element.setOpacity(effect.options.from).show();
}}, arguments[1] || {});
return new Effect.Opacity(element,options);
}
Effect.Puff = function(element) {
element = $(element);
- var oldStyle = { opacity: element.getInlineOpacity(), position: element.getStyle('position') };
+ var oldStyle = {
+ opacity: element.getInlineOpacity(),
+ position: element.getStyle('position'),
+ top: element.style.top,
+ left: element.style.left,
+ width: element.style.width,
+ height: element.style.height
+ };
return new Effect.Parallel(
[ new Effect.Scale(element, 200,
{ sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),
new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],
Object.extend({ duration: 1.0,
beforeSetupInternal: function(effect) {
- effect.effects[0].element.setStyle({position: 'absolute'}); },
+ Position.absolutize(effect.effects[0].element)
+ },
afterFinishInternal: function(effect) {
- effect.effects[0].element.hide();
- effect.effects[0].element.setStyle(oldStyle); }
+ effect.effects[0].element.hide().setStyle(oldStyle); }
}, arguments[1] || {})
);
}
@@ -596,8 +609,7 @@ Effect.BlindUp = function(element) {
scaleX: false,
restoreAfterFinish: true,
afterFinishInternal: function(effect) {
- effect.element.hide();
- effect.element.undoClipping();
+ effect.element.hide().undoClipping();
}
}, arguments[1] || {})
);
@@ -613,9 +625,7 @@ Effect.BlindDown = function(element) {
scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
restoreAfterFinish: true,
afterSetup: function(effect) {
- effect.element.makeClipping();
- effect.element.setStyle({height: '0px'});
- effect.element.show();
+ effect.element.makeClipping().setStyle({height: '0px'}).show();
},
afterFinishInternal: function(effect) {
effect.element.undoClipping();
@@ -635,14 +645,10 @@ Effect.SwitchOff = function(element) {
duration: 0.3, scaleFromCenter: true,
scaleX: false, scaleContent: false, restoreAfterFinish: true,
beforeSetup: function(effect) {
- effect.element.makePositioned();
- effect.element.makeClipping();
+ effect.element.makePositioned().makeClipping();
},
afterFinishInternal: function(effect) {
- effect.element.hide();
- effect.element.undoClipping();
- effect.element.undoPositioned();
- effect.element.setStyle({opacity: oldOpacity});
+ effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
}
})
}
@@ -664,9 +670,7 @@ Effect.DropOut = function(element) {
effect.effects[0].element.makePositioned();
},
afterFinishInternal: function(effect) {
- effect.effects[0].element.hide();
- effect.effects[0].element.undoPositioned();
- effect.effects[0].element.setStyle(oldStyle);
+ effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
}
}, arguments[1] || {}));
}
@@ -688,16 +692,14 @@ Effect.Shake = function(element) {
{ x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
new Effect.Move(effect.element,
{ x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
- effect.element.undoPositioned();
- effect.element.setStyle(oldStyle);
+ effect.element.undoPositioned().setStyle(oldStyle);
}}) }}) }}) }}) }}) }});
}
Effect.SlideDown = function(element) {
- element = $(element);
- element.cleanWhitespace();
+ element = $(element).cleanWhitespace();
// SlideDown need to have the content of the element wrapped in a container element with fixed height!
- var oldInnerBottom = $(element.firstChild).getStyle('bottom');
+ var oldInnerBottom = element.down().getStyle('bottom');
var elementDimensions = element.getDimensions();
return new Effect.Scale(element, 100, Object.extend({
scaleContent: false,
@@ -707,34 +709,24 @@ Effect.SlideDown = function(element) {
restoreAfterFinish: true,
afterSetup: function(effect) {
effect.element.makePositioned();
- effect.element.firstChild.makePositioned();
+ effect.element.down().makePositioned();
if(window.opera) effect.element.setStyle({top: ''});
- effect.element.makeClipping();
- effect.element.setStyle({height: '0px'});
- effect.element.show(); },
+ effect.element.makeClipping().setStyle({height: '0px'}).show();
+ },
afterUpdateInternal: function(effect) {
- effect.element.firstChild.setStyle({bottom:
+ effect.element.down().setStyle({bottom:
(effect.dims[0] - effect.element.clientHeight) + 'px' });
},
afterFinishInternal: function(effect) {
- effect.element.undoClipping();
- // IE will crash if child is undoPositioned first
- if(/MSIE/.test(navigator.userAgent)){
- effect.element.undoPositioned();
- effect.element.firstChild.undoPositioned();
- }else{
- effect.element.firstChild.undoPositioned();
- effect.element.undoPositioned();
- }
- effect.element.firstChild.setStyle({bottom: oldInnerBottom}); }
+ effect.element.undoClipping().undoPositioned();
+ effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
}, arguments[1] || {})
);
}
Effect.SlideUp = function(element) {
- element = $(element);
- element.cleanWhitespace();
- var oldInnerBottom = $(element.firstChild).getStyle('bottom');
+ element = $(element).cleanWhitespace();
+ var oldInnerBottom = element.down().getStyle('bottom');
return new Effect.Scale(element, window.opera ? 0 : 1,
Object.extend({ scaleContent: false,
scaleX: false,
@@ -743,32 +735,32 @@ Effect.SlideUp = function(element) {
restoreAfterFinish: true,
beforeStartInternal: function(effect) {
effect.element.makePositioned();
- effect.element.firstChild.makePositioned();
+ effect.element.down().makePositioned();
if(window.opera) effect.element.setStyle({top: ''});
- effect.element.makeClipping();
- effect.element.show(); },
+ effect.element.makeClipping().show();
+ },
afterUpdateInternal: function(effect) {
- effect.element.firstChild.setStyle({bottom:
- (effect.dims[0] - effect.element.clientHeight) + 'px' }); },
+ effect.element.down().setStyle({bottom:
+ (effect.dims[0] - effect.element.clientHeight) + 'px' });
+ },
afterFinishInternal: function(effect) {
- effect.element.hide();
- effect.element.undoClipping();
- effect.element.firstChild.undoPositioned();
- effect.element.undoPositioned();
- effect.element.setStyle({bottom: oldInnerBottom}); }
+ effect.element.hide().undoClipping().undoPositioned().setStyle({bottom: oldInnerBottom});
+ effect.element.down().undoPositioned();
+ }
}, arguments[1] || {})
);
}
// Bug in opera makes the TD containing this element expand for a instance after finish
Effect.Squish = function(element) {
- return new Effect.Scale(element, window.opera ? 1 : 0,
- { restoreAfterFinish: true,
- beforeSetup: function(effect) {
- effect.element.makeClipping(effect.element); },
- afterFinishInternal: function(effect) {
- effect.element.hide(effect.element);
- effect.element.undoClipping(effect.element); }
+ return new Effect.Scale(element, window.opera ? 1 : 0, {
+ restoreAfterFinish: true,
+ beforeSetup: function(effect) {
+ effect.element.makeClipping();
+ },
+ afterFinishInternal: function(effect) {
+ effect.element.hide().undoClipping();
+ }
});
}
@@ -824,9 +816,7 @@ Effect.Grow = function(element) {
y: initialMoveY,
duration: 0.01,
beforeSetup: function(effect) {
- effect.element.hide();
- effect.element.makeClipping();
- effect.element.makePositioned();
+ effect.element.hide().makeClipping().makePositioned();
},
afterFinishInternal: function(effect) {
new Effect.Parallel(
@@ -837,13 +827,10 @@ Effect.Grow = function(element) {
sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
], Object.extend({
beforeSetup: function(effect) {
- effect.effects[0].element.setStyle({height: '0px'});
- effect.effects[0].element.show();
+ effect.effects[0].element.setStyle({height: '0px'}).show();
},
afterFinishInternal: function(effect) {
- effect.effects[0].element.undoClipping();
- effect.effects[0].element.undoPositioned();
- effect.effects[0].element.setStyle(oldStyle);
+ effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle);
}
}, options)
)
@@ -897,13 +884,10 @@ Effect.Shrink = function(element) {
new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
], Object.extend({
beforeStartInternal: function(effect) {
- effect.effects[0].element.makePositioned();
- effect.effects[0].element.makeClipping(); },
+ effect.effects[0].element.makePositioned().makeClipping();
+ },
afterFinishInternal: function(effect) {
- effect.effects[0].element.hide();
- effect.effects[0].element.undoClipping();
- effect.effects[0].element.undoPositioned();
- effect.effects[0].element.setStyle(oldStyle); }
+ effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
}, options)
);
}
@@ -913,10 +897,10 @@ Effect.Pulsate = function(element) {
var options = arguments[1] || {};
var oldOpacity = element.getInlineOpacity();
var transition = options.transition || Effect.Transitions.sinoidal;
- var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos)) };
+ var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) };
reverser.bind(transition);
return new Effect.Opacity(element,
- Object.extend(Object.extend({ duration: 3.0, from: 0,
+ Object.extend(Object.extend({ duration: 2.0, from: 0,
afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
}, options), {transition: reverser}));
}
@@ -928,7 +912,7 @@ Effect.Fold = function(element) {
left: element.style.left,
width: element.style.width,
height: element.style.height };
- Element.makeClipping(element);
+ element.makeClipping();
return new Effect.Scale(element, 5, Object.extend({
scaleContent: false,
scaleX: false,
@@ -937,15 +921,162 @@ Effect.Fold = function(element) {
scaleContent: false,
scaleY: false,
afterFinishInternal: function(effect) {
- effect.element.hide();
- effect.element.undoClipping();
- effect.element.setStyle(oldStyle);
+ effect.element.hide().undoClipping().setStyle(oldStyle);
} });
}}, arguments[1] || {}));
};
+Effect.Morph = Class.create();
+Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), {
+ initialize: function(element) {
+ this.element = $(element);
+ if(!this.element) throw(Effect._elementDoesNotExistError);
+ var options = Object.extend({
+ style: {}
+ }, arguments[1] || {});
+ if (typeof options.style == 'string') {
+ if(options.style.indexOf(':') == -1) {
+ var cssText = '', selector = '.' + options.style;
+ $A(document.styleSheets).reverse().each(function(styleSheet) {
+ if (styleSheet.cssRules) cssRules = styleSheet.cssRules;
+ else if (styleSheet.rules) cssRules = styleSheet.rules;
+ $A(cssRules).reverse().each(function(rule) {
+ if (selector == rule.selectorText) {
+ cssText = rule.style.cssText;
+ throw $break;
+ }
+ });
+ if (cssText) throw $break;
+ });
+ this.style = cssText.parseStyle();
+ options.afterFinishInternal = function(effect){
+ effect.element.addClassName(effect.options.style);
+ effect.transforms.each(function(transform) {
+ if(transform.style != 'opacity')
+ effect.element.style[transform.style.camelize()] = '';
+ });
+ }
+ } else this.style = options.style.parseStyle();
+ } else this.style = $H(options.style)
+ this.start(options);
+ },
+ setup: function(){
+ function parseColor(color){
+ if(!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
+ color = color.parseColor();
+ return $R(0,2).map(function(i){
+ return parseInt( color.slice(i*2+1,i*2+3), 16 )
+ });
+ }
+ this.transforms = this.style.map(function(pair){
+ var property = pair[0].underscore().dasherize(), value = pair[1], unit = null;
+
+ if(value.parseColor('#zzzzzz') != '#zzzzzz') {
+ value = value.parseColor();
+ unit = 'color';
+ } else if(property == 'opacity') {
+ value = parseFloat(value);
+ if(/MSIE/.test(navigator.userAgent) && !window.opera && (!this.element.currentStyle.hasLayout))
+ this.element.setStyle({zoom: 1});
+ } else if(Element.CSS_LENGTH.test(value))
+ var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/),
+ value = parseFloat(components[1]), unit = (components.length == 3) ? components[2] : null;
+
+ var originalValue = this.element.getStyle(property);
+ return $H({
+ style: property,
+ originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0),
+ targetValue: unit=='color' ? parseColor(value) : value,
+ unit: unit
+ });
+ }.bind(this)).reject(function(transform){
+ return (
+ (transform.originalValue == transform.targetValue) ||
+ (
+ transform.unit != 'color' &&
+ (isNaN(transform.originalValue) || isNaN(transform.targetValue))
+ )
+ )
+ });
+ },
+ update: function(position) {
+ var style = $H(), value = null;
+ this.transforms.each(function(transform){
+ value = transform.unit=='color' ?
+ $R(0,2).inject('#',function(m,v,i){
+ return m+(Math.round(transform.originalValue[i]+
+ (transform.targetValue[i] - transform.originalValue[i])*position)).toColorPart() }) :
+ transform.originalValue + Math.round(
+ ((transform.targetValue - transform.originalValue) * position) * 1000)/1000 + transform.unit;
+ style[transform.style] = value;
+ });
+ this.element.setStyle(style);
+ }
+});
+
+Effect.Transform = Class.create();
+Object.extend(Effect.Transform.prototype, {
+ initialize: function(tracks){
+ this.tracks = [];
+ this.options = arguments[1] || {};
+ this.addTracks(tracks);
+ },
+ addTracks: function(tracks){
+ tracks.each(function(track){
+ var data = $H(track).values().first();
+ this.tracks.push($H({
+ ids: $H(track).keys().first(),
+ effect: Effect.Morph,
+ options: { style: data }
+ }));
+ }.bind(this));
+ return this;
+ },
+ play: function(){
+ return new Effect.Parallel(
+ this.tracks.map(function(track){
+ var elements = [$(track.ids) || $$(track.ids)].flatten();
+ return elements.map(function(e){ return new track.effect(e, Object.extend({ sync:true }, track.options)) });
+ }).flatten(),
+ this.options
+ );
+ }
+});
+
+Element.CSS_PROPERTIES = $w(
+ 'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' +
+ 'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
+ 'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
+ 'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
+ 'fontSize fontWeight height left letterSpacing lineHeight ' +
+ 'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+
+ 'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
+ 'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
+ 'right textIndent top width wordSpacing zIndex');
+
+Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
+
+String.prototype.parseStyle = function(){
+ var element = Element.extend(document.createElement('div'));
+ element.innerHTML = '<div style="' + this + '"></div>';
+ var style = element.down().style, styleRules = $H();
+
+ Element.CSS_PROPERTIES.each(function(property){
+ if(style[property]) styleRules[property] = style[property];
+ });
+ if(/MSIE/.test(navigator.userAgent) && !window.opera && this.indexOf('opacity') > -1) {
+ styleRules.opacity = this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1];
+ }
+ return styleRules;
+};
+
+Element.morph = function(element, style) {
+ new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || {}));
+ return element;
+};
+
['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom',
- 'collectTextNodes','collectTextNodesIgnoreClass','childrenWithClassName'].each(
+ 'collectTextNodes','collectTextNodesIgnoreClass','morph'].each(
function(f) { Element.Methods[f] = Element[f]; }
);
diff --git a/framework/Web/Javascripts/effects/slider.js b/framework/Web/Javascripts/effects/slider.js
index 696992ca..f24f2823 100644
--- a/framework/Web/Javascripts/effects/slider.js
+++ b/framework/Web/Javascripts/effects/slider.js
@@ -1,25 +1,9 @@
-// Copyright (c) 2005 Marty Haught, Thomas Fuchs
-//
-// See http://script.aculo.us for more info
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
+// script.aculo.us slider.js v1.7.0, Fri Jan 19 19:16:36 CET 2007
+
+// Copyright (c) 2005, 2006 Marty Haught, Thomas Fuchs
//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
if(!Control) var Control = {};
Control.Slider = Class.create();
@@ -239,14 +223,16 @@ Control.Slider.prototype = {
// find the handle (prevents issues with Safari)
while((this.handles.indexOf(handle) == -1) && handle.parentNode)
handle = handle.parentNode;
-
- this.activeHandle = handle;
- this.activeHandleIdx = this.handles.indexOf(this.activeHandle);
- this.updateStyles();
-
- var offsets = Position.cumulativeOffset(this.activeHandle);
- this.offsetX = (pointer[0] - offsets[0]);
- this.offsetY = (pointer[1] - offsets[1]);
+
+ if(this.handles.indexOf(handle)!=-1) {
+ this.activeHandle = handle;
+ this.activeHandleIdx = this.handles.indexOf(this.activeHandle);
+ this.updateStyles();
+
+ var offsets = Position.cumulativeOffset(this.activeHandle);
+ this.offsetX = (pointer[0] - offsets[0]);
+ this.offsetY = (pointer[1] - offsets[1]);
+ }
}
}
Event.stop(event);
diff --git a/framework/Web/Javascripts/js/compressed/effects.js b/framework/Web/Javascripts/js/compressed/effects.js
index 1a2915e6..50b64f7a 100644
--- a/framework/Web/Javascripts/js/compressed/effects.js
+++ b/framework/Web/Javascripts/js/compressed/effects.js
@@ -3,65 +3,59 @@ String.prototype.parseColor=function(){var color='#';if(this.slice(0,4)=='rgb(')
return(color.length==7?color:(arguments[0]||this));}
Element.collectTextNodes=function(element){return $A($(element).childNodes).collect(function(node){return(node.nodeType==3?node.nodeValue:(node.hasChildNodes()?Element.collectTextNodes(node):''));}).flatten().join('');}
Element.collectTextNodesIgnoreClass=function(element,className){return $A($(element).childNodes).collect(function(node){return(node.nodeType==3?node.nodeValue:((node.hasChildNodes()&&!Element.hasClassName(node,className))?Element.collectTextNodesIgnoreClass(node,className):''));}).flatten().join('');}
-Element.setContentZoom=function(element,percent){element=$(element);Element.setStyle(element,{fontSize:(percent/100)+'em'});if(navigator.appVersion.indexOf('AppleWebKit')>0)window.scrollBy(0,0);}
-Element.getOpacity=function(element){var opacity;if(opacity=Element.getStyle(element,'opacity'))
-return parseFloat(opacity);if(opacity=(Element.getStyle(element,'filter')||'').match(/alpha\(opacity=(.*)\)/))
-if(opacity[1])return parseFloat(opacity[1])/100;return 1.0;}
-Element.setOpacity=function(element,value){element=$(element);if(value==1){Element.setStyle(element,{opacity:(/Gecko/.test(navigator.userAgent)&&!/Konqueror|Safari|KHTML/.test(navigator.userAgent))?0.999999:null});if(/MSIE/.test(navigator.userAgent))
-Element.setStyle(element,{filter:Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});}else{if(value<0.00001)value=0;Element.setStyle(element,{opacity:value});if(/MSIE/.test(navigator.userAgent))
-Element.setStyle(element,{filter:Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')+'alpha(opacity='+value*100+')'});}}
+Element.setContentZoom=function(element,percent){element=$(element);element.setStyle({fontSize:(percent/100)+'em'});if(navigator.appVersion.indexOf('AppleWebKit')>0)window.scrollBy(0,0);return element;}
+Element.getOpacity=function(element){return $(element).getStyle('opacity');}
+Element.setOpacity=function(element,value){return $(element).setStyle({opacity:value});}
Element.getInlineOpacity=function(element){return $(element).style.opacity||'';}
-Element.childrenWithClassName=function(element,className,findFirst){var classNameRegExp=new RegExp("(^|\\s)"+className+"(\\s|$)");var results=$A($(element).getElementsByTagName('*'))[findFirst?'detect':'select'](function(c){return(c.className&&c.className.match(classNameRegExp));});if(!results)results=[];return results;}
Element.forceRerendering=function(element){try{element=$(element);var n=document.createTextNode(' ');element.appendChild(n);element.removeChild(n);}catch(e){}};Array.prototype.call=function(){var args=arguments;this.each(function(f){f.apply(this,args)});}
-var Effect={tagifyText:function(element){if(typeof Builder=='undefined')
-throw("Effect.tagifyText requires including script.aculo.us' builder.js library");var tagifyStyle='position:relative';if(/MSIE/.test(navigator.userAgent))tagifyStyle+=';zoom:1';element=$(element);$A(element.childNodes).each(function(child){if(child.nodeType==3){child.nodeValue.toArray().each(function(character){element.insertBefore(Builder.node('span',{style:tagifyStyle},character==' '?String.fromCharCode(160):character),child);});Element.remove(child);}});},multiple:function(element,effect){var elements;if(((typeof element=='object')||(typeof element=='function'))&&(element.length))
+var Effect={_elementDoesNotExistError:{name:'ElementDoesNotExistError',message:'The specified DOM element does not exist, but is required for this effect to operate'},tagifyText:function(element){if(typeof Builder=='undefined')
+throw("Effect.tagifyText requires including script.aculo.us' builder.js library");var tagifyStyle='position:relative';if(/MSIE/.test(navigator.userAgent)&&!window.opera)tagifyStyle+=';zoom:1';element=$(element);$A(element.childNodes).each(function(child){if(child.nodeType==3){child.nodeValue.toArray().each(function(character){element.insertBefore(Builder.node('span',{style:tagifyStyle},character==' '?String.fromCharCode(160):character),child);});Element.remove(child);}});},multiple:function(element,effect){var elements;if(((typeof element=='object')||(typeof element=='function'))&&(element.length))
elements=element;else
-elements=$(element).childNodes;var options=Object.extend({speed:0.1,delay:0.0},arguments[2]||{});var masterDelay=options.delay;$A(elements).each(function(element,index){new effect(element,Object.extend(options,{delay:index*options.speed+masterDelay}));});},PAIRS:{'slide':['SlideDown','SlideUp'],'blind':['BlindDown','BlindUp'],'appear':['Appear','Fade']},toggle:function(element,effect){element=$(element);effect=(effect||'appear').toLowerCase();var options=Object.extend({queue:{position:'end',scope:(element.id||'global'),limit:1}},arguments[2]||{});Effect[element.visible()?Effect.PAIRS[effect][1]:Effect.PAIRS[effect][0]](element,options);}};var Effect2=Effect;Effect.Transitions={}
-Effect.Transitions.linear=Prototype.K;Effect.Transitions.sinoidal=function(pos){return(-Math.cos(pos*Math.PI)/2)+0.5;}
-Effect.Transitions.reverse=function(pos){return 1-pos;}
-Effect.Transitions.flicker=function(pos){return((-Math.cos(pos*Math.PI)/4)+0.75)+Math.random()/4;}
-Effect.Transitions.wobble=function(pos){return(-Math.cos(pos*Math.PI*(9*pos))/2)+0.5;}
-Effect.Transitions.pulse=function(pos){return(Math.floor(pos*10)%2==0?(pos*10-Math.floor(pos*10)):1-(pos*10-Math.floor(pos*10)));}
-Effect.Transitions.none=function(pos){return 0;}
-Effect.Transitions.full=function(pos){return 1;}
-Effect.ScopedQueue=Class.create();Object.extend(Object.extend(Effect.ScopedQueue.prototype,Enumerable),{initialize:function(){this.effects=[];this.interval=null;},_each:function(iterator){this.effects._each(iterator);},add:function(effect){var timestamp=new Date().getTime();var position=(typeof effect.options.queue=='string')?effect.options.queue:effect.options.queue.position;switch(position){case'front':this.effects.findAll(function(e){return e.state=='idle'}).each(function(e){e.startOn+=effect.finishOn;e.finishOn+=effect.finishOn;});break;case'end':timestamp=this.effects.pluck('finishOn').max()||timestamp;break;}
+elements=$(element).childNodes;var options=Object.extend({speed:0.1,delay:0.0},arguments[2]||{});var masterDelay=options.delay;$A(elements).each(function(element,index){new effect(element,Object.extend(options,{delay:index*options.speed+masterDelay}));});},PAIRS:{'slide':['SlideDown','SlideUp'],'blind':['BlindDown','BlindUp'],'appear':['Appear','Fade']},toggle:function(element,effect){element=$(element);effect=(effect||'appear').toLowerCase();var options=Object.extend({queue:{position:'end',scope:(element.id||'global'),limit:1}},arguments[2]||{});Effect[element.visible()?Effect.PAIRS[effect][1]:Effect.PAIRS[effect][0]](element,options);}};var Effect2=Effect;Effect.Transitions={linear:Prototype.K,sinoidal:function(pos){return(-Math.cos(pos*Math.PI)/2)+0.5;},reverse:function(pos){return 1-pos;},flicker:function(pos){return((-Math.cos(pos*Math.PI)/4)+0.75)+Math.random()/4;},wobble:function(pos){return(-Math.cos(pos*Math.PI*(9*pos))/2)+0.5;},pulse:function(pos,pulses){pulses=pulses||5;return(Math.round((pos%(1/pulses))*pulses)==0?((pos*pulses*2)-Math.floor(pos*pulses*2)):1-((pos*pulses*2)-Math.floor(pos*pulses*2)));},none:function(pos){return 0;},full:function(pos){return 1;}};Effect.ScopedQueue=Class.create();Object.extend(Object.extend(Effect.ScopedQueue.prototype,Enumerable),{initialize:function(){this.effects=[];this.interval=null;},_each:function(iterator){this.effects._each(iterator);},add:function(effect){var timestamp=new Date().getTime();var position=(typeof effect.options.queue=='string')?effect.options.queue:effect.options.queue.position;switch(position){case'front':this.effects.findAll(function(e){return e.state=='idle'}).each(function(e){e.startOn+=effect.finishOn;e.finishOn+=effect.finishOn;});break;case'with-last':timestamp=this.effects.pluck('startOn').max()||timestamp;break;case'end':timestamp=this.effects.pluck('finishOn').max()||timestamp;break;}
effect.startOn+=timestamp;effect.finishOn+=timestamp;if(!effect.options.queue.limit||(this.effects.length<effect.options.queue.limit))
this.effects.push(effect);if(!this.interval)
-this.interval=setInterval(this.loop.bind(this),40);},remove:function(effect){this.effects=this.effects.reject(function(e){return e==effect});if(this.effects.length==0){clearInterval(this.interval);this.interval=null;}},loop:function(){var timePos=new Date().getTime();this.effects.invoke('loop',timePos);}});Effect.Queues={instances:$H(),get:function(queueName){if(typeof queueName!='string')return queueName;if(!this.instances[queueName])
+this.interval=setInterval(this.loop.bind(this),15);},remove:function(effect){this.effects=this.effects.reject(function(e){return e==effect});if(this.effects.length==0){clearInterval(this.interval);this.interval=null;}},loop:function(){var timePos=new Date().getTime();for(var i=0,len=this.effects.length;i<len;i++)
+if(this.effects[i])this.effects[i].loop(timePos);}});Effect.Queues={instances:$H(),get:function(queueName){if(typeof queueName!='string')return queueName;if(!this.instances[queueName])
this.instances[queueName]=new Effect.ScopedQueue();return this.instances[queueName];}}
-Effect.Queue=Effect.Queues.get('global');Effect.DefaultOptions={transition:Effect.Transitions.sinoidal,duration:1.0,fps:25.0,sync:false,from:0.0,to:1.0,delay:0.0,queue:'parallel'}
+Effect.Queue=Effect.Queues.get('global');Effect.DefaultOptions={transition:Effect.Transitions.sinoidal,duration:1.0,fps:60.0,sync:false,from:0.0,to:1.0,delay:0.0,queue:'parallel'}
Effect.Base=function(){};Effect.Base.prototype={position:null,start:function(options){this.options=Object.extend(Object.extend({},Effect.DefaultOptions),options||{});this.currentFrame=0;this.state='idle';this.startOn=this.options.delay*1000;this.finishOn=this.startOn+(this.options.duration*1000);this.event('beforeStart');if(!this.options.sync)
Effect.Queues.get(typeof this.options.queue=='string'?'global':this.options.queue.scope).add(this);},loop:function(timePos){if(timePos>=this.startOn){if(timePos>=this.finishOn){this.render(1.0);this.cancel();this.event('beforeFinish');if(this.finish)this.finish();this.event('afterFinish');return;}
var pos=(timePos-this.startOn)/(this.finishOn-this.startOn);var frame=Math.round(pos*this.options.fps*this.options.duration);if(frame>this.currentFrame){this.render(pos);this.currentFrame=frame;}}},render:function(pos){if(this.state=='idle'){this.state='running';this.event('beforeSetup');if(this.setup)this.setup();this.event('afterSetup');}
if(this.state=='running'){if(this.options.transition)pos=this.options.transition(pos);pos*=(this.options.to-this.options.from);pos+=this.options.from;this.position=pos;this.event('beforeUpdate');if(this.update)this.update(pos);this.event('afterUpdate');}},cancel:function(){if(!this.options.sync)
-Effect.Queues.get(typeof this.options.queue=='string'?'global':this.options.queue.scope).remove(this);this.state='finished';},event:function(eventName){if(this.options[eventName+'Internal'])this.options[eventName+'Internal'](this);if(this.options[eventName])this.options[eventName](this);},inspect:function(){return'#<Effect:'+$H(this).inspect()+',options:'+$H(this.options).inspect()+'>';}}
-Effect.Parallel=Class.create();Object.extend(Object.extend(Effect.Parallel.prototype,Effect.Base.prototype),{initialize:function(effects){this.effects=effects||[];this.start(arguments[1]);},update:function(position){this.effects.invoke('render',position);},finish:function(position){this.effects.each(function(effect){effect.render(1.0);effect.cancel();effect.event('beforeFinish');if(effect.finish)effect.finish(position);effect.event('afterFinish');});}});Effect.Opacity=Class.create();Object.extend(Object.extend(Effect.Opacity.prototype,Effect.Base.prototype),{initialize:function(element){this.element=$(element);if(/MSIE/.test(navigator.userAgent)&&(!this.element.currentStyle.hasLayout))
-this.element.setStyle({zoom:1});var options=Object.extend({from:this.element.getOpacity()||0.0,to:1.0},arguments[1]||{});this.start(options);},update:function(position){this.element.setOpacity(position);}});Effect.Move=Class.create();Object.extend(Object.extend(Effect.Move.prototype,Effect.Base.prototype),{initialize:function(element){this.element=$(element);var options=Object.extend({x:0,y:0,mode:'relative'},arguments[1]||{});this.start(options);},setup:function(){this.element.makePositioned();this.originalLeft=parseFloat(this.element.getStyle('left')||'0');this.originalTop=parseFloat(this.element.getStyle('top')||'0');if(this.options.mode=='absolute'){this.options.x=this.options.x-this.originalLeft;this.options.y=this.options.y-this.originalTop;}},update:function(position){this.element.setStyle({left:Math.round(this.options.x*position+this.originalLeft)+'px',top:Math.round(this.options.y*position+this.originalTop)+'px'});}});Effect.MoveBy=function(element,toTop,toLeft){return new Effect.Move(element,Object.extend({x:toLeft,y:toTop},arguments[3]||{}));};Effect.Scale=Class.create();Object.extend(Object.extend(Effect.Scale.prototype,Effect.Base.prototype),{initialize:function(element,percent){this.element=$(element)
-var options=Object.extend({scaleX:true,scaleY:true,scaleContent:true,scaleFromCenter:false,scaleMode:'box',scaleFrom:100.0,scaleTo:percent},arguments[2]||{});this.start(options);},setup:function(){this.restoreAfterFinish=this.options.restoreAfterFinish||false;this.elementPositioning=this.element.getStyle('position');this.originalStyle={};['top','left','width','height','fontSize'].each(function(k){this.originalStyle[k]=this.element.style[k];}.bind(this));this.originalTop=this.element.offsetTop;this.originalLeft=this.element.offsetLeft;var fontSize=this.element.getStyle('font-size')||'100%';['em','px','%','pt'].each(function(fontSizeType){if(fontSize.indexOf(fontSizeType)>0){this.fontSize=parseFloat(fontSize);this.fontSizeType=fontSizeType;}}.bind(this));this.factor=(this.options.scaleTo-this.options.scaleFrom)/100;this.dims=null;if(this.options.scaleMode=='box')
+Effect.Queues.get(typeof this.options.queue=='string'?'global':this.options.queue.scope).remove(this);this.state='finished';},event:function(eventName){if(this.options[eventName+'Internal'])this.options[eventName+'Internal'](this);if(this.options[eventName])this.options[eventName](this);},inspect:function(){var data=$H();for(property in this)
+if(typeof this[property]!='function')data[property]=this[property];return'#<Effect:'+data.inspect()+',options:'+$H(this.options).inspect()+'>';}}
+Effect.Parallel=Class.create();Object.extend(Object.extend(Effect.Parallel.prototype,Effect.Base.prototype),{initialize:function(effects){this.effects=effects||[];this.start(arguments[1]);},update:function(position){this.effects.invoke('render',position);},finish:function(position){this.effects.each(function(effect){effect.render(1.0);effect.cancel();effect.event('beforeFinish');if(effect.finish)effect.finish(position);effect.event('afterFinish');});}});Effect.Event=Class.create();Object.extend(Object.extend(Effect.Event.prototype,Effect.Base.prototype),{initialize:function(){var options=Object.extend({duration:0},arguments[0]||{});this.start(options);},update:Prototype.emptyFunction});Effect.Opacity=Class.create();Object.extend(Object.extend(Effect.Opacity.prototype,Effect.Base.prototype),{initialize:function(element){this.element=$(element);if(!this.element)throw(Effect._elementDoesNotExistError);if(/MSIE/.test(navigator.userAgent)&&!window.opera&&(!this.element.currentStyle.hasLayout))
+this.element.setStyle({zoom:1});var options=Object.extend({from:this.element.getOpacity()||0.0,to:1.0},arguments[1]||{});this.start(options);},update:function(position){this.element.setOpacity(position);}});Effect.Move=Class.create();Object.extend(Object.extend(Effect.Move.prototype,Effect.Base.prototype),{initialize:function(element){this.element=$(element);if(!this.element)throw(Effect._elementDoesNotExistError);var options=Object.extend({x:0,y:0,mode:'relative'},arguments[1]||{});this.start(options);},setup:function(){this.element.makePositioned();this.originalLeft=parseFloat(this.element.getStyle('left')||'0');this.originalTop=parseFloat(this.element.getStyle('top')||'0');if(this.options.mode=='absolute'){this.options.x=this.options.x-this.originalLeft;this.options.y=this.options.y-this.originalTop;}},update:function(position){this.element.setStyle({left:Math.round(this.options.x*position+this.originalLeft)+'px',top:Math.round(this.options.y*position+this.originalTop)+'px'});}});Effect.MoveBy=function(element,toTop,toLeft){return new Effect.Move(element,Object.extend({x:toLeft,y:toTop},arguments[3]||{}));};Effect.Scale=Class.create();Object.extend(Object.extend(Effect.Scale.prototype,Effect.Base.prototype),{initialize:function(element,percent){this.element=$(element);if(!this.element)throw(Effect._elementDoesNotExistError);var options=Object.extend({scaleX:true,scaleY:true,scaleContent:true,scaleFromCenter:false,scaleMode:'box',scaleFrom:100.0,scaleTo:percent},arguments[2]||{});this.start(options);},setup:function(){this.restoreAfterFinish=this.options.restoreAfterFinish||false;this.elementPositioning=this.element.getStyle('position');this.originalStyle={};['top','left','width','height','fontSize'].each(function(k){this.originalStyle[k]=this.element.style[k];}.bind(this));this.originalTop=this.element.offsetTop;this.originalLeft=this.element.offsetLeft;var fontSize=this.element.getStyle('font-size')||'100%';['em','px','%','pt'].each(function(fontSizeType){if(fontSize.indexOf(fontSizeType)>0){this.fontSize=parseFloat(fontSize);this.fontSizeType=fontSizeType;}}.bind(this));this.factor=(this.options.scaleTo-this.options.scaleFrom)/100;this.dims=null;if(this.options.scaleMode=='box')
this.dims=[this.element.offsetHeight,this.element.offsetWidth];if(/^content/.test(this.options.scaleMode))
this.dims=[this.element.scrollHeight,this.element.scrollWidth];if(!this.dims)
this.dims=[this.options.scaleMode.originalHeight,this.options.scaleMode.originalWidth];},update:function(position){var currentScale=(this.options.scaleFrom/100.0)+(this.factor*position);if(this.options.scaleContent&&this.fontSize)
this.element.setStyle({fontSize:this.fontSize*currentScale+this.fontSizeType});this.setDimensions(this.dims[0]*currentScale,this.dims[1]*currentScale);},finish:function(position){if(this.restoreAfterFinish)this.element.setStyle(this.originalStyle);},setDimensions:function(height,width){var d={};if(this.options.scaleX)d.width=Math.round(width)+'px';if(this.options.scaleY)d.height=Math.round(height)+'px';if(this.options.scaleFromCenter){var topd=(height-this.dims[0])/2;var leftd=(width-this.dims[1])/2;if(this.elementPositioning=='absolute'){if(this.options.scaleY)d.top=this.originalTop-topd+'px';if(this.options.scaleX)d.left=this.originalLeft-leftd+'px';}else{if(this.options.scaleY)d.top=-topd+'px';if(this.options.scaleX)d.left=-leftd+'px';}}
-this.element.setStyle(d);}});Effect.Highlight=Class.create();Object.extend(Object.extend(Effect.Highlight.prototype,Effect.Base.prototype),{initialize:function(element){this.element=$(element);var options=Object.extend({startcolor:'#ffff99'},arguments[1]||{});this.start(options);},setup:function(){if(this.element.getStyle('display')=='none'){this.cancel();return;}
-this.oldStyle={backgroundImage:this.element.getStyle('background-image')};this.element.setStyle({backgroundImage:'none'});if(!this.options.endcolor)
+this.element.setStyle(d);}});Effect.Highlight=Class.create();Object.extend(Object.extend(Effect.Highlight.prototype,Effect.Base.prototype),{initialize:function(element){this.element=$(element);if(!this.element)throw(Effect._elementDoesNotExistError);var options=Object.extend({startcolor:'#ffff99'},arguments[1]||{});this.start(options);},setup:function(){if(this.element.getStyle('display')=='none'){this.cancel();return;}
+this.oldStyle={};if(!this.options.keepBackgroundImage){this.oldStyle.backgroundImage=this.element.getStyle('background-image');this.element.setStyle({backgroundImage:'none'});}
+if(!this.options.endcolor)
this.options.endcolor=this.element.getStyle('background-color').parseColor('#ffffff');if(!this.options.restorecolor)
this.options.restorecolor=this.element.getStyle('background-color');this._base=$R(0,2).map(function(i){return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16)}.bind(this));this._delta=$R(0,2).map(function(i){return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i]}.bind(this));},update:function(position){this.element.setStyle({backgroundColor:$R(0,2).inject('#',function(m,v,i){return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart());}.bind(this))});},finish:function(){this.element.setStyle(Object.extend(this.oldStyle,{backgroundColor:this.options.restorecolor}));}});Effect.ScrollTo=Class.create();Object.extend(Object.extend(Effect.ScrollTo.prototype,Effect.Base.prototype),{initialize:function(element){this.element=$(element);this.start(arguments[1]||{});},setup:function(){Position.prepare();var offsets=Position.cumulativeOffset(this.element);if(this.options.offset)offsets[1]+=this.options.offset;var max=window.innerHeight?window.height-window.innerHeight:document.body.scrollHeight-
-(document.documentElement.clientHeight?document.documentElement.clientHeight:document.body.clientHeight);this.scrollStart=Position.deltaY;this.delta=(offsets[1]>max?max:offsets[1])-this.scrollStart;},update:function(position){Position.prepare();window.scrollTo(Position.deltaX,this.scrollStart+(position*this.delta));}});Effect.Fade=function(element){element=$(element);var oldOpacity=element.getInlineOpacity();var options=Object.extend({from:element.getOpacity()||1.0,to:0.0,afterFinishInternal:function(effect){if(effect.options.to!=0)return;effect.element.hide();effect.element.setStyle({opacity:oldOpacity});}},arguments[1]||{});return new Effect.Opacity(element,options);}
-Effect.Appear=function(element){element=$(element);var options=Object.extend({from:(element.getStyle('display')=='none'?0.0:element.getOpacity()||0.0),to:1.0,afterFinishInternal:function(effect){effect.element.forceRerendering();},beforeSetup:function(effect){effect.element.setOpacity(effect.options.from);effect.element.show();}},arguments[1]||{});return new Effect.Opacity(element,options);}
-Effect.Puff=function(element){element=$(element);var oldStyle={opacity:element.getInlineOpacity(),position:element.getStyle('position')};return new Effect.Parallel([new Effect.Scale(element,200,{sync:true,scaleFromCenter:true,scaleContent:true,restoreAfterFinish:true}),new Effect.Opacity(element,{sync:true,to:0.0})],Object.extend({duration:1.0,beforeSetupInternal:function(effect){effect.effects[0].element.setStyle({position:'absolute'});},afterFinishInternal:function(effect){effect.effects[0].element.hide();effect.effects[0].element.setStyle(oldStyle);}},arguments[1]||{}));}
-Effect.BlindUp=function(element){element=$(element);element.makeClipping();return new Effect.Scale(element,0,Object.extend({scaleContent:false,scaleX:false,restoreAfterFinish:true,afterFinishInternal:function(effect){effect.element.hide();effect.element.undoClipping();}},arguments[1]||{}));}
-Effect.BlindDown=function(element){element=$(element);var elementDimensions=element.getDimensions();return new Effect.Scale(element,100,Object.extend({scaleContent:false,scaleX:false,scaleFrom:0,scaleMode:{originalHeight:elementDimensions.height,originalWidth:elementDimensions.width},restoreAfterFinish:true,afterSetup:function(effect){effect.element.makeClipping();effect.element.setStyle({height:'0px'});effect.element.show();},afterFinishInternal:function(effect){effect.element.undoClipping();}},arguments[1]||{}));}
-Effect.SwitchOff=function(element){element=$(element);var oldOpacity=element.getInlineOpacity();return new Effect.Appear(element,Object.extend({duration:0.4,from:0,transition:Effect.Transitions.flicker,afterFinishInternal:function(effect){new Effect.Scale(effect.element,1,{duration:0.3,scaleFromCenter:true,scaleX:false,scaleContent:false,restoreAfterFinish:true,beforeSetup:function(effect){effect.element.makePositioned();effect.element.makeClipping();},afterFinishInternal:function(effect){effect.element.hide();effect.element.undoClipping();effect.element.undoPositioned();effect.element.setStyle({opacity:oldOpacity});}})}},arguments[1]||{}));}
-Effect.DropOut=function(element){element=$(element);var oldStyle={top:element.getStyle('top'),left:element.getStyle('left'),opacity:element.getInlineOpacity()};return new Effect.Parallel([new Effect.Move(element,{x:0,y:100,sync:true}),new Effect.Opacity(element,{sync:true,to:0.0})],Object.extend({duration:0.5,beforeSetup:function(effect){effect.effects[0].element.makePositioned();},afterFinishInternal:function(effect){effect.effects[0].element.hide();effect.effects[0].element.undoPositioned();effect.effects[0].element.setStyle(oldStyle);}},arguments[1]||{}));}
-Effect.Shake=function(element){element=$(element);var oldStyle={top:element.getStyle('top'),left:element.getStyle('left')};return new Effect.Move(element,{x:20,y:0,duration:0.05,afterFinishInternal:function(effect){new Effect.Move(effect.element,{x:-40,y:0,duration:0.1,afterFinishInternal:function(effect){new Effect.Move(effect.element,{x:40,y:0,duration:0.1,afterFinishInternal:function(effect){new Effect.Move(effect.element,{x:-40,y:0,duration:0.1,afterFinishInternal:function(effect){new Effect.Move(effect.element,{x:40,y:0,duration:0.1,afterFinishInternal:function(effect){new Effect.Move(effect.element,{x:-20,y:0,duration:0.05,afterFinishInternal:function(effect){effect.element.undoPositioned();effect.element.setStyle(oldStyle);}})}})}})}})}})}});}
-Effect.SlideDown=function(element){element=$(element);element.cleanWhitespace();var oldInnerBottom=$(element.firstChild).getStyle('bottom');var elementDimensions=element.getDimensions();return new Effect.Scale(element,100,Object.extend({scaleContent:false,scaleX:false,scaleFrom:window.opera?0:1,scaleMode:{originalHeight:elementDimensions.height,originalWidth:elementDimensions.width},restoreAfterFinish:true,afterSetup:function(effect){effect.element.makePositioned();effect.element.firstChild.makePositioned();if(window.opera)effect.element.setStyle({top:''});effect.element.makeClipping();effect.element.setStyle({height:'0px'});effect.element.show();},afterUpdateInternal:function(effect){effect.element.firstChild.setStyle({bottom:(effect.dims[0]-effect.element.clientHeight)+'px'});},afterFinishInternal:function(effect){effect.element.undoClipping();if(/MSIE/.test(navigator.userAgent)){effect.element.undoPositioned();effect.element.firstChild.undoPositioned();}else{effect.element.firstChild.undoPositioned();effect.element.undoPositioned();}
-effect.element.firstChild.setStyle({bottom:oldInnerBottom});}},arguments[1]||{}));}
-Effect.SlideUp=function(element){element=$(element);element.cleanWhitespace();var oldInnerBottom=$(element.firstChild).getStyle('bottom');return new Effect.Scale(element,window.opera?0:1,Object.extend({scaleContent:false,scaleX:false,scaleMode:'box',scaleFrom:100,restoreAfterFinish:true,beforeStartInternal:function(effect){effect.element.makePositioned();effect.element.firstChild.makePositioned();if(window.opera)effect.element.setStyle({top:''});effect.element.makeClipping();effect.element.show();},afterUpdateInternal:function(effect){effect.element.firstChild.setStyle({bottom:(effect.dims[0]-effect.element.clientHeight)+'px'});},afterFinishInternal:function(effect){effect.element.hide();effect.element.undoClipping();effect.element.firstChild.undoPositioned();effect.element.undoPositioned();effect.element.setStyle({bottom:oldInnerBottom});}},arguments[1]||{}));}
-Effect.Squish=function(element){return new Effect.Scale(element,window.opera?1:0,{restoreAfterFinish:true,beforeSetup:function(effect){effect.element.makeClipping(effect.element);},afterFinishInternal:function(effect){effect.element.hide(effect.element);effect.element.undoClipping(effect.element);}});}
+(document.documentElement.clientHeight?document.documentElement.clientHeight:document.body.clientHeight);this.scrollStart=Position.deltaY;this.delta=(offsets[1]>max?max:offsets[1])-this.scrollStart;},update:function(position){Position.prepare();window.scrollTo(Position.deltaX,this.scrollStart+(position*this.delta));}});Effect.Fade=function(element){element=$(element);var oldOpacity=element.getInlineOpacity();var options=Object.extend({from:element.getOpacity()||1.0,to:0.0,afterFinishInternal:function(effect){if(effect.options.to!=0)return;effect.element.hide().setStyle({opacity:oldOpacity});}},arguments[1]||{});return new Effect.Opacity(element,options);}
+Effect.Appear=function(element){element=$(element);var options=Object.extend({from:(element.getStyle('display')=='none'?0.0:element.getOpacity()||0.0),to:1.0,afterFinishInternal:function(effect){effect.element.forceRerendering();},beforeSetup:function(effect){effect.element.setOpacity(effect.options.from).show();}},arguments[1]||{});return new Effect.Opacity(element,options);}
+Effect.Puff=function(element){element=$(element);var oldStyle={opacity:element.getInlineOpacity(),position:element.getStyle('position'),top:element.style.top,left:element.style.left,width:element.style.width,height:element.style.height};return new Effect.Parallel([new Effect.Scale(element,200,{sync:true,scaleFromCenter:true,scaleContent:true,restoreAfterFinish:true}),new Effect.Opacity(element,{sync:true,to:0.0})],Object.extend({duration:1.0,beforeSetupInternal:function(effect){Position.absolutize(effect.effects[0].element)},afterFinishInternal:function(effect){effect.effects[0].element.hide().setStyle(oldStyle);}},arguments[1]||{}));}
+Effect.BlindUp=function(element){element=$(element);element.makeClipping();return new Effect.Scale(element,0,Object.extend({scaleContent:false,scaleX:false,restoreAfterFinish:true,afterFinishInternal:function(effect){effect.element.hide().undoClipping();}},arguments[1]||{}));}
+Effect.BlindDown=function(element){element=$(element);var elementDimensions=element.getDimensions();return new Effect.Scale(element,100,Object.extend({scaleContent:false,scaleX:false,scaleFrom:0,scaleMode:{originalHeight:elementDimensions.height,originalWidth:elementDimensions.width},restoreAfterFinish:true,afterSetup:function(effect){effect.element.makeClipping().setStyle({height:'0px'}).show();},afterFinishInternal:function(effect){effect.element.undoClipping();}},arguments[1]||{}));}
+Effect.SwitchOff=function(element){element=$(element);var oldOpacity=element.getInlineOpacity();return new Effect.Appear(element,Object.extend({duration:0.4,from:0,transition:Effect.Transitions.flicker,afterFinishInternal:function(effect){new Effect.Scale(effect.element,1,{duration:0.3,scaleFromCenter:true,scaleX:false,scaleContent:false,restoreAfterFinish:true,beforeSetup:function(effect){effect.element.makePositioned().makeClipping();},afterFinishInternal:function(effect){effect.element.hide().undoClipping().undoPositioned().setStyle({opacity:oldOpacity});}})}},arguments[1]||{}));}
+Effect.DropOut=function(element){element=$(element);var oldStyle={top:element.getStyle('top'),left:element.getStyle('left'),opacity:element.getInlineOpacity()};return new Effect.Parallel([new Effect.Move(element,{x:0,y:100,sync:true}),new Effect.Opacity(element,{sync:true,to:0.0})],Object.extend({duration:0.5,beforeSetup:function(effect){effect.effects[0].element.makePositioned();},afterFinishInternal:function(effect){effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);}},arguments[1]||{}));}
+Effect.Shake=function(element){element=$(element);var oldStyle={top:element.getStyle('top'),left:element.getStyle('left')};return new Effect.Move(element,{x:20,y:0,duration:0.05,afterFinishInternal:function(effect){new Effect.Move(effect.element,{x:-40,y:0,duration:0.1,afterFinishInternal:function(effect){new Effect.Move(effect.element,{x:40,y:0,duration:0.1,afterFinishInternal:function(effect){new Effect.Move(effect.element,{x:-40,y:0,duration:0.1,afterFinishInternal:function(effect){new Effect.Move(effect.element,{x:40,y:0,duration:0.1,afterFinishInternal:function(effect){new Effect.Move(effect.element,{x:-20,y:0,duration:0.05,afterFinishInternal:function(effect){effect.element.undoPositioned().setStyle(oldStyle);}})}})}})}})}})}});}
+Effect.SlideDown=function(element){element=$(element).cleanWhitespace();var oldInnerBottom=element.down().getStyle('bottom');var elementDimensions=element.getDimensions();return new Effect.Scale(element,100,Object.extend({scaleContent:false,scaleX:false,scaleFrom:window.opera?0:1,scaleMode:{originalHeight:elementDimensions.height,originalWidth:elementDimensions.width},restoreAfterFinish:true,afterSetup:function(effect){effect.element.makePositioned();effect.element.down().makePositioned();if(window.opera)effect.element.setStyle({top:''});effect.element.makeClipping().setStyle({height:'0px'}).show();},afterUpdateInternal:function(effect){effect.element.down().setStyle({bottom:(effect.dims[0]-effect.element.clientHeight)+'px'});},afterFinishInternal:function(effect){effect.element.undoClipping().undoPositioned();effect.element.down().undoPositioned().setStyle({bottom:oldInnerBottom});}},arguments[1]||{}));}
+Effect.SlideUp=function(element){element=$(element).cleanWhitespace();var oldInnerBottom=element.down().getStyle('bottom');return new Effect.Scale(element,window.opera?0:1,Object.extend({scaleContent:false,scaleX:false,scaleMode:'box',scaleFrom:100,restoreAfterFinish:true,beforeStartInternal:function(effect){effect.element.makePositioned();effect.element.down().makePositioned();if(window.opera)effect.element.setStyle({top:''});effect.element.makeClipping().show();},afterUpdateInternal:function(effect){effect.element.down().setStyle({bottom:(effect.dims[0]-effect.element.clientHeight)+'px'});},afterFinishInternal:function(effect){effect.element.hide().undoClipping().undoPositioned().setStyle({bottom:oldInnerBottom});effect.element.down().undoPositioned();}},arguments[1]||{}));}
+Effect.Squish=function(element){return new Effect.Scale(element,window.opera?1:0,{restoreAfterFinish:true,beforeSetup:function(effect){effect.element.makeClipping();},afterFinishInternal:function(effect){effect.element.hide().undoClipping();}});}
Effect.Grow=function(element){element=$(element);var options=Object.extend({direction:'center',moveTransition:Effect.Transitions.sinoidal,scaleTransition:Effect.Transitions.sinoidal,opacityTransition:Effect.Transitions.full},arguments[1]||{});var oldStyle={top:element.style.top,left:element.style.left,height:element.style.height,width:element.style.width,opacity:element.getInlineOpacity()};var dims=element.getDimensions();var initialMoveX,initialMoveY;var moveX,moveY;switch(options.direction){case'top-left':initialMoveX=initialMoveY=moveX=moveY=0;break;case'top-right':initialMoveX=dims.width;initialMoveY=moveY=0;moveX=-dims.width;break;case'bottom-left':initialMoveX=moveX=0;initialMoveY=dims.height;moveY=-dims.height;break;case'bottom-right':initialMoveX=dims.width;initialMoveY=dims.height;moveX=-dims.width;moveY=-dims.height;break;case'center':initialMoveX=dims.width/2;initialMoveY=dims.height/2;moveX=-dims.width/2;moveY=-dims.height/2;break;}
-return new Effect.Move(element,{x:initialMoveX,y:initialMoveY,duration:0.01,beforeSetup:function(effect){effect.element.hide();effect.element.makeClipping();effect.element.makePositioned();},afterFinishInternal:function(effect){new Effect.Parallel([new Effect.Opacity(effect.element,{sync:true,to:1.0,from:0.0,transition:options.opacityTransition}),new Effect.Move(effect.element,{x:moveX,y:moveY,sync:true,transition:options.moveTransition}),new Effect.Scale(effect.element,100,{scaleMode:{originalHeight:dims.height,originalWidth:dims.width},sync:true,scaleFrom:window.opera?1:0,transition:options.scaleTransition,restoreAfterFinish:true})],Object.extend({beforeSetup:function(effect){effect.effects[0].element.setStyle({height:'0px'});effect.effects[0].element.show();},afterFinishInternal:function(effect){effect.effects[0].element.undoClipping();effect.effects[0].element.undoPositioned();effect.effects[0].element.setStyle(oldStyle);}},options))}});}
+return new Effect.Move(element,{x:initialMoveX,y:initialMoveY,duration:0.01,beforeSetup:function(effect){effect.element.hide().makeClipping().makePositioned();},afterFinishInternal:function(effect){new Effect.Parallel([new Effect.Opacity(effect.element,{sync:true,to:1.0,from:0.0,transition:options.opacityTransition}),new Effect.Move(effect.element,{x:moveX,y:moveY,sync:true,transition:options.moveTransition}),new Effect.Scale(effect.element,100,{scaleMode:{originalHeight:dims.height,originalWidth:dims.width},sync:true,scaleFrom:window.opera?1:0,transition:options.scaleTransition,restoreAfterFinish:true})],Object.extend({beforeSetup:function(effect){effect.effects[0].element.setStyle({height:'0px'}).show();},afterFinishInternal:function(effect){effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle);}},options))}});}
Effect.Shrink=function(element){element=$(element);var options=Object.extend({direction:'center',moveTransition:Effect.Transitions.sinoidal,scaleTransition:Effect.Transitions.sinoidal,opacityTransition:Effect.Transitions.none},arguments[1]||{});var oldStyle={top:element.style.top,left:element.style.left,height:element.style.height,width:element.style.width,opacity:element.getInlineOpacity()};var dims=element.getDimensions();var moveX,moveY;switch(options.direction){case'top-left':moveX=moveY=0;break;case'top-right':moveX=dims.width;moveY=0;break;case'bottom-left':moveX=0;moveY=dims.height;break;case'bottom-right':moveX=dims.width;moveY=dims.height;break;case'center':moveX=dims.width/2;moveY=dims.height/2;break;}
-return new Effect.Parallel([new Effect.Opacity(element,{sync:true,to:0.0,from:1.0,transition:options.opacityTransition}),new Effect.Scale(element,window.opera?1:0,{sync:true,transition:options.scaleTransition,restoreAfterFinish:true}),new Effect.Move(element,{x:moveX,y:moveY,sync:true,transition:options.moveTransition})],Object.extend({beforeStartInternal:function(effect){effect.effects[0].element.makePositioned();effect.effects[0].element.makeClipping();},afterFinishInternal:function(effect){effect.effects[0].element.hide();effect.effects[0].element.undoClipping();effect.effects[0].element.undoPositioned();effect.effects[0].element.setStyle(oldStyle);}},options));}
-Effect.Pulsate=function(element){element=$(element);var options=arguments[1]||{};var oldOpacity=element.getInlineOpacity();var transition=options.transition||Effect.Transitions.sinoidal;var reverser=function(pos){return transition(1-Effect.Transitions.pulse(pos))};reverser.bind(transition);return new Effect.Opacity(element,Object.extend(Object.extend({duration:3.0,from:0,afterFinishInternal:function(effect){effect.element.setStyle({opacity:oldOpacity});}},options),{transition:reverser}));}
-Effect.Fold=function(element){element=$(element);var oldStyle={top:element.style.top,left:element.style.left,width:element.style.width,height:element.style.height};Element.makeClipping(element);return new Effect.Scale(element,5,Object.extend({scaleContent:false,scaleX:false,afterFinishInternal:function(effect){new Effect.Scale(element,1,{scaleContent:false,scaleY:false,afterFinishInternal:function(effect){effect.element.hide();effect.element.undoClipping();effect.element.setStyle(oldStyle);}});}},arguments[1]||{}));};['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom','collectTextNodes','collectTextNodesIgnoreClass','childrenWithClassName'].each(function(f){Element.Methods[f]=Element[f];});Element.Methods.visualEffect=function(element,effect,options){s=effect.gsub(/_/,'-').camelize();effect_class=s.charAt(0).toUpperCase()+s.substring(1);new Effect[effect_class](element,options);return $(element);};Element.addMethods();Prado.Effect={Highlight:function(element,options)
-{new Effect.Highlight(element,options||{});}} \ No newline at end of file
+return new Effect.Parallel([new Effect.Opacity(element,{sync:true,to:0.0,from:1.0,transition:options.opacityTransition}),new Effect.Scale(element,window.opera?1:0,{sync:true,transition:options.scaleTransition,restoreAfterFinish:true}),new Effect.Move(element,{x:moveX,y:moveY,sync:true,transition:options.moveTransition})],Object.extend({beforeStartInternal:function(effect){effect.effects[0].element.makePositioned().makeClipping();},afterFinishInternal:function(effect){effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle);}},options));}
+Effect.Pulsate=function(element){element=$(element);var options=arguments[1]||{};var oldOpacity=element.getInlineOpacity();var transition=options.transition||Effect.Transitions.sinoidal;var reverser=function(pos){return transition(1-Effect.Transitions.pulse(pos,options.pulses))};reverser.bind(transition);return new Effect.Opacity(element,Object.extend(Object.extend({duration:2.0,from:0,afterFinishInternal:function(effect){effect.element.setStyle({opacity:oldOpacity});}},options),{transition:reverser}));}
+Effect.Fold=function(element){element=$(element);var oldStyle={top:element.style.top,left:element.style.left,width:element.style.width,height:element.style.height};element.makeClipping();return new Effect.Scale(element,5,Object.extend({scaleContent:false,scaleX:false,afterFinishInternal:function(effect){new Effect.Scale(element,1,{scaleContent:false,scaleY:false,afterFinishInternal:function(effect){effect.element.hide().undoClipping().setStyle(oldStyle);}});}},arguments[1]||{}));};Effect.Morph=Class.create();Object.extend(Object.extend(Effect.Morph.prototype,Effect.Base.prototype),{initialize:function(element){this.element=$(element);if(!this.element)throw(Effect._elementDoesNotExistError);var options=Object.extend({style:{}},arguments[1]||{});if(typeof options.style=='string'){if(options.style.indexOf(':')==-1){var cssText='',selector='.'+options.style;$A(document.styleSheets).reverse().each(function(styleSheet){if(styleSheet.cssRules)cssRules=styleSheet.cssRules;else if(styleSheet.rules)cssRules=styleSheet.rules;$A(cssRules).reverse().each(function(rule){if(selector==rule.selectorText){cssText=rule.style.cssText;throw $break;}});if(cssText)throw $break;});this.style=cssText.parseStyle();options.afterFinishInternal=function(effect){effect.element.addClassName(effect.options.style);effect.transforms.each(function(transform){if(transform.style!='opacity')
+effect.element.style[transform.style.camelize()]='';});}}else this.style=options.style.parseStyle();}else this.style=$H(options.style)
+this.start(options);},setup:function(){function parseColor(color){if(!color||['rgba(0, 0, 0, 0)','transparent'].include(color))color='#ffffff';color=color.parseColor();return $R(0,2).map(function(i){return parseInt(color.slice(i*2+1,i*2+3),16)});}
+this.transforms=this.style.map(function(pair){var property=pair[0].underscore().dasherize(),value=pair[1],unit=null;if(value.parseColor('#zzzzzz')!='#zzzzzz'){value=value.parseColor();unit='color';}else if(property=='opacity'){value=parseFloat(value);if(/MSIE/.test(navigator.userAgent)&&!window.opera&&(!this.element.currentStyle.hasLayout))
+this.element.setStyle({zoom:1});}else if(Element.CSS_LENGTH.test(value))
+var components=value.match(/^([\+\-]?[0-9\.]+)(.*)$/),value=parseFloat(components[1]),unit=(components.length==3)?components[2]:null;var originalValue=this.element.getStyle(property);return $H({style:property,originalValue:unit=='color'?parseColor(originalValue):parseFloat(originalValue||0),targetValue:unit=='color'?parseColor(value):value,unit:unit});}.bind(this)).reject(function(transform){return((transform.originalValue==transform.targetValue)||(transform.unit!='color'&&(isNaN(transform.originalValue)||isNaN(transform.targetValue))))});},update:function(position){var style=$H(),value=null;this.transforms.each(function(transform){value=transform.unit=='color'?$R(0,2).inject('#',function(m,v,i){return m+(Math.round(transform.originalValue[i]+
+(transform.targetValue[i]-transform.originalValue[i])*position)).toColorPart()}):transform.originalValue+Math.round(((transform.targetValue-transform.originalValue)*position)*1000)/1000+transform.unit;style[transform.style]=value;});this.element.setStyle(style);}});Effect.Transform=Class.create();Object.extend(Effect.Transform.prototype,{initialize:function(tracks){this.tracks=[];this.options=arguments[1]||{};this.addTracks(tracks);},addTracks:function(tracks){tracks.each(function(track){var data=$H(track).values().first();this.tracks.push($H({ids:$H(track).keys().first(),effect:Effect.Morph,options:{style:data}}));}.bind(this));return this;},play:function(){return new Effect.Parallel(this.tracks.map(function(track){var elements=[$(track.ids)||$$(track.ids)].flatten();return elements.map(function(e){return new track.effect(e,Object.extend({sync:true},track.options))});}).flatten(),this.options);}});Element.CSS_PROPERTIES=$w('backgroundColor backgroundPosition borderBottomColor borderBottomStyle '+'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth '+'borderRightColor borderRightStyle borderRightWidth borderSpacing '+'borderTopColor borderTopStyle borderTopWidth bottom clip color '+'fontSize fontWeight height left letterSpacing lineHeight '+'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+'maxWidth minHeight minWidth opacity outlineColor outlineOffset '+'outlineWidth paddingBottom paddingLeft paddingRight paddingTop '+'right textIndent top width wordSpacing zIndex');Element.CSS_LENGTH=/^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;String.prototype.parseStyle=function(){var element=Element.extend(document.createElement('div'));element.innerHTML='<div style="'+this+'"></div>';var style=element.down().style,styleRules=$H();Element.CSS_PROPERTIES.each(function(property){if(style[property])styleRules[property]=style[property];});if(/MSIE/.test(navigator.userAgent)&&!window.opera&&this.indexOf('opacity')>-1){styleRules.opacity=this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1];}
+return styleRules;};Element.morph=function(element,style){new Effect.Morph(element,Object.extend({style:style},arguments[2]||{}));return element;};['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom','collectTextNodes','collectTextNodesIgnoreClass','morph'].each(function(f){Element.Methods[f]=Element[f];});Element.Methods.visualEffect=function(element,effect,options){s=effect.gsub(/_/,'-').camelize();effect_class=s.charAt(0).toUpperCase()+s.substring(1);new Effect[effect_class](element,options);return $(element);};Element.addMethods(); \ No newline at end of file
diff --git a/framework/Web/Javascripts/js/compressed/prado.js b/framework/Web/Javascripts/js/compressed/prado.js
index f338cb67..e0299c97 100644
--- a/framework/Web/Javascripts/js/compressed/prado.js
+++ b/framework/Web/Javascripts/js/compressed/prado.js
@@ -1,14 +1,255 @@
-var Prototype={Version:'1.50',ScriptFragment:'(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',emptyFunction:function(){},K:function(x){return x}}
+var Prototype={Version:'1.5.1_rc2',Browser:{IE:!!(window.attachEvent&&!window.opera),Opera:!!window.opera,WebKit:navigator.userAgent.indexOf('AppleWebKit/')>-1,Gecko:navigator.userAgent.indexOf('Gecko')>-1&&navigator.userAgent.indexOf('KHTML')==-1},BrowserFeatures:{XPath:!!document.evaluate,ElementExtensions:!!window.HTMLElement,SpecificElementExtensions:(document.createElement('div').__proto__!==document.createElement('form').__proto__)},ScriptFragment:'(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',emptyFunction:function(){},K:function(x){return x}}
var Class={create:function(){return function(){this.initialize.apply(this,arguments);}}}
var Abstract=new Object();Object.extend=function(destination,source){for(var property in source){destination[property]=source[property];}
return destination;}
-Object.inspect=function(object){try{if(object==undefined)return'undefined';if(object==null)return'null';return object.inspect?object.inspect():object.toString();}catch(e){if(e instanceof RangeError)return'...';throw e;}}
-Function.prototype.bind=function(){var __method=this,args=$A(arguments),object=args.shift();return function(){return __method.apply(object,args.concat($A(arguments)));}}
-Function.prototype.bindAsEventListener=function(object){var __method=this;return function(event){return __method.call(object,event||window.event);}}
-Object.extend(Number.prototype,{toColorPart:function(){var digits=this.toString(16);if(this<16)return'0'+digits;return digits;},succ:function(){return this+1;},times:function(iterator){$R(0,this,true).each(iterator);return this;}});var Try={these:function(){var returnValue;for(var i=0;i<arguments.length;i++){var lambda=arguments[i];try{returnValue=lambda();break;}catch(e){}}
+Object.extend(Object,{inspect:function(object){try{if(object===undefined)return'undefined';if(object===null)return'null';return object.inspect?object.inspect():object.toString();}catch(e){if(e instanceof RangeError)return'...';throw e;}},toJSON:function(object){var type=typeof object;switch(type){case'undefined':case'function':case'unknown':return;case'boolean':return object.toString();}
+if(object===null)return'null';if(object.toJSON)return object.toJSON();if(object.ownerDocument===document)return;var results=[];for(var property in object){var value=Object.toJSON(object[property]);if(value!==undefined)
+results.push(property.toJSON()+':'+value);}
+return'{'+results.join(',')+'}';},keys:function(object){var keys=[];for(var property in object)
+keys.push(property);return keys;},values:function(object){var values=[];for(var property in object)
+values.push(object[property]);return values;},clone:function(object){return Object.extend({},object);}});Function.prototype.bind=function(){var __method=this,args=$A(arguments),object=args.shift();return function(){return __method.apply(object,args.concat($A(arguments)));}}
+Function.prototype.bindAsEventListener=function(object){var __method=this,args=$A(arguments),object=args.shift();return function(event){return __method.apply(object,[(event||window.event)].concat(args).concat($A(arguments)));}}
+Object.extend(Number.prototype,{toColorPart:function(){return this.toPaddedString(2,16);},succ:function(){return this+1;},times:function(iterator){$R(0,this,true).each(iterator);return this;},toPaddedString:function(length,radix){var string=this.toString(radix||10);return'0'.times(length-string.length)+string;},toJSON:function(){return isFinite(this)?this.toString():'null';}});Date.prototype.toJSON=function(){return'"'+this.getFullYear()+'-'+
+(this.getMonth()+1).toPaddedString(2)+'-'+
+this.getDate().toPaddedString(2)+'T'+
+this.getHours().toPaddedString(2)+':'+
+this.getMinutes().toPaddedString(2)+':'+
+this.getSeconds().toPaddedString(2)+'"';};var Try={these:function(){var returnValue;for(var i=0,length=arguments.length;i<length;i++){var lambda=arguments[i];try{returnValue=lambda();break;}catch(e){}}
return returnValue;}}
-var PeriodicalExecuter=Class.create();PeriodicalExecuter.prototype={initialize:function(callback,frequency){this.callback=callback;this.frequency=frequency;this.currentlyExecuting=false;this.registerCallback();},registerCallback:function(){setInterval(this.onTimerEvent.bind(this),this.frequency*1000);},onTimerEvent:function(){if(!this.currentlyExecuting){try{this.currentlyExecuting=true;this.callback();}finally{this.currentlyExecuting=false;}}}}
+var PeriodicalExecuter=Class.create();PeriodicalExecuter.prototype={initialize:function(callback,frequency){this.callback=callback;this.frequency=frequency;this.currentlyExecuting=false;this.registerCallback();},registerCallback:function(){this.timer=setInterval(this.onTimerEvent.bind(this),this.frequency*1000);},stop:function(){if(!this.timer)return;clearInterval(this.timer);this.timer=null;},onTimerEvent:function(){if(!this.currentlyExecuting){try{this.currentlyExecuting=true;this.callback(this);}finally{this.currentlyExecuting=false;}}}}
+Object.extend(String,{interpret:function(value){return value==null?'':String(value);},specialChar:{'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','\\':'\\\\'}});Object.extend(String.prototype,{gsub:function(pattern,replacement){var result='',source=this,match;replacement=arguments.callee.prepareReplacement(replacement);while(source.length>0){if(match=source.match(pattern)){result+=source.slice(0,match.index);result+=String.interpret(replacement(match));source=source.slice(match.index+match[0].length);}else{result+=source,source='';}}
+return result;},sub:function(pattern,replacement,count){replacement=this.gsub.prepareReplacement(replacement);count=count===undefined?1:count;return this.gsub(pattern,function(match){if(--count<0)return match[0];return replacement(match);});},scan:function(pattern,iterator){this.gsub(pattern,iterator);return this;},truncate:function(length,truncation){length=length||30;truncation=truncation===undefined?'...':truncation;return this.length>length?this.slice(0,length-truncation.length)+truncation:this;},strip:function(){return this.replace(/^\s+/,'').replace(/\s+$/,'');},stripTags:function(){return this.replace(/<\/?[^>]+>/gi,'');},stripScripts:function(){return this.replace(new RegExp(Prototype.ScriptFragment,'img'),'');},extractScripts:function(){var matchAll=new RegExp(Prototype.ScriptFragment,'img');var matchOne=new RegExp(Prototype.ScriptFragment,'im');return(this.match(matchAll)||[]).map(function(scriptTag){return(scriptTag.match(matchOne)||['',''])[1];});},evalScripts:function(){return this.extractScripts().map(function(script){return eval(script)});},escapeHTML:function(){var self=arguments.callee;self.text.data=this;return self.div.innerHTML;},unescapeHTML:function(){var div=document.createElement('div');div.innerHTML=this.stripTags();return div.childNodes[0]?(div.childNodes.length>1?$A(div.childNodes).inject('',function(memo,node){return memo+node.nodeValue}):div.childNodes[0].nodeValue):'';},toQueryParams:function(separator){var match=this.strip().match(/([^?#]*)(#.*)?$/);if(!match)return{};return match[1].split(separator||'&').inject({},function(hash,pair){if((pair=pair.split('='))[0]){var name=decodeURIComponent(pair[0]);var value=pair[1]?decodeURIComponent(pair[1]):undefined;if(hash[name]!==undefined){if(hash[name].constructor!=Array)
+hash[name]=[hash[name]];if(value)hash[name].push(value);}
+else hash[name]=value;}
+return hash;});},toArray:function(){return this.split('');},succ:function(){return this.slice(0,this.length-1)+
+String.fromCharCode(this.charCodeAt(this.length-1)+1);},times:function(count){var result='';for(var i=0;i<count;i++)result+=this;return result;},camelize:function(){var parts=this.split('-'),len=parts.length;if(len==1)return parts[0];var camelized=this.charAt(0)=='-'?parts[0].charAt(0).toUpperCase()+parts[0].substring(1):parts[0];for(var i=1;i<len;i++)
+camelized+=parts[i].charAt(0).toUpperCase()+parts[i].substring(1);return camelized;},capitalize:function(){return this.charAt(0).toUpperCase()+this.substring(1).toLowerCase();},underscore:function(){return this.gsub(/::/,'/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();},dasherize:function(){return this.gsub(/_/,'-');},inspect:function(useDoubleQuotes){var escapedString=this.gsub(/[\x00-\x1f\\]/,function(match){var character=String.specialChar[match[0]];return character?character:'\\u00'+match[0].charCodeAt().toPaddedString(2,16);});if(useDoubleQuotes)return'"'+escapedString.replace(/"/g,'\\"')+'"';return"'"+escapedString.replace(/'/g,'\\\'')+"'";},toJSON:function(){return this.inspect(true);},evalJSON:function(sanitize){try{if(!sanitize||(/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(this)))
+return eval('('+this+')');}catch(e){}
+throw new SyntaxError('Badly formated JSON string: '+this.inspect());},include:function(pattern){return this.indexOf(pattern)>-1;},startsWith:function(pattern){return this.indexOf(pattern)==0;},endsWith:function(pattern){return this.lastIndexOf(pattern)==(this.length-pattern.length);},empty:function(){return this=='';},blank:function(){return/^\s*$/.test(this);}});String.prototype.gsub.prepareReplacement=function(replacement){if(typeof replacement=='function')return replacement;var template=new Template(replacement);return function(match){return template.evaluate(match)};}
+String.prototype.parseQuery=String.prototype.toQueryParams;Object.extend(String.prototype.escapeHTML,{div:document.createElement('div'),text:document.createTextNode('')});with(String.prototype.escapeHTML)div.appendChild(text);var Template=Class.create();Template.Pattern=/(^|.|\r|\n)(#\{(.*?)\})/;Template.prototype={initialize:function(template,pattern){this.template=template.toString();this.pattern=pattern||Template.Pattern;},evaluate:function(object){return this.template.gsub(this.pattern,function(match){var before=match[1];if(before=='\\')return match[2];return before+String.interpret(object[match[3]]);});}}
+var $break=new Object();var $continue=new Object();var Enumerable={each:function(iterator){var index=0;try{this._each(function(value){iterator(value,index++);});}catch(e){if(e!=$break)throw e;}
+return this;},eachSlice:function(number,iterator){var index=-number,slices=[],array=this.toArray();while((index+=number)<array.length)
+slices.push(array.slice(index,index+number));return slices.map(iterator);},all:function(iterator){var result=true;this.each(function(value,index){result=result&&!!(iterator||Prototype.K)(value,index);if(!result)throw $break;});return result;},any:function(iterator){var result=false;this.each(function(value,index){if(result=!!(iterator||Prototype.K)(value,index))
+throw $break;});return result;},collect:function(iterator){var results=[];this.each(function(value,index){results.push((iterator||Prototype.K)(value,index));});return results;},detect:function(iterator){var result;this.each(function(value,index){if(iterator(value,index)){result=value;throw $break;}});return result;},findAll:function(iterator){var results=[];this.each(function(value,index){if(iterator(value,index))
+results.push(value);});return results;},grep:function(pattern,iterator){var results=[];this.each(function(value,index){var stringValue=value.toString();if(stringValue.match(pattern))
+results.push((iterator||Prototype.K)(value,index));})
+return results;},include:function(object){var found=false;this.each(function(value){if(value==object){found=true;throw $break;}});return found;},inGroupsOf:function(number,fillWith){fillWith=fillWith===undefined?null:fillWith;return this.eachSlice(number,function(slice){while(slice.length<number)slice.push(fillWith);return slice;});},inject:function(memo,iterator){this.each(function(value,index){memo=iterator(memo,value,index);});return memo;},invoke:function(method){var args=$A(arguments).slice(1);return this.map(function(value){return value[method].apply(value,args);});},max:function(iterator){var result;this.each(function(value,index){value=(iterator||Prototype.K)(value,index);if(result==undefined||value>=result)
+result=value;});return result;},min:function(iterator){var result;this.each(function(value,index){value=(iterator||Prototype.K)(value,index);if(result==undefined||value<result)
+result=value;});return result;},partition:function(iterator){var trues=[],falses=[];this.each(function(value,index){((iterator||Prototype.K)(value,index)?trues:falses).push(value);});return[trues,falses];},pluck:function(property){var results=[];this.each(function(value,index){results.push(value[property]);});return results;},reject:function(iterator){var results=[];this.each(function(value,index){if(!iterator(value,index))
+results.push(value);});return results;},sortBy:function(iterator){return this.map(function(value,index){return{value:value,criteria:iterator(value,index)};}).sort(function(left,right){var a=left.criteria,b=right.criteria;return a<b?-1:a>b?1:0;}).pluck('value');},toArray:function(){return this.map();},zip:function(){var iterator=Prototype.K,args=$A(arguments);if(typeof args.last()=='function')
+iterator=args.pop();var collections=[this].concat(args).map($A);return this.map(function(value,index){return iterator(collections.pluck(index));});},size:function(){return this.toArray().length;},inspect:function(){return'#<Enumerable:'+this.toArray().inspect()+'>';}}
+Object.extend(Enumerable,{map:Enumerable.collect,find:Enumerable.detect,select:Enumerable.findAll,member:Enumerable.include,entries:Enumerable.toArray});var $A=Array.from=function(iterable){if(!iterable)return[];if(iterable.toArray){return iterable.toArray();}else{var results=[];for(var i=0,length=iterable.length;i<length;i++)
+results.push(iterable[i]);return results;}}
+if(Prototype.Browser.WebKit){$A=Array.from=function(iterable){if(!iterable)return[];if(!(typeof iterable=='function'&&iterable=='[object NodeList]')&&iterable.toArray){return iterable.toArray();}else{var results=[];for(var i=0,length=iterable.length;i<length;i++)
+results.push(iterable[i]);return results;}}}
+Object.extend(Array.prototype,Enumerable);if(!Array.prototype._reverse)
+Array.prototype._reverse=Array.prototype.reverse;Object.extend(Array.prototype,{_each:function(iterator){for(var i=0,length=this.length;i<length;i++)
+iterator(this[i]);},clear:function(){this.length=0;return this;},first:function(){return this[0];},last:function(){return this[this.length-1];},compact:function(){return this.select(function(value){return value!=null;});},flatten:function(){return this.inject([],function(array,value){return array.concat(value&&value.constructor==Array?value.flatten():[value]);});},without:function(){var values=$A(arguments);return this.select(function(value){return!values.include(value);});},indexOf:function(object){for(var i=0,length=this.length;i<length;i++)
+if(this[i]==object)return i;return-1;},reverse:function(inline){return(inline!==false?this:this.toArray())._reverse();},reduce:function(){return this.length>1?this:this[0];},uniq:function(sorted){return this.inject([],function(array,value,index){if(0==index||(sorted?array.last()!=value:!array.include(value)))
+array.push(value);return array;});},clone:function(){return[].concat(this);},size:function(){return this.length;},inspect:function(){return'['+this.map(Object.inspect).join(', ')+']';},toJSON:function(){var results=[];this.each(function(object){var value=Object.toJSON(object);if(value!==undefined)results.push(value);});return'['+results.join(',')+']';}});Array.prototype.toArray=Array.prototype.clone;function $w(string){string=string.strip();return string?string.split(/\s+/):[];}
+if(Prototype.Browser.Opera){Array.prototype.concat=function(){var array=[];for(var i=0,length=this.length;i<length;i++)array.push(this[i]);for(var i=0,length=arguments.length;i<length;i++){if(arguments[i].constructor==Array){for(var j=0,arrayLength=arguments[i].length;j<arrayLength;j++)
+array.push(arguments[i][j]);}else{array.push(arguments[i]);}}
+return array;}}
+var Hash=function(object){if(object instanceof Hash)this.merge(object);else Object.extend(this,object||{});};Object.extend(Hash,{toQueryString:function(obj){var parts=[];parts.add=arguments.callee.addPair;this.prototype._each.call(obj,function(pair){if(!pair.key)return;var value=pair.value;if(value&&typeof value=='object'){if(value.constructor==Array)value.each(function(value){parts.add(pair.key,value);});return;}
+parts.add(pair.key,value);});return parts.join('&');},toJSON:function(object){var results=[];this.prototype._each.call(object,function(pair){var value=Object.toJSON(pair.value);if(value!==undefined)results.push(pair.key.toJSON()+':'+value);});return'{'+results.join(',')+'}';}});Hash.toQueryString.addPair=function(key,value,prefix){if(value==null)return;key=encodeURIComponent(key);this.push(key+'='+(value==null?'':encodeURIComponent(value)));}
+Object.extend(Hash.prototype,Enumerable);Object.extend(Hash.prototype,{_each:function(iterator){for(var key in this){var value=this[key];if(value&&value==Hash.prototype[key])continue;var pair=[key,value];pair.key=key;pair.value=value;iterator(pair);}},keys:function(){return this.pluck('key');},values:function(){return this.pluck('value');},merge:function(hash){return $H(hash).inject(this,function(mergedHash,pair){mergedHash[pair.key]=pair.value;return mergedHash;});},remove:function(){var result;for(var i=0,length=arguments.length;i<length;i++){var value=this[arguments[i]];if(value!==undefined){if(result===undefined)result=value;else{if(result.constructor!=Array)result=[result];result.push(value)}}
+delete this[arguments[i]];}
+return result;},toQueryString:function(){return Hash.toQueryString(this);},inspect:function(){return'#<Hash:{'+this.map(function(pair){return pair.map(Object.inspect).join(': ');}).join(', ')+'}>';},toJSON:function(){return Hash.toJSON(this);}});function $H(object){if(object instanceof Hash)return object;return new Hash(object);};if(function(){var i=0,Test=function(value){this.key=value};Test.prototype.key='foo';for(var property in new Test('bar'))i++;return i>1;}())Hash.prototype._each=function(iterator){var cache=[];for(var key in this){var value=this[key];if((value&&value==Hash.prototype[key])||cache.include(key))continue;cache.push(key);var pair=[key,value];pair.key=key;pair.value=value;iterator(pair);}};ObjectRange=Class.create();Object.extend(ObjectRange.prototype,Enumerable);Object.extend(ObjectRange.prototype,{initialize:function(start,end,exclusive){this.start=start;this.end=end;this.exclusive=exclusive;},_each:function(iterator){var value=this.start;while(this.include(value)){iterator(value);value=value.succ();}},include:function(value){if(value<this.start)
+return false;if(this.exclusive)
+return value<this.end;return value<=this.end;}});var $R=function(start,end,exclusive){return new ObjectRange(start,end,exclusive);}
+var Ajax={getTransport:function(){return Try.these(function(){return new XMLHttpRequest()},function(){return new ActiveXObject('Msxml2.XMLHTTP')},function(){return new ActiveXObject('Microsoft.XMLHTTP')})||false;},activeRequestCount:0}
+Ajax.Responders={responders:[],_each:function(iterator){this.responders._each(iterator);},register:function(responder){if(!this.include(responder))
+this.responders.push(responder);},unregister:function(responder){this.responders=this.responders.without(responder);},dispatch:function(callback,request,transport,json){this.each(function(responder){if(typeof responder[callback]=='function'){try{responder[callback].apply(responder,[request,transport,json]);}catch(e){}}});}};Object.extend(Ajax.Responders,Enumerable);Ajax.Responders.register({onCreate:function(){Ajax.activeRequestCount++;},onComplete:function(){Ajax.activeRequestCount--;}});Ajax.Base=function(){};Ajax.Base.prototype={setOptions:function(options){this.options={method:'post',asynchronous:true,contentType:'application/x-www-form-urlencoded',encoding:'UTF-8',parameters:''}
+Object.extend(this.options,options||{});this.options.method=this.options.method.toLowerCase();if(typeof this.options.parameters=='string')
+this.options.parameters=this.options.parameters.toQueryParams();}}
+Ajax.Request=Class.create();Ajax.Request.Events=['Uninitialized','Loading','Loaded','Interactive','Complete'];Ajax.Request.prototype=Object.extend(new Ajax.Base(),{_complete:false,initialize:function(url,options){this.transport=Ajax.getTransport();this.setOptions(options);this.request(url);},request:function(url){this.url=url;this.method=this.options.method;var params=Object.clone(this.options.parameters);if(!['get','post'].include(this.method)){params['_method']=this.method;this.method='post';}
+this.parameters=params;if(params=Hash.toQueryString(params)){if(this.method=='get')
+this.url+=(this.url.include('?')?'&':'?')+params;else if(/Konqueror|Safari|KHTML/.test(navigator.userAgent))
+params+='&_=';}
+try{Ajax.Responders.dispatch('onCreate',this,this.transport);this.transport.open(this.method.toUpperCase(),this.url,this.options.asynchronous);if(this.options.asynchronous)
+setTimeout(function(){this.respondToReadyState(1)}.bind(this),10);this.transport.onreadystatechange=this.onStateChange.bind(this);this.setRequestHeaders();this.body=this.method=='post'?(this.options.postBody||params):null;this.transport.send(this.body);if(!this.options.asynchronous&&this.transport.overrideMimeType)
+this.onStateChange();}
+catch(e){this.dispatchException(e);}},onStateChange:function(){var readyState=this.transport.readyState;if(readyState>1&&!((readyState==4)&&this._complete))
+this.respondToReadyState(this.transport.readyState);},setRequestHeaders:function(){var headers={'X-Requested-With':'XMLHttpRequest','X-Prototype-Version':Prototype.Version,'Accept':'text/javascript, text/html, application/xml, text/xml, */*'};if(this.method=='post'){headers['Content-type']=this.options.contentType+
+(this.options.encoding?'; charset='+this.options.encoding:'');if(this.transport.overrideMimeType&&(navigator.userAgent.match(/Gecko\/(\d{4})/)||[0,2005])[1]<2005)
+headers['Connection']='close';}
+if(typeof this.options.requestHeaders=='object'){var extras=this.options.requestHeaders;if(typeof extras.push=='function')
+for(var i=0,length=extras.length;i<length;i+=2)
+headers[extras[i]]=extras[i+1];else
+$H(extras).each(function(pair){headers[pair.key]=pair.value});}
+for(var name in headers)
+this.transport.setRequestHeader(name,headers[name]);},success:function(){return!this.transport.status||(this.transport.status>=200&&this.transport.status<300);},respondToReadyState:function(readyState){var state=Ajax.Request.Events[readyState];var transport=this.transport,json=this.evalJSON();if(state=='Complete'){try{this._complete=true;(this.options['on'+this.transport.status]||this.options['on'+(this.success()?'Success':'Failure')]||Prototype.emptyFunction)(transport,json);}catch(e){this.dispatchException(e);}
+if((this.getHeader('Content-type')||'text/javascript').strip().match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
+this.evalResponse();}
+try{(this.options['on'+state]||Prototype.emptyFunction)(transport,json);Ajax.Responders.dispatch('on'+state,this,transport,json);}catch(e){this.dispatchException(e);}
+if(state=='Complete'){this.transport.onreadystatechange=Prototype.emptyFunction;}},getHeader:function(name){try{return this.transport.getResponseHeader(name);}catch(e){return null}},evalJSON:function(){try{var json=this.getHeader('X-JSON');return json?eval('('+json+')'):null;}catch(e){return null}},evalResponse:function(){try{return eval(this.transport.responseText);}catch(e){this.dispatchException(e);}},dispatchException:function(exception){(this.options.onException||Prototype.emptyFunction)(this,exception);Ajax.Responders.dispatch('onException',this,exception);}});Ajax.Updater=Class.create();Object.extend(Object.extend(Ajax.Updater.prototype,Ajax.Request.prototype),{initialize:function(container,url,options){this.container={success:(container.success||container),failure:(container.failure||(container.success?null:container))}
+this.transport=Ajax.getTransport();this.setOptions(options);var onComplete=this.options.onComplete||Prototype.emptyFunction;this.options.onComplete=(function(transport,param){this.updateContent();onComplete(transport,param);}).bind(this);this.request(url);},updateContent:function(){var receiver=this.container[this.success()?'success':'failure'];var response=this.transport.responseText;if(!this.options.evalScripts)response=response.stripScripts();if(receiver=$(receiver)){if(this.options.insertion)
+new this.options.insertion(receiver,response);else
+receiver.update(response);}
+if(this.success()){if(this.onComplete)
+setTimeout(this.onComplete.bind(this),10);}}});Ajax.PeriodicalUpdater=Class.create();Ajax.PeriodicalUpdater.prototype=Object.extend(new Ajax.Base(),{initialize:function(container,url,options){this.setOptions(options);this.onComplete=this.options.onComplete;this.frequency=(this.options.frequency||2);this.decay=(this.options.decay||1);this.updater={};this.container=container;this.url=url;this.start();},start:function(){this.options.onComplete=this.updateComplete.bind(this);this.onTimerEvent();},stop:function(){this.updater.options.onComplete=undefined;clearTimeout(this.timer);(this.onComplete||Prototype.emptyFunction).apply(this,arguments);},updateComplete:function(request){if(this.options.decay){this.decay=(request.responseText==this.lastText?this.decay*this.options.decay:1);this.lastText=request.responseText;}
+this.timer=setTimeout(this.onTimerEvent.bind(this),this.decay*this.frequency*1000);},onTimerEvent:function(){this.updater=new Ajax.Updater(this.container,this.url,this.options);}});function $(element){if(arguments.length>1){for(var i=0,elements=[],length=arguments.length;i<length;i++)
+elements.push($(arguments[i]));return elements;}
+if(typeof element=='string')
+element=document.getElementById(element);return Element.extend(element);}
+if(Prototype.BrowserFeatures.XPath){document._getElementsByXPath=function(expression,parentElement){var results=[];var query=document.evaluate(expression,$(parentElement)||document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);for(var i=0,length=query.snapshotLength;i<length;i++)
+results.push(query.snapshotItem(i));return results;};document.getElementsByClassName=function(className,parentElement){var q=".//*[contains(concat(' ', @class, ' '), ' "+className+" ')]";return document._getElementsByXPath(q,parentElement);}}else document.getElementsByClassName=function(className,parentElement){var children=($(parentElement)||document.body).getElementsByTagName('*');var elements=[],child;for(var i=0,length=children.length;i<length;i++){child=children[i];if(Element.hasClassName(child,className))
+elements.push(Element.extend(child));}
+return elements;};if(!window.Element)var Element={};Element.extend=function(element){var F=Prototype.BrowserFeatures;if(!element||!element.tagName||element.nodeType==3||element._extended||F.SpecificElementExtensions||element==window)
+return element;var methods={},tagName=element.tagName,cache=Element.extend.cache,T=Element.Methods.ByTag;if(!F.ElementExtensions){Object.extend(methods,Element.Methods),Object.extend(methods,Element.Methods.Simulated);}
+if(T[tagName])Object.extend(methods,T[tagName]);for(var property in methods){var value=methods[property];if(typeof value=='function'&&!(property in element))
+element[property]=cache.findOrStore(value);}
+element._extended=Prototype.emptyFunction;return element;};Element.extend.cache={findOrStore:function(value){return this[value]=this[value]||function(){return value.apply(null,[this].concat($A(arguments)));}}};Element.Methods={visible:function(element){return $(element).style.display!='none';},toggle:function(element){element=$(element);Element[Element.visible(element)?'hide':'show'](element);return element;},hide:function(element){$(element).style.display='none';return element;},show:function(element){$(element).style.display='';return element;},remove:function(element){element=$(element);element.parentNode.removeChild(element);return element;},update:function(element,html){html=typeof html=='undefined'?'':html.toString();$(element).innerHTML=html.stripScripts();setTimeout(function(){html.evalScripts()},10);return element;},replace:function(element,html){element=$(element);html=typeof html=='undefined'?'':html.toString();if(element.outerHTML){element.outerHTML=html.stripScripts();}else{var range=element.ownerDocument.createRange();range.selectNodeContents(element);element.parentNode.replaceChild(range.createContextualFragment(html.stripScripts()),element);}
+setTimeout(function(){html.evalScripts()},10);return element;},inspect:function(element){element=$(element);var result='<'+element.tagName.toLowerCase();$H({'id':'id','className':'class'}).each(function(pair){var property=pair.first(),attribute=pair.last();var value=(element[property]||'').toString();if(value)result+=' '+attribute+'='+value.inspect(true);});return result+'>';},recursivelyCollect:function(element,property){element=$(element);var elements=[];while(element=element[property])
+if(element.nodeType==1)
+elements.push(Element.extend(element));return elements;},ancestors:function(element){return $(element).recursivelyCollect('parentNode');},descendants:function(element){return $A($(element).getElementsByTagName('*')).each(Element.extend);},immediateDescendants:function(element){if(!(element=$(element).firstChild))return[];while(element&&element.nodeType!=1)element=element.nextSibling;if(element)return[element].concat($(element).nextSiblings());return[];},previousSiblings:function(element){return $(element).recursivelyCollect('previousSibling');},nextSiblings:function(element){return $(element).recursivelyCollect('nextSibling');},siblings:function(element){element=$(element);return element.previousSiblings().reverse().concat(element.nextSiblings());},match:function(element,selector){if(typeof selector=='string')
+selector=new Selector(selector);return selector.match($(element));},up:function(element,expression,index){var ancestors=$(element).ancestors();return expression?Selector.findElement(ancestors,expression,index):ancestors[index||0];},down:function(element,expression,index){var descendants=$(element).descendants();return expression?Selector.findElement(descendants,expression,index):descendants[index||0];},previous:function(element,expression,index){var previousSiblings=$(element).previousSiblings();return expression?Selector.findElement(previousSiblings,expression,index):previousSiblings[index||0];},next:function(element,expression,index){var nextSiblings=$(element).nextSiblings();return expression?Selector.findElement(nextSiblings,expression,index):nextSiblings[index||0];},getElementsBySelector:function(){var args=$A(arguments),element=$(args.shift());return Selector.findChildElements(element,args);},getElementsByClassName:function(element,className){return document.getElementsByClassName(className,element);},readAttribute:function(element,name){element=$(element);if(Prototype.Browser.IE){if(!element.attributes)return null;var t=Element._attributeTranslations;if(t.values[name])return t.values[name](element,name);if(t.names[name])name=t.names[name];var attribute=element.attributes[name];return attribute?attribute.nodeValue:null;}
+return element.getAttribute(name);},getHeight:function(element){return $(element).getDimensions().height;},getWidth:function(element){return $(element).getDimensions().width;},classNames:function(element){return new Element.ClassNames(element);},hasClassName:function(element,className){if(!(element=$(element)))return;var elementClassName=element.className;if(elementClassName.length==0)return false;if(elementClassName==className||elementClassName.match(new RegExp("(^|\\s)"+className+"(\\s|$)")))
+return true;return false;},addClassName:function(element,className){if(!(element=$(element)))return;Element.classNames(element).add(className);return element;},removeClassName:function(element,className){if(!(element=$(element)))return;Element.classNames(element).remove(className);return element;},toggleClassName:function(element,className){if(!(element=$(element)))return;Element.classNames(element)[element.hasClassName(className)?'remove':'add'](className);return element;},observe:function(){Event.observe.apply(Event,arguments);return $A(arguments).first();},stopObserving:function(){Event.stopObserving.apply(Event,arguments);return $A(arguments).first();},cleanWhitespace:function(element){element=$(element);var node=element.firstChild;while(node){var nextNode=node.nextSibling;if(node.nodeType==3&&!/\S/.test(node.nodeValue))
+element.removeChild(node);node=nextNode;}
+return element;},empty:function(element){return $(element).innerHTML.blank();},descendantOf:function(element,ancestor){element=$(element),ancestor=$(ancestor);while(element=element.parentNode)
+if(element==ancestor)return true;return false;},scrollTo:function(element){element=$(element);var pos=Position.cumulativeOffset(element);window.scrollTo(pos[0],pos[1]);return element;},getStyle:function(element,style){element=$(element);style=style=='float'?'cssFloat':style.camelize();var value=element.style[style];if(!value){var css=document.defaultView.getComputedStyle(element,null);value=css?css[style]:null;}
+if(style=='opacity')return value?parseFloat(value):1.0;return value=='auto'?null:value;},getOpacity:function(element){return $(element).getStyle('opacity');},setStyle:function(element,styles,camelized){element=$(element);var elementStyle=element.style;for(var property in styles)
+if(property=='opacity')element.setOpacity(styles[property])
+else
+elementStyle[(property=='float'||property=='cssFloat')?(elementStyle.styleFloat===undefined?'cssFloat':'styleFloat'):(camelized?property:property.camelize())]=styles[property];return element;},setOpacity:function(element,value){element=$(element);element.style.opacity=(value==1||value==='')?'':(value<0.00001)?0:value;return element;},getDimensions:function(element){element=$(element);var display=$(element).getStyle('display');if(display!='none'&&display!=null)
+return{width:element.offsetWidth,height:element.offsetHeight};var els=element.style;var originalVisibility=els.visibility;var originalPosition=els.position;var originalDisplay=els.display;els.visibility='hidden';els.position='absolute';els.display='block';var originalWidth=element.clientWidth;var originalHeight=element.clientHeight;els.display=originalDisplay;els.position=originalPosition;els.visibility=originalVisibility;return{width:originalWidth,height:originalHeight};},makePositioned:function(element){element=$(element);var pos=Element.getStyle(element,'position');if(pos=='static'||!pos){element._madePositioned=true;element.style.position='relative';if(window.opera){element.style.top=0;element.style.left=0;}}
+return element;},undoPositioned:function(element){element=$(element);if(element._madePositioned){element._madePositioned=undefined;element.style.position=element.style.top=element.style.left=element.style.bottom=element.style.right='';}
+return element;},makeClipping:function(element){element=$(element);if(element._overflow)return element;element._overflow=element.style.overflow||'auto';if((Element.getStyle(element,'overflow')||'visible')!='hidden')
+element.style.overflow='hidden';return element;},undoClipping:function(element){element=$(element);if(!element._overflow)return element;element.style.overflow=element._overflow=='auto'?'':element._overflow;element._overflow=null;return element;}};Object.extend(Element.Methods,{childOf:Element.Methods.descendantOf});if(Prototype.Browser.Opera){Element.Methods._getStyle=Element.Methods.getStyle;Element.Methods.getStyle=function(element,style){switch(style){case'left':case'top':case'right':case'bottom':if(Element._getStyle(element,'position')=='static')return null;default:return Element._getStyle(element,style);}};}
+else if(Prototype.Browser.IE){Element.Methods.getStyle=function(element,style){element=$(element);style=(style=='float'||style=='cssFloat')?'styleFloat':style.camelize();var value=element.style[style];if(!value&&element.currentStyle)value=element.currentStyle[style];if(style=='opacity'){if(value=(element.getStyle('filter')||'').match(/alpha\(opacity=(.*)\)/))
+if(value[1])return parseFloat(value[1])/100;return 1.0;}
+if(value=='auto'){if((style=='width'||style=='height')&&(element.getStyle('display')!='none'))
+return element['offset'+style.capitalize()]+'px';return null;}
+return value;};Element.Methods.setOpacity=function(element,value){element=$(element);var filter=element.getStyle('filter'),style=element.style;if(value==1||value===''){style.filter=filter.replace(/alpha\([^\)]*\)/gi,'');return element;}else if(value<0.00001)value=0;style.filter=filter.replace(/alpha\([^\)]*\)/gi,'')+'alpha(opacity='+(value*100)+')';return element;};Element.Methods.update=function(element,html){element=$(element);html=typeof html=='undefined'?'':html.toString();var tagName=element.tagName.toUpperCase();if(['THEAD','TBODY','TR','TD'].include(tagName)){var div=document.createElement('div');switch(tagName){case'THEAD':case'TBODY':div.innerHTML='<table><tbody>'+html.stripScripts()+'</tbody></table>';depth=2;break;case'TR':div.innerHTML='<table><tbody><tr>'+html.stripScripts()+'</tr></tbody></table>';depth=3;break;case'TD':div.innerHTML='<table><tbody><tr><td>'+html.stripScripts()+'</td></tr></tbody></table>';depth=4;}
+$A(element.childNodes).each(function(node){element.removeChild(node)});depth.times(function(){div=div.firstChild});$A(div.childNodes).each(function(node){element.appendChild(node)});}else{element.innerHTML=html.stripScripts();}
+setTimeout(function(){html.evalScripts()},10);return element;}}
+else if(Prototype.Browser.Gecko){Element.Methods.setOpacity=function(element,value){element=$(element);element.style.opacity=(value==1)?0.999999:(value==='')?'':(value<0.00001)?0:value;return element;};}
+Element._attributeTranslations={names:{colspan:"colSpan",rowspan:"rowSpan",valign:"vAlign",datetime:"dateTime",accesskey:"accessKey",tabindex:"tabIndex",enctype:"encType",maxlength:"maxLength",readonly:"readOnly",longdesc:"longDesc"},values:{_getAttr:function(element,attribute){return element.getAttribute(attribute,2);},_flag:function(element,attribute){return $(element).hasAttribute(attribute)?attribute:null;},style:function(element){return element.style.cssText.toLowerCase();},title:function(element){var node=element.getAttributeNode('title');return node.specified?node.nodeValue:null;}}};(function(){Object.extend(this,{href:this._getAttr,src:this._getAttr,disabled:this._flag,checked:this._flag,readonly:this._flag,multiple:this._flag});}).call(Element._attributeTranslations.values);Element.Methods.Simulated={hasAttribute:function(element,attribute){var t=Element._attributeTranslations,node;attribute=t.names[attribute]||attribute;node=$(element).getAttributeNode(attribute);return node&&node.specified;}};Element.Methods.ByTag={};Object.extend(Element,Element.Methods);if(!Prototype.BrowserFeatures.ElementExtensions&&document.createElement('div').__proto__){window.HTMLElement={};window.HTMLElement.prototype=document.createElement('div').__proto__;Prototype.BrowserFeatures.ElementExtensions=true;}
+Element.hasAttribute=function(element,attribute){if(element.hasAttribute)return element.hasAttribute(attribute);return Element.Methods.Simulated.hasAttribute(element,attribute);};Element.addMethods=function(methods){var F=Prototype.BrowserFeatures,T=Element.Methods.ByTag;if(arguments.length==2){var tagName=methods;methods=arguments[1];}
+if(!tagName)Object.extend(Element.Methods,methods||{});else{if(tagName.constructor==Array)tagName.each(extend);else extend(tagName);}
+function extend(tagName){tagName=tagName.toUpperCase();if(!Element.Methods.ByTag[tagName])
+Element.Methods.ByTag[tagName]={};Object.extend(Element.Methods.ByTag[tagName],methods);}
+function copy(methods,destination,onlyIfAbsent){onlyIfAbsent=onlyIfAbsent||false;var cache=Element.extend.cache;for(var property in methods){var value=methods[property];if(!onlyIfAbsent||!(property in destination))
+destination[property]=cache.findOrStore(value);}}
+function findDOMClass(tagName){var klass;var trans={"OPTGROUP":"OptGroup","TEXTAREA":"TextArea","P":"Paragraph","FIELDSET":"FieldSet","UL":"UList","OL":"OList","DL":"DList","DIR":"Directory","H1":"Heading","H2":"Heading","H3":"Heading","H4":"Heading","H5":"Heading","H6":"Heading","Q":"Quote","INS":"Mod","DEL":"Mod","A":"Anchor","IMG":"Image","CAPTION":"TableCaption","COL":"TableCol","COLGROUP":"TableCol","THEAD":"TableSection","TFOOT":"TableSection","TBODY":"TableSection","TR":"TableRow","TH":"TableCell","TD":"TableCell","FRAMESET":"FrameSet","IFRAME":"IFrame"};if(trans[tagName])klass='HTML'+trans[tagName]+'Element';if(window[klass])return window[klass];klass='HTML'+tagName+'Element';if(window[klass])return window[klass];klass='HTML'+tagName.capitalize()+'Element';if(window[klass])return window[klass];window[klass]={};window[klass].prototype=document.createElement(tagName).__proto__;return window[klass];}
+if(F.ElementExtensions){copy(Element.Methods,HTMLElement.prototype);copy(Element.Methods.Simulated,HTMLElement.prototype,true);}
+if(F.SpecificElementExtensions){for(var tag in Element.Methods.ByTag){var klass=findDOMClass(tag);if(typeof klass=="undefined")continue;copy(T[tag],klass.prototype);}}};var Toggle={display:Element.toggle};Abstract.Insertion=function(adjacency){this.adjacency=adjacency;}
+Abstract.Insertion.prototype={initialize:function(element,content){this.element=$(element);this.content=content.stripScripts();if(this.adjacency&&this.element.insertAdjacentHTML){try{this.element.insertAdjacentHTML(this.adjacency,this.content);}catch(e){var tagName=this.element.tagName.toUpperCase();if(['TBODY','TR'].include(tagName)){this.insertContent(this.contentFromAnonymousTable());}else{throw e;}}}else{this.range=this.element.ownerDocument.createRange();if(this.initializeRange)this.initializeRange();this.insertContent([this.range.createContextualFragment(this.content)]);}
+setTimeout(function(){content.evalScripts()},10);},contentFromAnonymousTable:function(){var div=document.createElement('div');div.innerHTML='<table><tbody>'+this.content+'</tbody></table>';return $A(div.childNodes[0].childNodes[0].childNodes);}}
+var Insertion=new Object();Insertion.Before=Class.create();Insertion.Before.prototype=Object.extend(new Abstract.Insertion('beforeBegin'),{initializeRange:function(){this.range.setStartBefore(this.element);},insertContent:function(fragments){fragments.each((function(fragment){this.element.parentNode.insertBefore(fragment,this.element);}).bind(this));}});Insertion.Top=Class.create();Insertion.Top.prototype=Object.extend(new Abstract.Insertion('afterBegin'),{initializeRange:function(){this.range.selectNodeContents(this.element);this.range.collapse(true);},insertContent:function(fragments){fragments.reverse(false).each((function(fragment){this.element.insertBefore(fragment,this.element.firstChild);}).bind(this));}});Insertion.Bottom=Class.create();Insertion.Bottom.prototype=Object.extend(new Abstract.Insertion('beforeEnd'),{initializeRange:function(){this.range.selectNodeContents(this.element);this.range.collapse(this.element);},insertContent:function(fragments){fragments.each((function(fragment){this.element.appendChild(fragment);}).bind(this));}});Insertion.After=Class.create();Insertion.After.prototype=Object.extend(new Abstract.Insertion('afterEnd'),{initializeRange:function(){this.range.setStartAfter(this.element);},insertContent:function(fragments){fragments.each((function(fragment){this.element.parentNode.insertBefore(fragment,this.element.nextSibling);}).bind(this));}});Element.ClassNames=Class.create();Element.ClassNames.prototype={initialize:function(element){this.element=$(element);},_each:function(iterator){this.element.className.split(/\s+/).select(function(name){return name.length>0;})._each(iterator);},set:function(className){this.element.className=className;},add:function(classNameToAdd){if(this.include(classNameToAdd))return;this.set($A(this).concat(classNameToAdd).join(' '));},remove:function(classNameToRemove){if(!this.include(classNameToRemove))return;this.set($A(this).without(classNameToRemove).join(' '));},toString:function(){return $A(this).join(' ');}};Object.extend(Element.ClassNames.prototype,Enumerable);var Selector=Class.create();Selector.prototype={initialize:function(expression){this.expression=expression.strip();this.compileMatcher();},compileMatcher:function(){if(Prototype.BrowserFeatures.XPath&&!(/\[[\w-]*?:/).test(this.expression))
+return this.compileXPathMatcher();var e=this.expression,ps=Selector.patterns,h=Selector.handlers,c=Selector.criteria,le,p,m;if(Selector._cache[e]){this.matcher=Selector._cache[e];return;}
+this.matcher=["this.matcher = function(root) {","var r = root, h = Selector.handlers, c = false, n;"];while(e&&le!=e&&(/\S/).test(e)){le=e;for(var i in ps){p=ps[i];if(m=e.match(p)){this.matcher.push(typeof c[i]=='function'?c[i](m):new Template(c[i]).evaluate(m));e=e.replace(m[0],'');break;}}}
+this.matcher.push("return h.unique(n);\n}");eval(this.matcher.join('\n'));Selector._cache[this.expression]=this.matcher;},compileXPathMatcher:function(){var e=this.expression,ps=Selector.patterns,x=Selector.xpath,le,p,m;if(Selector._cache[e]){this.xpath=Selector._cache[e];return;}
+this.matcher=['.//*'];while(e&&le!=e&&(/\S/).test(e)){le=e;for(var i in ps){if(m=e.match(ps[i])){this.matcher.push(typeof x[i]=='function'?x[i](m):new Template(x[i]).evaluate(m));e=e.replace(m[0],'');break;}}}
+this.xpath=this.matcher.join('');Selector._cache[this.expression]=this.xpath;},findElements:function(root){root=root||document;if(this.xpath)return document._getElementsByXPath(this.xpath,root);return this.matcher(root);},match:function(element){return this.findElements(document).include(element);},toString:function(){return this.expression;},inspect:function(){return"#<Selector:"+this.expression.inspect()+">";}};Object.extend(Selector,{_cache:{},xpath:{descendant:"//*",child:"/*",adjacent:"/following-sibling::*[1]",laterSibling:'/following-sibling::*',tagName:function(m){if(m[1]=='*')return'';return"[local-name()='"+m[1].toLowerCase()+"' or local-name()='"+m[1].toUpperCase()+"']";},className:"[contains(concat(' ', @class, ' '), ' #{1} ')]",id:"[@id='#{1}']",attrPresence:"[@#{1}]",attr:function(m){m[3]=m[5]||m[6];return new Template(Selector.xpath.operators[m[2]]).evaluate(m);},pseudo:function(m){var h=Selector.xpath.pseudos[m[1]];if(!h)return'';if(typeof h==='function')return h(m);return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);},operators:{'=':"[@#{1}='#{3}']",'!=':"[@#{1}!='#{3}']",'^=':"[starts-with(@#{1}, '#{3}')]",'$=':"[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",'*=':"[contains(@#{1}, '#{3}')]",'~=':"[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",'|=':"[contains(concat('-', @#{1}, '-'), '-#{3}-')]"},pseudos:{'first-child':'[not(preceding-sibling::*)]','last-child':'[not(following-sibling::*)]','only-child':'[not(preceding-sibling::* or following-sibling::*)]','empty':"[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",'checked':"[@checked]",'disabled':"[@disabled]",'enabled':"[not(@disabled)]",'not':function(m){if(!m[6])return'';var p=Selector.patterns,x=Selector.xpath;for(var i in p){if(mm=m[6].match(p[i])){var ss=typeof x[i]=='function'?x[i](mm):new Template(x[i]).evaluate(mm);m[6]=ss.substring(1,ss.length-1);break;}}
+return"[not("+m[6]+")]";},'nth-child':function(m){return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ",m);},'nth-last-child':function(m){return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ",m);},'nth-of-type':function(m){return Selector.xpath.pseudos.nth("position() ",m);},'nth-last-of-type':function(m){return Selector.xpath.pseudos.nth("(last() + 1 - position()) ",m);},'first-of-type':function(m){m[6]="1";return Selector.xpath.pseudos['nth-of-type'](m);},'last-of-type':function(m){m[6]="1";return Selector.xpath.pseudos['nth-last-of-type'](m);},'only-of-type':function(m){var p=Selector.xpath.pseudos;return p['first-of-type'](m)+p['last-of-type'](m);},nth:function(predicate,m){var mm,formula=m[6];if(formula=='even')formula='2n+0';if(formula=='odd')formula='2n+1';if(mm=formula.match(/^(\d+)$/))
+predicate+="= "+mm[1];if(mm=formula.match(/^(\d+)?n(\+(\d+))?/)){var a=mm[1]?Number(mm[1]):1;var b=mm[3]?Number(mm[3]):0;predicate+="mod "+a+" = "+b;}
+return"["+predicate+"]";}}},criteria:{tagName:'n = h.tagName(n, r, "#{1}", c); c = false;',className:'n = h.className(n, r, "#{1}", c); c = false;',id:'n = h.id(n, r, "#{1}", c); c = false;',attrPresence:'n = h.attrPresence(n, r, "#{1}"); c = false;',attr:function(m){m[3]=m[5]||m[6];return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m);},pseudo:'n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;',descendant:'c = "descendant";',child:'c = "child";',adjacent:'c = "adjacent";',laterSibling:'c = "laterSibling";'},patterns:{laterSibling:/^\s*~\s*/,child:/^\s*>\s*/,adjacent:/^\s*\+\s*/,descendant:/^\s/,tagName:/^\s*(\*|[\w\-]+)(\b|$)?/,id:/^#([\w\-\*]+)(\b|$)/,className:/^\.([\w\-\*]+)(\b|$)/,pseudo:/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$)/,attrPresence:/^\[([\w]+)\]/,attr:new RegExp(/\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\]]*?)\4|([^'"][^\]]*?)))?\]/)},handlers:{concat:function(a,b){for(var i=0,node;node=b[i];i++)
+a.push(node);return a;},mark:function(nodes){for(var i=0,node;node=nodes[i];i++)
+node._counted=true;return nodes;},unmark:function(nodes){for(var i=0,node;node=nodes[i];i++)
+node._counted=undefined;return nodes;},index:function(parentNode,reverse,ofType){parentNode._counted=true;if(reverse){for(var nodes=parentNode.childNodes,i=nodes.length-1,j=1;i>=0;i--){node=nodes[i];if(node.nodeType==1&&(!ofType||node._counted))node.nodeIndex=j++;}}else{for(var i=0,j=1,nodes=parentNode.childNodes;node=nodes[i];i++)
+if(node.nodeType==1&&(!ofType||node._counted))node.nodeIndex=j++;}},unique:function(nodes){if(nodes.length==0)return nodes;var results=[],n;for(var i=0,l=nodes.length;i<l;i++)
+if(!(n=nodes[i])._counted){n._counted=true;results.push(Element.extend(n));}
+return Selector.handlers.unmark(results);},descendant:function(nodes){var h=Selector.handlers;for(var i=0,results=[],node;node=nodes[i];i++)
+h.concat(results,Element.descendants(node));return results;},child:function(nodes){var h=Selector.handlers;for(var i=0,results=[],node;node=nodes[i];i++)
+h.concat(results,Element.immediateDescendants(node));return results;},adjacent:function(nodes){for(var i=0,results=[],node;node=nodes[i];i++){var next=this.nextElementSibling(node);if(next)results.push(next);}
+return results;},laterSibling:function(nodes){var h=Selector.handlers;for(var i=0,results=[],node;node=nodes[i];i++)
+h.concat(results,Element.nextSiblings(node));return results;},nextElementSibling:function(node){while(node=node.nextSibling)
+if(node.nodeType==1)return node;return null;},previousElementSibling:function(node){while(node=node.previousSibling)
+if(node.nodeType==1)return node;return null;},tagName:function(nodes,root,tagName,combinator){tagName=tagName.toUpperCase();var results=[],h=Selector.handlers;if(nodes){if(combinator){if(combinator=="descendant"){for(var i=0,node;node=nodes[i];i++)
+h.concat(results,node.getElementsByTagName(tagName));return results;}else nodes=this[combinator](nodes);if(tagName=="*")return nodes;}
+for(var i=0,node;node=nodes[i];i++)
+if(node.tagName.toUpperCase()==tagName)results.push(node);return results;}else return root.getElementsByTagName(tagName);},id:function(nodes,root,id,combinator){var targetNode=$(id),h=Selector.handlers;if(!nodes&&root==document)return targetNode?[targetNode]:[];if(nodes){if(combinator){if(combinator=='child'){for(var i=0,node;node=nodes[i];i++)
+if(targetNode.parentNode==node)return[targetNode];}else if(combinator=='descendant'){for(var i=0,node;node=nodes[i];i++)
+if(Element.descendantOf(targetNode,node))return[targetNode];}else if(combinator=='adjacent'){for(var i=0,node;node=nodes[i];i++)
+if(Selector.handlers.previousElementSibling(targetNode)==node)
+return[targetNode];}else nodes=h[combinator](nodes);}
+for(var i=0,node;node=nodes[i];i++)
+if(node==targetNode)return[targetNode];return[];}
+return(targetNode&&Element.descendantOf(targetNode,root))?[targetNode]:[];},className:function(nodes,root,className,combinator){if(nodes&&combinator)nodes=this[combinator](nodes);return Selector.handlers.byClassName(nodes,root,className);},byClassName:function(nodes,root,className){if(!nodes)nodes=Selector.handlers.descendant([root]);var needle=' '+className+' ';for(var i=0,results=[],node,nodeClassName;node=nodes[i];i++){nodeClassName=node.className;if(nodeClassName.length==0)continue;if(nodeClassName==className||(' '+nodeClassName+' ').include(needle))
+results.push(node);}
+return results;},attrPresence:function(nodes,root,attr){var results=[];for(var i=0,node;node=nodes[i];i++)
+if(Element.hasAttribute(node,attr))results.push(node);return results;},attr:function(nodes,root,attr,value,operator){var handler=Selector.operators[operator],results=[];for(var i=0,node;node=nodes[i];i++){var nodeValue=Element.readAttribute(node,attr);if(nodeValue===null)continue;if(handler(nodeValue,value))results.push(node);}
+return results;},pseudo:function(nodes,name,value,root,combinator){if(combinator)nodes=this[combinator](nodes);return Selector.pseudos[name](nodes,value,root);}},pseudos:{'first-child':function(nodes,value,root){for(var i=0,results=[],node;node=nodes[i];i++){if(Selector.handlers.previousElementSibling(node))continue;results.push(node);}
+return results;},'last-child':function(nodes,value,root){for(var i=0,results=[],node;node=nodes[i];i++){if(Selector.handlers.nextElementSibling(node))continue;results.push(node);}
+return results;},'only-child':function(nodes,value,root){var h=Selector.handlers;for(var i=0,results=[],node;node=nodes[i];i++)
+if(!h.previousElementSibling(node)&&!h.nextElementSibling(node))
+results.push(node);return results;},'nth-child':function(nodes,formula,root){return Selector.pseudos.nth(nodes,formula,root);},'nth-last-child':function(nodes,formula,root){return Selector.pseudos.nth(nodes,formula,root,true);},'nth-of-type':function(nodes,formula,root){return Selector.pseudos.nth(nodes,formula,root,false,true);},'nth-last-of-type':function(nodes,formula,root){return Selector.pseudos.nth(nodes,formula,root,true,true);},'first-of-type':function(nodes,formula,root){return Selector.pseudos.nth(nodes,"1",root,false,true);},'last-of-type':function(nodes,formula,root){return Selector.pseudos.nth(nodes,"1",root,true,true);},'only-of-type':function(nodes,formula,root){var p=Selector.pseudos;return p['last-of-type'](p['first-of-type'](nodes,formula,root),formula,root);},nth:function(nodes,formula,root,reverse,ofType){if(formula=='even')formula='2n+0';if(formula=='odd')formula='2n+1';var h=Selector.handlers,results=[],indexed=[],m;h.mark(nodes);for(var i=0,node;node=nodes[i];i++){if(!node.parentNode._counted){h.index(node.parentNode,reverse,ofType);indexed.push(node.parentNode);}}
+if(formula.match(/^\d+$/)){formula=Number(formula);for(var i=0,node;node=nodes[i];i++)
+if(node.nodeIndex==formula)results.push(node);}else if(m=formula.match(/^(\d+)?n(\+(\d+))?$/)){var a=m[1]?Number(m[1]):1;var b=m[3]?Number(m[3]):0;for(var i=0,node;node=nodes[i];i++)
+if(node.nodeIndex%a==b)results.push(node);}
+h.unmark(nodes);h.unmark(indexed);return results;},'empty':function(nodes,value,root){for(var i=0,results=[],node;node=nodes[i];i++){if(node.tagName=='!'||(node.firstChild&&!node.innerHTML.match(/^\s*$/)))continue;results.push(node);}
+return results;},'not':function(nodes,selector,root){var h=Selector.handlers,exclusions=$A(nodes),selectorType,m;for(var i in Selector.patterns){if(m=selector.match(Selector.patterns[i])){selectorType=i;break;}}
+switch(selectorType){case'className':case'tagName':case'id':case'attrPresence':exclusions=h[selectorType](exclusions,root,m[1],false);break;case'attr':m[3]=m[5]||m[6];exclusions=h.attr(exclusions,root,m[1],m[3],m[2]);break;case'pseudo':exclusions=h.pseudo(exclusions,m[1],m[6],root,false);break;default:throw'Illegal selector in :not clause.';}
+h.mark(exclusions);for(var i=0,results=[],node;node=nodes[i];i++)
+if(!node._counted)results.push(node);h.unmark(exclusions);return results;},'enabled':function(nodes,value,root){for(var i=0,results=[],node;node=nodes[i];i++)
+if(!node.disabled)results.push(node);return results;},'disabled':function(nodes,value,root){for(var i=0,results=[],node;node=nodes[i];i++)
+if(node.disabled)results.push(node);return results;},'checked':function(nodes,value,root){for(var i=0,results=[],node;node=nodes[i];i++)
+if(node.checked)results.push(node);return results;}},operators:{'=':function(nv,v){return nv==v;},'!=':function(nv,v){return nv!=v;},'^=':function(nv,v){return nv.startsWith(v);},'$=':function(nv,v){return nv.endsWith(v);},'*=':function(nv,v){return nv.include(v);},'~=':function(nv,v){return(' '+nv+' ').include(' '+v+' ');},'|=':function(nv,v){return('-'+nv.toUpperCase()+'-').include('-'+v.toUpperCase()+'-');}},matchElements:function(elements,expression){var matches=new Selector(expression).findElements(),h=Selector.handlers;h.mark(matches);for(var i=0,results=[],element;element=elements[i];i++)
+if(element._counted)results.push(element);h.unmark(matches);return results;},findElement:function(elements,expression,index){if(typeof expression=='number'){index=expression;expression=false;}
+return Selector.matchElements(elements,expression||'*')[index||0];},findChildElements:function(element,expressions){var exprs=expressions.join(','),expressions=[];exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/,function(m){expressions.push(m[1].strip());});var results=[],h=Selector.handlers;for(var i=0,l=expressions.length,selector;i<l;i++){selector=new Selector(expressions[i].strip());h.concat(results,selector.findElements(element));}
+return(l>1)?h.unique(results):results;}});function $$(){return Selector.findChildElements(document,$A(arguments));}
+var Form={reset:function(form){$(form).reset();return form;},serializeElements:function(elements,getHash){var data=elements.inject({},function(result,element){if(!element.disabled&&element.name){var key=element.name,value=$(element).getValue();if(value!=null){if(key in result){if(result[key].constructor!=Array)result[key]=[result[key]];result[key].push(value);}
+else result[key]=value;}}
+return result;});return getHash?data:Hash.toQueryString(data);}};Form.Methods={serialize:function(form,getHash){return Form.serializeElements(Form.getElements(form),getHash);},getElements:function(form){return $A($(form).getElementsByTagName('*')).inject([],function(elements,child){if(Form.Element.Serializers[child.tagName.toLowerCase()])
+elements.push(Element.extend(child));return elements;});},getInputs:function(form,typeName,name){form=$(form);var inputs=form.getElementsByTagName('input');if(!typeName&&!name)return $A(inputs).map(Element.extend);for(var i=0,matchingInputs=[],length=inputs.length;i<length;i++){var input=inputs[i];if((typeName&&input.type!=typeName)||(name&&input.name!=name))
+continue;matchingInputs.push(Element.extend(input));}
+return matchingInputs;},disable:function(form){form=$(form);form.getElements().each(function(element){element.blur();element.disabled='true';});return form;},enable:function(form){form=$(form);form.getElements().each(function(element){element.disabled='';});return form;},findFirstElement:function(form){return $(form).getElements().find(function(element){return element.type!='hidden'&&!element.disabled&&['input','select','textarea'].include(element.tagName.toLowerCase());});},focusFirstElement:function(form){form=$(form);form.findFirstElement().activate();return form;},request:function(form,options){form=$(form),options=Object.clone(options||{});var params=options.parameters;options.parameters=form.serialize(true);if(params){if(typeof params=='string')params=params.toQueryParams();Object.extend(options.parameters,params);}
+if(form.hasAttribute('method')&&!options.method)
+options.method=form.method;return new Ajax.Request(form.action,options);}}
+Object.extend(Form,Form.Methods);Form.Element={focus:function(element){$(element).focus();return element;},select:function(element){$(element).select();return element;}}
+Form.Element.Methods={serialize:function(element){element=$(element);if(!element.disabled&&element.name){var value=element.getValue();if(value!=undefined){var pair={};pair[element.name]=value;return Hash.toQueryString(pair);}}
+return'';},getValue:function(element){element=$(element);var method=element.tagName.toLowerCase();return Form.Element.Serializers[method](element);},clear:function(element){$(element).value='';return element;},present:function(element){return $(element).value!='';},activate:function(element){element=$(element);try{element.focus();if(element.select&&(element.tagName.toLowerCase()!='input'||!['button','reset','submit'].include(element.type)))
+element.select();}catch(e){}
+return element;},disable:function(element){element=$(element);element.blur();element.disabled=true;return element;},enable:function(element){element=$(element);element.disabled=false;return element;}}
+Object.extend(Form.Element,Form.Element.Methods);Object.extend(Element.Methods.ByTag,{"FORM":Object.clone(Form.Methods),"INPUT":Object.clone(Form.Element.Methods),"SELECT":Object.clone(Form.Element.Methods),"TEXTAREA":Object.clone(Form.Element.Methods)});var Field=Form.Element;var $F=Form.Element.getValue;Form.Element.Serializers={input:function(element){switch(element.type.toLowerCase()){case'checkbox':case'radio':return Form.Element.Serializers.inputSelector(element);default:return Form.Element.Serializers.textarea(element);}},inputSelector:function(element){return element.checked?element.value:null;},textarea:function(element){return element.value;},select:function(element){return this[element.type=='select-one'?'selectOne':'selectMany'](element);},selectOne:function(element){var index=element.selectedIndex;return index>=0?this.optionValue(element.options[index]):null;},selectMany:function(element){var values,length=element.length;if(!length)return null;for(var i=0,values=[];i<length;i++){var opt=element.options[i];if(opt.selected)values.push(this.optionValue(opt));}
+return values;},optionValue:function(opt){return Element.extend(opt).hasAttribute('value')?opt.value:opt.text;}}
+Abstract.TimedObserver=function(){}
+Abstract.TimedObserver.prototype={initialize:function(element,frequency,callback){this.frequency=frequency;this.element=$(element);this.callback=callback;this.lastValue=this.getValue();this.registerCallback();},registerCallback:function(){setInterval(this.onTimerEvent.bind(this),this.frequency*1000);},onTimerEvent:function(){var value=this.getValue();var changed=('string'==typeof this.lastValue&&'string'==typeof value?this.lastValue!=value:String(this.lastValue)!=String(value));if(changed){this.callback(this.element,value);this.lastValue=value;}}}
+Form.Element.Observer=Class.create();Form.Element.Observer.prototype=Object.extend(new Abstract.TimedObserver(),{getValue:function(){return Form.Element.getValue(this.element);}});Form.Observer=Class.create();Form.Observer.prototype=Object.extend(new Abstract.TimedObserver(),{getValue:function(){return Form.serialize(this.element);}});Abstract.EventObserver=function(){}
+Abstract.EventObserver.prototype={initialize:function(element,callback){this.element=$(element);this.callback=callback;this.lastValue=this.getValue();if(this.element.tagName.toLowerCase()=='form')
+this.registerFormCallbacks();else
+this.registerCallback(this.element);},onElementEvent:function(){var value=this.getValue();if(this.lastValue!=value){this.callback(this.element,value);this.lastValue=value;}},registerFormCallbacks:function(){Form.getElements(this.element).each(this.registerCallback.bind(this));},registerCallback:function(element){if(element.type){switch(element.type.toLowerCase()){case'checkbox':case'radio':Event.observe(element,'click',this.onElementEvent.bind(this));break;default:Event.observe(element,'change',this.onElementEvent.bind(this));break;}}}}
+Form.Element.EventObserver=Class.create();Form.Element.EventObserver.prototype=Object.extend(new Abstract.EventObserver(),{getValue:function(){return Form.Element.getValue(this.element);}});Form.EventObserver=Class.create();Form.EventObserver.prototype=Object.extend(new Abstract.EventObserver(),{getValue:function(){return Form.serialize(this.element);}});if(!window.Event){var Event=new Object();}
+Object.extend(Event,{KEY_BACKSPACE:8,KEY_TAB:9,KEY_RETURN:13,KEY_ESC:27,KEY_LEFT:37,KEY_UP:38,KEY_RIGHT:39,KEY_DOWN:40,KEY_DELETE:46,KEY_HOME:36,KEY_END:35,KEY_PAGEUP:33,KEY_PAGEDOWN:34,element:function(event){return event.target||event.srcElement;},isLeftClick:function(event){return(((event.which)&&(event.which==1))||((event.button)&&(event.button==1)));},pointerX:function(event){return event.pageX||(event.clientX+
+(document.documentElement.scrollLeft||document.body.scrollLeft));},pointerY:function(event){return event.pageY||(event.clientY+
+(document.documentElement.scrollTop||document.body.scrollTop));},stop:function(event){if(event.preventDefault){event.preventDefault();event.stopPropagation();}else{event.returnValue=false;event.cancelBubble=true;}},findElement:function(event,tagName){var element=Event.element(event);while(element.parentNode&&(!element.tagName||(element.tagName.toUpperCase()!=tagName.toUpperCase())))
+element=element.parentNode;return element;},observers:false,_observeAndCache:function(element,name,observer,useCapture){if(!this.observers)this.observers=[];if(element.addEventListener){this.observers.push([element,name,observer,useCapture]);element.addEventListener(name,observer,useCapture);}else if(element.attachEvent){this.observers.push([element,name,observer,useCapture]);element.attachEvent('on'+name,observer);}},unloadCache:function(){if(!Event.observers)return;for(var i=0,length=Event.observers.length;i<length;i++){Event.stopObserving.apply(this,Event.observers[i]);Event.observers[i][0]=null;}
+Event.observers=false;},observe:function(element,name,observer,useCapture){element=$(element);useCapture=useCapture||false;if(name=='keypress'&&(Prototype.Browser.WebKit||element.attachEvent))
+name='keydown';Event._observeAndCache(element,name,observer,useCapture);},stopObserving:function(element,name,observer,useCapture){element=$(element);useCapture=useCapture||false;if(name=='keypress'&&(Prototype.Browser.WebKit||element.attachEvent))
+name='keydown';if(element.removeEventListener){element.removeEventListener(name,observer,useCapture);}else if(element.detachEvent){try{element.detachEvent('on'+name,observer);}catch(e){}}}});if(Prototype.Browser.IE)
+Event.observe(window,'unload',Event.unloadCache,false);var Position={includeScrollOffsets:false,prepare:function(){this.deltaX=window.pageXOffset||document.documentElement.scrollLeft||document.body.scrollLeft||0;this.deltaY=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0;},realOffset:function(element){var valueT=0,valueL=0;do{valueT+=element.scrollTop||0;valueL+=element.scrollLeft||0;element=element.parentNode;}while(element);return[valueL,valueT];},cumulativeOffset:function(element){var valueT=0,valueL=0;do{valueT+=element.offsetTop||0;valueL+=element.offsetLeft||0;element=element.offsetParent;}while(element);return[valueL,valueT];},positionedOffset:function(element){var valueT=0,valueL=0;do{valueT+=element.offsetTop||0;valueL+=element.offsetLeft||0;element=element.offsetParent;if(element){if(element.tagName=='BODY')break;var p=Element.getStyle(element,'position');if(p=='relative'||p=='absolute')break;}}while(element);return[valueL,valueT];},offsetParent:function(element){if(element.offsetParent)return element.offsetParent;if(element==document.body)return element;while((element=element.parentNode)&&element!=document.body)
+if(Element.getStyle(element,'position')!='static')
+return element;return document.body;},within:function(element,x,y){if(this.includeScrollOffsets)
+return this.withinIncludingScrolloffsets(element,x,y);this.xcomp=x;this.ycomp=y;this.offset=this.cumulativeOffset(element);return(y>=this.offset[1]&&y<this.offset[1]+element.offsetHeight&&x>=this.offset[0]&&x<this.offset[0]+element.offsetWidth);},withinIncludingScrolloffsets:function(element,x,y){var offsetcache=this.realOffset(element);this.xcomp=x+offsetcache[0]-this.deltaX;this.ycomp=y+offsetcache[1]-this.deltaY;this.offset=this.cumulativeOffset(element);return(this.ycomp>=this.offset[1]&&this.ycomp<this.offset[1]+element.offsetHeight&&this.xcomp>=this.offset[0]&&this.xcomp<this.offset[0]+element.offsetWidth);},overlap:function(mode,element){if(!mode)return 0;if(mode=='vertical')
+return((this.offset[1]+element.offsetHeight)-this.ycomp)/element.offsetHeight;if(mode=='horizontal')
+return((this.offset[0]+element.offsetWidth)-this.xcomp)/element.offsetWidth;},page:function(forElement){var valueT=0,valueL=0;var element=forElement;do{valueT+=element.offsetTop||0;valueL+=element.offsetLeft||0;if(element.offsetParent==document.body)
+if(Element.getStyle(element,'position')=='absolute')break;}while(element=element.offsetParent);element=forElement;do{if(!window.opera||element.tagName=='BODY'){valueT-=element.scrollTop||0;valueL-=element.scrollLeft||0;}}while(element=element.parentNode);return[valueL,valueT];},clone:function(source,target){var options=Object.extend({setLeft:true,setTop:true,setWidth:true,setHeight:true,offsetTop:0,offsetLeft:0},arguments[2]||{})
+source=$(source);var p=Position.page(source);target=$(target);var delta=[0,0];var parent=null;if(Element.getStyle(target,'position')=='absolute'){parent=Position.offsetParent(target);delta=Position.page(parent);}
+if(parent==document.body){delta[0]-=document.body.offsetLeft;delta[1]-=document.body.offsetTop;}
+if(options.setLeft)target.style.left=(p[0]-delta[0]+options.offsetLeft)+'px';if(options.setTop)target.style.top=(p[1]-delta[1]+options.offsetTop)+'px';if(options.setWidth)target.style.width=source.offsetWidth+'px';if(options.setHeight)target.style.height=source.offsetHeight+'px';},absolutize:function(element){element=$(element);if(element.style.position=='absolute')return;Position.prepare();var offsets=Position.positionedOffset(element);var top=offsets[1];var left=offsets[0];var width=element.clientWidth;var height=element.clientHeight;element._originalLeft=left-parseFloat(element.style.left||0);element._originalTop=top-parseFloat(element.style.top||0);element._originalWidth=element.style.width;element._originalHeight=element.style.height;element.style.position='absolute';element.style.top=top+'px';element.style.left=left+'px';element.style.width=width+'px';element.style.height=height+'px';},relativize:function(element){element=$(element);if(element.style.position=='relative')return;Position.prepare();element.style.position='relative';var top=parseFloat(element.style.top||0)-(element._originalTop||0);var left=parseFloat(element.style.left||0)-(element._originalLeft||0);element.style.top=top+'px';element.style.left=left+'px';element.style.height=element._originalHeight;element.style.width=element._originalWidth;}}
+if(Prototype.Browser.WebKit){Position.cumulativeOffset=function(element){var valueT=0,valueL=0;do{valueT+=element.offsetTop||0;valueL+=element.offsetLeft||0;if(element.offsetParent==document.body)
+if(Element.getStyle(element,'position')=='absolute')break;element=element.offsetParent;}while(element);return[valueL,valueT];}}
+Element.addMethods();var Builder={NODEMAP:{AREA:'map',CAPTION:'table',COL:'table',COLGROUP:'table',LEGEND:'fieldset',OPTGROUP:'select',OPTION:'select',PARAM:'object',TBODY:'table',TD:'table',TFOOT:'table',TH:'table',THEAD:'table',TR:'table'},node:function(elementName){elementName=elementName.toUpperCase();var parentTag=this.NODEMAP[elementName]||'div';var parentElement=document.createElement(parentTag);try{parentElement.innerHTML="<"+elementName+"></"+elementName+">";}catch(e){}
+var element=parentElement.firstChild||null;if(element&&(element.tagName.toUpperCase()!=elementName))
+element=element.getElementsByTagName(elementName)[0];if(!element)element=document.createElement(elementName);if(!element)return;if(arguments[1])
+if(this._isStringOrNumber(arguments[1])||(arguments[1]instanceof Array)){this._children(element,arguments[1]);}else{var attrs=this._attributes(arguments[1]);if(attrs.length){try{parentElement.innerHTML="<"+elementName+" "+
+attrs+"></"+elementName+">";}catch(e){}
+element=parentElement.firstChild||null;if(!element){element=document.createElement(elementName);for(attr in arguments[1])
+element[attr=='class'?'className':attr]=arguments[1][attr];}
+if(element.tagName.toUpperCase()!=elementName)
+element=parentElement.getElementsByTagName(elementName)[0];}}
+if(arguments[2])
+this._children(element,arguments[2]);return element;},_text:function(text){return document.createTextNode(text);},ATTR_MAP:{'className':'class','htmlFor':'for'},_attributes:function(attributes){var attrs=[];for(attribute in attributes)
+attrs.push((attribute in this.ATTR_MAP?this.ATTR_MAP[attribute]:attribute)+'="'+attributes[attribute].toString().escapeHTML()+'"');return attrs.join(" ");},_children:function(element,children){if(typeof children=='object'){children.flatten().each(function(e){if(typeof e=='object')
+element.appendChild(e)
+else
+if(Builder._isStringOrNumber(e))
+element.appendChild(Builder._text(e));});}else
+if(Builder._isStringOrNumber(children))
+element.appendChild(Builder._text(children));},_isStringOrNumber:function(param){return(typeof param=='string'||typeof param=='number');},build:function(html){var element=this.node('div');$(element).update(html.strip());return element.down();},dump:function(scope){if(typeof scope!='object'&&typeof scope!='function')scope=window;var tags=("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY "+"BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET "+"FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+"KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+"PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+"TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/);tags.each(function(tag){scope[tag]=function(){return Builder.node.apply(Builder,[tag].concat($A(arguments)));}});}}
Function.prototype.bindEvent=function()
{var __method=this,args=$A(arguments),object=args.shift();return function(event)
{return __method.apply(object,[event||window.event].concat(args));}}
@@ -18,11 +259,7 @@ Object.extend(component.prototype,definition);return component;}
var Base=function(){if(arguments.length){if(this==window){Base.prototype.extend.call(arguments[0],arguments.callee.prototype);}else{this.extend(arguments[0]);}}};Base.version="1.0.2";Base.prototype={extend:function(source,value){var extend=Base.prototype.extend;if(arguments.length==2){var ancestor=this[source];if((ancestor instanceof Function)&&(value instanceof Function)&&ancestor.valueOf()!=value.valueOf()&&/\bbase\b/.test(value)){var method=value;value=function(){var previous=this.base;this.base=ancestor;var returnValue=method.apply(this,arguments);this.base=previous;return returnValue;};value.valueOf=function(){return method;};value.toString=function(){return String(method);};}
return this[source]=value;}else if(source){var _prototype={toSource:null};var _protected=["toString","valueOf"];if(Base._prototyping)_protected[2]="constructor";for(var i=0;(name=_protected[i]);i++){if(source[name]!=_prototype[name]){extend.call(this,name,source[name]);}}
for(var name in source){if(!_prototype[name]){extend.call(this,name,source[name]);}}}
-return this;},base:function(){}};Base.extend=function(_instance,_static){var extend=Base.prototype.extend;if(!_instance)_instance={};Base._prototyping=true;var _prototype=new this;extend.call(_prototype,_instance);var constructor=_prototype.constructor;_prototype.constructor=this;delete Base._prototyping;var klass=function(){if(!Base._prototyping)constructor.apply(this,arguments);this.constructor=klass;};klass.prototype=_prototype;klass.extend=this.extend;klass.implement=this.implement;klass.toString=function(){return String(constructor);};extend.call(klass,_static);var object=constructor?klass:_prototype;if(object.init instanceof Function)object.init();return object;};Base.implement=function(_interface){if(_interface instanceof Function)_interface=_interface.prototype;this.prototype.extend(_interface);};Object.extend(String.prototype,{gsub:function(pattern,replacement){var result='',source=this,match;replacement=arguments.callee.prepareReplacement(replacement);while(source.length>0){if(match=source.match(pattern)){result+=source.slice(0,match.index);result+=(replacement(match)||'').toString();source=source.slice(match.index+match[0].length);}else{result+=source,source='';}}
-return result;},sub:function(pattern,replacement,count){replacement=this.gsub.prepareReplacement(replacement);count=count===undefined?1:count;return this.gsub(pattern,function(match){if(--count<0)return match[0];return replacement(match);});},scan:function(pattern,iterator){this.gsub(pattern,iterator);return this;},truncate:function(length,truncation){length=length||30;truncation=truncation===undefined?'...':truncation;return this.length>length?this.slice(0,length-truncation.length)+truncation:this;},strip:function(){return this.replace(/^\s+/,'').replace(/\s+$/,'');},stripTags:function(){return this.replace(/<\/?[^>]+>/gi,'');},stripScripts:function(){return this.replace(new RegExp(Prototype.ScriptFragment,'img'),'');},extractScripts:function(){var matchAll=new RegExp(Prototype.ScriptFragment,'img');var matchOne=new RegExp(Prototype.ScriptFragment,'im');return(this.match(matchAll)||[]).map(function(scriptTag){return(scriptTag.match(matchOne)||['',''])[1];});},evalScripts:function(){return this.extractScripts().map(function(script){return eval(script)});},escapeHTML:function(){var div=document.createElement('div');var text=document.createTextNode(this);div.appendChild(text);return div.innerHTML;},unescapeHTML:function(){var div=document.createElement('div');div.innerHTML=this.stripTags();return div.childNodes[0]?div.childNodes[0].nodeValue:'';},toQueryParams:function(){var pairs=this.match(/^\??(.*)$/)[1].split('&');return pairs.inject({},function(params,pairString){var pair=pairString.split('=');params[pair[0]]=pair[1];return params;});},toArray:function(){return this.split('');},camelize:function(){var oStringList=this.split('-');if(oStringList.length==1)return oStringList[0];var camelizedString=this.indexOf('-')==0?oStringList[0].charAt(0).toUpperCase()+oStringList[0].substring(1):oStringList[0];for(var i=1,len=oStringList.length;i<len;i++){var s=oStringList[i];camelizedString+=s.charAt(0).toUpperCase()+s.substring(1);}
-return camelizedString;},inspect:function(){return"'"+this.replace(/\\/g,'\\\\').replace(/'/g,'\\\'')+"'";}});String.prototype.gsub.prepareReplacement=function(replacement){if(typeof replacement=='function')return replacement;var template=new Template(replacement);return function(match){return template.evaluate(match)};}
-String.prototype.parseQuery=String.prototype.toQueryParams;var Template=Class.create();Template.Pattern=/(^|.|\r|\n)(#\{(.*?)\})/;Template.prototype={initialize:function(template,pattern){this.template=template.toString();this.pattern=pattern||Template.Pattern;},evaluate:function(object){return this.template.gsub(this.pattern,function(match){var before=match[1];if(before=='\\')return match[2];return before+(object[match[3]]||'').toString();});}}
-Object.extend(String.prototype,{pad:function(side,len,chr){if(!chr)chr=' ';var s=this;var left=side.toLowerCase()=='left';while(s.length<len)s=left?chr+s:s+chr;return s;},padLeft:function(len,chr){return this.pad('left',len,chr);},padRight:function(len,chr){return this.pad('right',len,chr);},zerofill:function(len){return this.padLeft(len,'0');},trim:function(){return this.replace(/^\s+|\s+$/g,'');},trimLeft:function(){return this.replace(/^\s+/,'');},trimRight:function(){return this.replace(/\s+$/,'');},toFunction:function()
+return this;},base:function(){}};Base.extend=function(_instance,_static){var extend=Base.prototype.extend;if(!_instance)_instance={};Base._prototyping=true;var _prototype=new this;extend.call(_prototype,_instance);var constructor=_prototype.constructor;_prototype.constructor=this;delete Base._prototyping;var klass=function(){if(!Base._prototyping)constructor.apply(this,arguments);this.constructor=klass;};klass.prototype=_prototype;klass.extend=this.extend;klass.implement=this.implement;klass.toString=function(){return String(constructor);};extend.call(klass,_static);var object=constructor?klass:_prototype;if(object.init instanceof Function)object.init();return object;};Base.implement=function(_interface){if(_interface instanceof Function)_interface=_interface.prototype;this.prototype.extend(_interface);};Object.extend(String.prototype,{pad:function(side,len,chr){if(!chr)chr=' ';var s=this;var left=side.toLowerCase()=='left';while(s.length<len)s=left?chr+s:s+chr;return s;},padLeft:function(len,chr){return this.pad('left',len,chr);},padRight:function(len,chr){return this.pad('right',len,chr);},zerofill:function(len){return this.padLeft(len,'0');},trim:function(){return this.replace(/^\s+|\s+$/g,'');},trimLeft:function(){return this.replace(/^\s+/,'');},trimRight:function(){return this.replace(/\s+$/,'');},toFunction:function()
{var commands=this.split(/\./);var command=window;commands.each(function(action)
{if(command[new String(action)])
command=command[new String(action)];});if(typeof(command)=="function")
@@ -38,96 +275,7 @@ return null;m[1]=m[1]||"";m[2]=m[2]||"0";m[4]=m[4]||"0";var cleanInput=m[1]+(m[2
+"\\s*$");var m=this.match(exp);if(m==null)
return null;var intermed=m[2]+m[5];var cleanInput=m[1]+intermed.replace(new RegExp("(\\"+groupchar+")","g"),"")
+((digits>0)?"."+m[7]:"");var num=parseFloat(cleanInput);return(isNaN(num)?null:num);},toDate:function(format)
-{return Date.SimpleParse(this,format);}});var $break=new Object();var $continue=new Object();var Enumerable={each:function(iterator){var index=0;try{this._each(function(value){try{iterator(value,index++);}catch(e){if(e!=$continue)throw e;}});}catch(e){if(e!=$break)throw e;}},all:function(iterator){var result=true;this.each(function(value,index){result=result&&!!(iterator||Prototype.K)(value,index);if(!result)throw $break;});return result;},any:function(iterator){var result=true;this.each(function(value,index){if(result=!!(iterator||Prototype.K)(value,index))
-throw $break;});return result;},collect:function(iterator){var results=[];this.each(function(value,index){results.push(iterator(value,index));});return results;},detect:function(iterator){var result;this.each(function(value,index){if(iterator(value,index)){result=value;throw $break;}});return result;},findAll:function(iterator){var results=[];this.each(function(value,index){if(iterator(value,index))
-results.push(value);});return results;},grep:function(pattern,iterator){var results=[];this.each(function(value,index){var stringValue=value.toString();if(stringValue.match(pattern))
-results.push((iterator||Prototype.K)(value,index));})
-return results;},include:function(object){var found=false;this.each(function(value){if(value==object){found=true;throw $break;}});return found;},inject:function(memo,iterator){this.each(function(value,index){memo=iterator(memo,value,index);});return memo;},invoke:function(method){var args=$A(arguments).slice(1);return this.collect(function(value){return value[method].apply(value,args);});},max:function(iterator){var result;this.each(function(value,index){value=(iterator||Prototype.K)(value,index);if(result==undefined||value>=result)
-result=value;});return result;},min:function(iterator){var result;this.each(function(value,index){value=(iterator||Prototype.K)(value,index);if(result==undefined||value<result)
-result=value;});return result;},partition:function(iterator){var trues=[],falses=[];this.each(function(value,index){((iterator||Prototype.K)(value,index)?trues:falses).push(value);});return[trues,falses];},pluck:function(property){var results=[];this.each(function(value,index){results.push(value[property]);});return results;},reject:function(iterator){var results=[];this.each(function(value,index){if(!iterator(value,index))
-results.push(value);});return results;},sortBy:function(iterator){return this.collect(function(value,index){return{value:value,criteria:iterator(value,index)};}).sort(function(left,right){var a=left.criteria,b=right.criteria;return a<b?-1:a>b?1:0;}).pluck('value');},toArray:function(){return this.collect(Prototype.K);},zip:function(){var iterator=Prototype.K,args=$A(arguments);if(typeof args.last()=='function')
-iterator=args.pop();var collections=[this].concat(args).map($A);return this.map(function(value,index){return iterator(collections.pluck(index));});},inspect:function(){return'#<Enumerable:'+this.toArray().inspect()+'>';}}
-Object.extend(Enumerable,{map:Enumerable.collect,find:Enumerable.detect,select:Enumerable.findAll,member:Enumerable.include,entries:Enumerable.toArray});var $A=Array.from=function(iterable){if(!iterable)return[];if(iterable.toArray){return iterable.toArray();}else{var results=[];for(var i=0;i<iterable.length;i++)
-results.push(iterable[i]);return results;}}
-Object.extend(Array.prototype,Enumerable);if(!Array.prototype._reverse)
-Array.prototype._reverse=Array.prototype.reverse;Object.extend(Array.prototype,{_each:function(iterator){for(var i=0;i<this.length;i++)
-iterator(this[i]);},clear:function(){this.length=0;return this;},first:function(){return this[0];},last:function(){return this[this.length-1];},compact:function(){return this.select(function(value){return value!=undefined||value!=null;});},flatten:function(){return this.inject([],function(array,value){return array.concat(value&&value.constructor==Array?value.flatten():[value]);});},without:function(){var values=$A(arguments);return this.select(function(value){return!values.include(value);});},indexOf:function(object){for(var i=0;i<this.length;i++)
-if(this[i]==object)return i;return-1;},reverse:function(inline){return(inline!==false?this:this.toArray())._reverse();},inspect:function(){return'['+this.map(Object.inspect).join(', ')+']';}});var Hash={_each:function(iterator){for(var key in this){var value=this[key];if(typeof value=='function')continue;var pair=[key,value];pair.key=key;pair.value=value;iterator(pair);}},keys:function(){return this.pluck('key');},values:function(){return this.pluck('value');},merge:function(hash){return $H(hash).inject($H(this),function(mergedHash,pair){mergedHash[pair.key]=pair.value;return mergedHash;});},toQueryString:function(){return this.map(function(pair)
-{if(typeof(pair[1])=='object'||typeof(pair[1])=='array')
-{return $A(pair[1]).collect(function(value)
-{return encodeURIComponent(pair[0])+'='+encodeURIComponent(value);}).join('&');}
-else
-return pair.map(encodeURIComponent).join('=');}).join('&');},inspect:function(){return'#<Hash:{'+this.map(function(pair){return pair.map(Object.inspect).join(': ');}).join(', ')+'}>';}}
-function $H(object){var hash=Object.extend({},object||{});Object.extend(hash,Enumerable);Object.extend(hash,Hash);return hash;}
-ObjectRange=Class.create();Object.extend(ObjectRange.prototype,Enumerable);Object.extend(ObjectRange.prototype,{initialize:function(start,end,exclusive){this.start=start;this.end=end;this.exclusive=exclusive;},_each:function(iterator){var value=this.start;do{iterator(value);value=value.succ();}while(this.include(value));},include:function(value){if(value<this.start)
-return false;if(this.exclusive)
-return value<this.end;return value<=this.end;}});var $R=function(start,end,exclusive){return new ObjectRange(start,end,exclusive);}
-function $(){var results=[],element;for(var i=0;i<arguments.length;i++){element=arguments[i];if(typeof element=='string')
-element=document.getElementById(element);results.push(Element.extend(element));}
-return results.length<2?results[0]:results;}
-document.getElementsByClassName=function(className,parentElement){var children=($(parentElement)||document.body).getElementsByTagName('*');return $A(children).inject([],function(elements,child){if(child.className.match(new RegExp("(^|\\s)"+className+"(\\s|$)")))
-elements.push(Element.extend(child));return elements;});}
-if(!window.Element)
-var Element=new Object();Element.extend=function(element){if(!element)return;if(_nativeExtensions)return element;if(!element._extended&&element.tagName&&element!=window){var methods=Element.Methods,cache=Element.extend.cache;for(property in methods){var value=methods[property];if(typeof value=='function')
-element[property]=cache.findOrStore(value);}}
-element._extended=true;return element;}
-Element.extend.cache={findOrStore:function(value){return this[value]=this[value]||function(){return value.apply(null,[this].concat($A(arguments)));}}}
-Element.Methods={visible:function(element){return $(element).style.display!='none';},toggle:function(){for(var i=0;i<arguments.length;i++){var element=$(arguments[i]);Element[Element.visible(element)?'hide':'show'](element);}},hide:function(){for(var i=0;i<arguments.length;i++){var element=$(arguments[i]);element.style.display='none';}},show:function(){for(var i=0;i<arguments.length;i++){var element=$(arguments[i]);element.style.display='';}},remove:function(element){element=$(element);element.parentNode.removeChild(element);},update:function(element,html){$(element).innerHTML=html.stripScripts();setTimeout(function(){html.evalScripts()},10);},replace:function(element,html){element=$(element);if(element.outerHTML){element.outerHTML=html.stripScripts();}else{var range=element.ownerDocument.createRange();range.selectNodeContents(element);element.parentNode.replaceChild(range.createContextualFragment(html.stripScripts()),element);}
-setTimeout(function(){html.evalScripts()},10);},getHeight:function(element){element=$(element);return element.offsetHeight;},classNames:function(element){return new Element.ClassNames(element);},hasClassName:function(element,className){if(!(element=$(element)))return;return Element.classNames(element).include(className);},addClassName:function(element,className){if(!(element=$(element)))return;return Element.classNames(element).add(className);},removeClassName:function(element,className){if(!(element=$(element)))return;return Element.classNames(element).remove(className);},cleanWhitespace:function(element){element=$(element);for(var i=0;i<element.childNodes.length;i++){var node=element.childNodes[i];if(node.nodeType==3&&!/\S/.test(node.nodeValue))
-Element.remove(node);}},empty:function(element){return $(element).innerHTML.match(/^\s*$/);},childOf:function(element,ancestor){element=$(element),ancestor=$(ancestor);while(element=element.parentNode)
-if(element==ancestor)return true;return false;},scrollTo:function(element){element=$(element);var x=element.x?element.x:element.offsetLeft,y=element.y?element.y:element.offsetTop;window.scrollTo(x,y);},getStyle:function(element,style){element=$(element);var value=element.style[style.camelize()];if(!value){if(document.defaultView&&document.defaultView.getComputedStyle){var css=document.defaultView.getComputedStyle(element,null);value=css?css.getPropertyValue(style):null;}else if(element.currentStyle){value=element.currentStyle[style.camelize()];}}
-if(window.opera&&['left','top','right','bottom'].include(style))
-if(Element.getStyle(element,'position')=='static')value='auto';return value=='auto'?null:value;},setStyle:function(element,style){element=$(element);for(var name in style)
-element.style[name.camelize()]=style[name];},getDimensions:function(element){element=$(element);if(Element.getStyle(element,'display')!='none')
-return{width:element.offsetWidth,height:element.offsetHeight};var els=element.style;var originalVisibility=els.visibility;var originalPosition=els.position;els.visibility='hidden';els.position='absolute';els.display='';var originalWidth=element.clientWidth;var originalHeight=element.clientHeight;els.display='none';els.position=originalPosition;els.visibility=originalVisibility;return{width:originalWidth,height:originalHeight};},makePositioned:function(element){element=$(element);var pos=Element.getStyle(element,'position');if(pos=='static'||!pos){element._madePositioned=true;element.style.position='relative';if(window.opera){element.style.top=0;element.style.left=0;}}},undoPositioned:function(element){element=$(element);if(element._madePositioned){element._madePositioned=undefined;element.style.position=element.style.top=element.style.left=element.style.bottom=element.style.right='';}},makeClipping:function(element){element=$(element);if(element._overflow)return;element._overflow=element.style.overflow;if((Element.getStyle(element,'overflow')||'visible')!='hidden')
-element.style.overflow='hidden';},undoClipping:function(element){element=$(element);if(element._overflow)return;element.style.overflow=element._overflow;element._overflow=undefined;}}
-Object.extend(Element,Element.Methods);var _nativeExtensions=false;if(!HTMLElement&&/Konqueror|Safari|KHTML/.test(navigator.userAgent)){var HTMLElement={}
-HTMLElement.prototype=document.createElement('div').__proto__;}
-Element.addMethods=function(methods){Object.extend(Element.Methods,methods||{});if(typeof HTMLElement!='undefined'){var methods=Element.Methods,cache=Element.extend.cache;for(property in methods){var value=methods[property];if(typeof value=='function')
-HTMLElement.prototype[property]=cache.findOrStore(value);}
-_nativeExtensions=true;}}
-Element.addMethods();var Toggle=new Object();Toggle.display=Element.toggle;Abstract.Insertion=function(adjacency){this.adjacency=adjacency;}
-Abstract.Insertion.prototype={initialize:function(element,content){this.element=$(element);this.content=content.stripScripts();if(this.adjacency&&this.element.insertAdjacentHTML){try{this.element.insertAdjacentHTML(this.adjacency,this.content);}catch(e){var tagName=this.element.tagName.toLowerCase();if(tagName=='tbody'||tagName=='tr'){this.insertContent(this.contentFromAnonymousTable());}else{throw e;}}}else{this.range=this.element.ownerDocument.createRange();if(this.initializeRange)this.initializeRange();this.insertContent([this.range.createContextualFragment(this.content)]);}
-setTimeout(function(){content.evalScripts()},10);},contentFromAnonymousTable:function(){var div=document.createElement('div');div.innerHTML='<table><tbody>'+this.content+'</tbody></table>';return $A(div.childNodes[0].childNodes[0].childNodes);}}
-var Insertion=new Object();Insertion.Before=Class.create();Insertion.Before.prototype=Object.extend(new Abstract.Insertion('beforeBegin'),{initializeRange:function(){this.range.setStartBefore(this.element);},insertContent:function(fragments){fragments.each((function(fragment){this.element.parentNode.insertBefore(fragment,this.element);}).bind(this));}});Insertion.Top=Class.create();Insertion.Top.prototype=Object.extend(new Abstract.Insertion('afterBegin'),{initializeRange:function(){this.range.selectNodeContents(this.element);this.range.collapse(true);},insertContent:function(fragments){fragments.reverse(false).each((function(fragment){this.element.insertBefore(fragment,this.element.firstChild);}).bind(this));}});Insertion.Bottom=Class.create();Insertion.Bottom.prototype=Object.extend(new Abstract.Insertion('beforeEnd'),{initializeRange:function(){this.range.selectNodeContents(this.element);this.range.collapse(this.element);},insertContent:function(fragments){fragments.each((function(fragment){this.element.appendChild(fragment);}).bind(this));}});Insertion.After=Class.create();Insertion.After.prototype=Object.extend(new Abstract.Insertion('afterEnd'),{initializeRange:function(){this.range.setStartAfter(this.element);},insertContent:function(fragments){fragments.each((function(fragment){this.element.parentNode.insertBefore(fragment,this.element.nextSibling);}).bind(this));}});Element.ClassNames=Class.create();Element.ClassNames.prototype={initialize:function(element){this.element=$(element);},_each:function(iterator){this.element.className.split(/\s+/).select(function(name){return name.length>0;})._each(iterator);},set:function(className){this.element.className=className;},add:function(classNameToAdd){if(this.include(classNameToAdd))return;this.set(this.toArray().concat(classNameToAdd).join(' '));},remove:function(classNameToRemove){if(!this.include(classNameToRemove))return;this.set(this.select(function(className){return className!=classNameToRemove;}).join(' '));},toString:function(){return this.toArray().join(' ');}}
-Object.extend(Element.ClassNames.prototype,Enumerable);var Field={clear:function(){for(var i=0;i<arguments.length;i++)
-$(arguments[i]).value='';},focus:function(element){$(element).focus();},present:function(){for(var i=0;i<arguments.length;i++)
-if($(arguments[i]).value=='')return false;return true;},select:function(element){$(element).select();},activate:function(element){element=$(element);element.focus();if(element.select)
-element.select();}}
-var Form={serialize:function(form){var elements=Form.getElements($(form));var queryComponents=new Array();for(var i=0;i<elements.length;i++){var queryComponent=Form.Element.serialize(elements[i]);if(queryComponent)
-queryComponents.push(queryComponent);}
-return queryComponents.join('&');},getElements:function(form){form=$(form);var elements=new Array();for(var tagName in Form.Element.Serializers){var tagElements=form.getElementsByTagName(tagName);for(var j=0;j<tagElements.length;j++)
-elements.push(tagElements[j]);}
-return elements;},getInputs:function(form,typeName,name){form=$(form);var inputs=form.getElementsByTagName('input');if(!typeName&&!name)
-return inputs;var matchingInputs=new Array();for(var i=0;i<inputs.length;i++){var input=inputs[i];if((typeName&&input.type!=typeName)||(name&&input.name!=name))
-continue;matchingInputs.push(input);}
-return matchingInputs;},disable:function(form){var elements=Form.getElements(form);for(var i=0;i<elements.length;i++){var element=elements[i];element.blur();element.disabled='true';}},enable:function(form){var elements=Form.getElements(form);for(var i=0;i<elements.length;i++){var element=elements[i];element.disabled='';}},findFirstElement:function(form){return Form.getElements(form).find(function(element){return element.type!='hidden'&&!element.disabled&&['input','select','textarea'].include(element.tagName.toLowerCase());});},focusFirstElement:function(form){Field.activate(Form.findFirstElement(form));},reset:function(form){$(form).reset();}}
-Form.Element={serialize:function(element){element=$(element);var method=element.tagName.toLowerCase();var parameter=Form.Element.Serializers[method](element);if(parameter){var key=encodeURIComponent(parameter[0]);if(key.length==0)return;if(parameter[1].constructor!=Array)
-parameter[1]=[parameter[1]];return parameter[1].map(function(value){return key+'='+encodeURIComponent(value);}).join('&');}},getValue:function(element){element=$(element);var method=element.tagName.toLowerCase();var parameter=Form.Element.Serializers[method](element);if(parameter)
-return parameter[1];}}
-Form.Element.Serializers={input:function(element){if(typeof(element.type)=="undefined")
-return false;switch(element.type.toLowerCase()){case'submit':case'hidden':case'password':case'text':return Form.Element.Serializers.textarea(element);case'checkbox':case'radio':return Form.Element.Serializers.inputSelector(element);}
-return false;},inputSelector:function(element){if(element.checked)
-return[element.name,element.value];},textarea:function(element){return[element.name,element.value];},select:function(element){return Form.Element.Serializers[element.type=='select-one'?'selectOne':'selectMany'](element);},selectOne:function(element){var value='',opt,index=element.selectedIndex;if(index>=0){opt=element.options[index];value=opt.value||opt.text;}
-return[element.name,value];},selectMany:function(element){var value=[];for(var i=0;i<element.length;i++){var opt=element.options[i];if(opt.selected)
-value.push(opt.value||opt.text);}
-return[element.name,value];}}
-var $F=Form.Element.getValue;Abstract.TimedObserver=function(){}
-Abstract.TimedObserver.prototype={initialize:function(element,frequency,callback){this.frequency=frequency;this.element=$(element);this.callback=callback;this.lastValue=this.getValue();this.registerCallback();},registerCallback:function(){setInterval(this.onTimerEvent.bind(this),this.frequency*1000);},onTimerEvent:function(){var value=this.getValue();if(this.lastValue!=value){this.callback(this.element,value);this.lastValue=value;}}}
-Form.Element.Observer=Class.create();Form.Element.Observer.prototype=Object.extend(new Abstract.TimedObserver(),{getValue:function(){return Form.Element.getValue(this.element);}});Form.Observer=Class.create();Form.Observer.prototype=Object.extend(new Abstract.TimedObserver(),{getValue:function(){return Form.serialize(this.element);}});Abstract.EventObserver=function(){}
-Abstract.EventObserver.prototype={initialize:function(element,callback){this.element=$(element);this.callback=callback;this.lastValue=this.getValue();if(this.element.tagName.toLowerCase()=='form')
-this.registerFormCallbacks();else
-this.registerCallback(this.element);},onElementEvent:function(){var value=this.getValue();if(this.lastValue!=value){this.callback(this.element,value);this.lastValue=value;}},registerFormCallbacks:function(){var elements=Form.getElements(this.element);for(var i=0;i<elements.length;i++)
-this.registerCallback(elements[i]);},registerCallback:function(element){if(element.type){switch(element.type.toLowerCase()){case'checkbox':case'radio':Event.observe(element,'click',this.onElementEvent.bind(this));break;case'password':case'text':case'textarea':case'select-one':case'select-multiple':Event.observe(element,'change',this.onElementEvent.bind(this));break;}}}}
-Form.Element.EventObserver=Class.create();Form.Element.EventObserver.prototype=Object.extend(new Abstract.EventObserver(),{getValue:function(){return Form.Element.getValue(this.element);}});Form.EventObserver=Class.create();Form.EventObserver.prototype=Object.extend(new Abstract.EventObserver(),{getValue:function(){return Form.serialize(this.element);}});if(!window.Event){var Event=new Object();}
-Object.extend(Event,{KEY_BACKSPACE:8,KEY_TAB:9,KEY_RETURN:13,KEY_ESC:27,KEY_LEFT:37,KEY_UP:38,KEY_RIGHT:39,KEY_DOWN:40,KEY_DELETE:46,KEY_SPACEBAR:32,element:function(event){return event.target||event.srcElement;},isLeftClick:function(event){return(((event.which)&&(event.which==1))||((event.button)&&(event.button==1)));},pointerX:function(event){return event.pageX||(event.clientX+
-(document.documentElement.scrollLeft||document.body.scrollLeft));},pointerY:function(event){return event.pageY||(event.clientY+
-(document.documentElement.scrollTop||document.body.scrollTop));},stop:function(event){if(event.preventDefault){event.preventDefault();event.stopPropagation();}else{event.returnValue=false;event.cancelBubble=true;}},findElement:function(event,tagName){var element=Event.element(event);while(element.parentNode&&(!element.tagName||(element.tagName.toUpperCase()!=tagName.toUpperCase())))
-element=element.parentNode;return element;},observers:false,_observeAndCache:function(element,name,observer,useCapture){if(!this.observers)this.observers=[];if(element.addEventListener){this.observers.push([element,name,observer,useCapture]);element.addEventListener(name,observer,useCapture);}else if(element.attachEvent){this.observers.push([element,name,observer,useCapture]);element.attachEvent('on'+name,observer);}},unloadCache:function(){if(!Event.observers)return;for(var i=0;i<Event.observers.length;i++){Event.stopObserving.apply(this,Event.observers[i]);Event.observers[i][0]=null;}
-Event.observers=false;},observe:function(element,name,observer,useCapture){var element=$(element);useCapture=useCapture||false;if(name=='keypress'&&(navigator.appVersion.match(/Konqueror|Safari|KHTML/)||element.attachEvent))
-name='keydown';this._observeAndCache(element,name,observer,useCapture);},stopObserving:function(element,name,observer,useCapture){var element=$(element);useCapture=useCapture||false;if(name=='keypress'&&(navigator.appVersion.match(/Konqueror|Safari|KHTML/)||element.detachEvent))
-name='keydown';if(element.removeEventListener){element.removeEventListener(name,observer,useCapture);}else if(element.detachEvent){element.detachEvent('on'+name,observer);}}});if(navigator.appVersion.match(/\bMSIE\b/))
-Event.observe(window,'unload',Event.unloadCache,false);Object.extend(Event,{OnLoad:function(fn)
+{return Date.SimpleParse(this,format);}});Object.extend(Event,{OnLoad:function(fn)
{var w=document.addEventListener&&!window.addEventListener?document:window;Event.observe(w,'load',fn);},keyCode:function(e)
{return e.keyCode!=null?e.keyCode:e.charCode},isHTMLEvent:function(type)
{var events=['abort','blur','change','error','focus','load','reset','resize','scroll','select','submit','unload'];return events.include(type);},isMouseEvent:function(type)
@@ -145,62 +293,7 @@ element.dispatchEvent(event);}
else if(document.createEventObject)
{var evObj=document.createEventObject();element.fireEvent('on'+type,evObj);}
else if(typeof(element['on'+type])=="function")
-element['on'+type]();}});var Position={includeScrollOffsets:false,prepare:function(){this.deltaX=window.pageXOffset||document.documentElement.scrollLeft||document.body.scrollLeft||0;this.deltaY=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0;},realOffset:function(element){var valueT=0,valueL=0;do{valueT+=element.scrollTop||0;valueL+=element.scrollLeft||0;element=element.parentNode;}while(element);return[valueL,valueT];},cumulativeOffset:function(element){var valueT=0,valueL=0;do{valueT+=element.offsetTop||0;valueL+=element.offsetLeft||0;element=element.offsetParent;}while(element);return[valueL,valueT];},positionedOffset:function(element){var valueT=0,valueL=0;do{valueT+=element.offsetTop||0;valueL+=element.offsetLeft||0;element=element.offsetParent;if(element){p=Element.getStyle(element,'position');if(p=='relative'||p=='absolute')break;}}while(element);return[valueL,valueT];},offsetParent:function(element){if(element.offsetParent)return element.offsetParent;if(element==document.body)return element;while((element=element.parentNode)&&element!=document.body)
-if(Element.getStyle(element,'position')!='static')
-return element;return document.body;},within:function(element,x,y){if(this.includeScrollOffsets)
-return this.withinIncludingScrolloffsets(element,x,y);this.xcomp=x;this.ycomp=y;this.offset=this.cumulativeOffset(element);return(y>=this.offset[1]&&y<this.offset[1]+element.offsetHeight&&x>=this.offset[0]&&x<this.offset[0]+element.offsetWidth);},withinIncludingScrolloffsets:function(element,x,y){var offsetcache=this.realOffset(element);this.xcomp=x+offsetcache[0]-this.deltaX;this.ycomp=y+offsetcache[1]-this.deltaY;this.offset=this.cumulativeOffset(element);return(this.ycomp>=this.offset[1]&&this.ycomp<this.offset[1]+element.offsetHeight&&this.xcomp>=this.offset[0]&&this.xcomp<this.offset[0]+element.offsetWidth);},overlap:function(mode,element){if(!mode)return 0;if(mode=='vertical')
-return((this.offset[1]+element.offsetHeight)-this.ycomp)/element.offsetHeight;if(mode=='horizontal')
-return((this.offset[0]+element.offsetWidth)-this.xcomp)/element.offsetWidth;},clone:function(source,target){source=$(source);target=$(target);target.style.position='absolute';var offsets=this.cumulativeOffset(source);target.style.top=offsets[1]+'px';target.style.left=offsets[0]+'px';target.style.width=source.offsetWidth+'px';target.style.height=source.offsetHeight+'px';},page:function(forElement){var valueT=0,valueL=0;var element=forElement;do{valueT+=element.offsetTop||0;valueL+=element.offsetLeft||0;if(element.offsetParent==document.body)
-if(Element.getStyle(element,'position')=='absolute')break;}while(element=element.offsetParent);element=forElement;do{valueT-=element.scrollTop||0;valueL-=element.scrollLeft||0;}while(element=element.parentNode);return[valueL,valueT];},clone:function(source,target){var options=Object.extend({setLeft:true,setTop:true,setWidth:true,setHeight:true,offsetTop:0,offsetLeft:0},arguments[2]||{})
-source=$(source);var p=Position.page(source);target=$(target);var delta=[0,0];var parent=null;if(Element.getStyle(target,'position')=='absolute'){parent=Position.offsetParent(target);delta=Position.page(parent);}
-if(parent==document.body){delta[0]-=document.body.offsetLeft;delta[1]-=document.body.offsetTop;}
-if(options.setLeft)target.style.left=(p[0]-delta[0]+options.offsetLeft)+'px';if(options.setTop)target.style.top=(p[1]-delta[1]+options.offsetTop)+'px';if(options.setWidth)target.style.width=source.offsetWidth+'px';if(options.setHeight)target.style.height=source.offsetHeight+'px';},absolutize:function(element){element=$(element);if(element.style.position=='absolute')return;Position.prepare();var offsets=Position.positionedOffset(element);var top=offsets[1];var left=offsets[0];var width=element.clientWidth;var height=element.clientHeight;element._originalLeft=left-parseFloat(element.style.left||0);element._originalTop=top-parseFloat(element.style.top||0);element._originalWidth=element.style.width;element._originalHeight=element.style.height;element.style.position='absolute';element.style.top=top+'px';;element.style.left=left+'px';;element.style.width=width+'px';;element.style.height=height+'px';;},relativize:function(element){element=$(element);if(element.style.position=='relative')return;Position.prepare();element.style.position='relative';var top=parseFloat(element.style.top||0)-(element._originalTop||0);var left=parseFloat(element.style.left||0)-(element._originalLeft||0);element.style.top=top+'px';element.style.left=left+'px';element.style.height=element._originalHeight;element.style.width=element._originalWidth;}}
-if(/Konqueror|Safari|KHTML/.test(navigator.userAgent)){Position.cumulativeOffset=function(element){var valueT=0,valueL=0;do{valueT+=element.offsetTop||0;valueL+=element.offsetLeft||0;if(element.offsetParent==document.body)
-if(Element.getStyle(element,'position')=='absolute')break;element=element.offsetParent;}while(element);return[valueL,valueT];}}
-var Selector=Class.create();Selector.prototype={initialize:function(expression){this.params={classNames:[]};this.expression=expression.toString().strip();this.parseExpression();this.compileMatcher();},parseExpression:function(){function abort(message){throw'Parse error in selector: '+message;}
-if(this.expression=='')abort('empty expression');var params=this.params,expr=this.expression,match,modifier,clause,rest;while(match=expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)){params.attributes=params.attributes||[];params.attributes.push({name:match[2],operator:match[3],value:match[4]||match[5]||''});expr=match[1];}
-if(expr=='*')return this.params.wildcard=true;while(match=expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)){modifier=match[1],clause=match[2],rest=match[3];switch(modifier){case'#':params.id=clause;break;case'.':params.classNames.push(clause);break;case'':case undefined:params.tagName=clause.toUpperCase();break;default:abort(expr.inspect());}
-expr=rest;}
-if(expr.length>0)abort(expr.inspect());},buildMatchExpression:function(){var params=this.params,conditions=[],clause;if(params.wildcard)
-conditions.push('true');if(clause=params.id)
-conditions.push('element.id == '+clause.inspect());if(clause=params.tagName)
-conditions.push('element.tagName.toUpperCase() == '+clause.inspect());if((clause=params.classNames).length>0)
-for(var i=0;i<clause.length;i++)
-conditions.push('Element.hasClassName(element, '+clause[i].inspect()+')');if(clause=params.attributes){clause.each(function(attribute){var value='element.getAttribute('+attribute.name.inspect()+')';var splitValueBy=function(delimiter){return value+' && '+value+'.split('+delimiter.inspect()+')';}
-switch(attribute.operator){case'=':conditions.push(value+' == '+attribute.value.inspect());break;case'~=':conditions.push(splitValueBy(' ')+'.include('+attribute.value.inspect()+')');break;case'|=':conditions.push(splitValueBy('-')+'.first().toUpperCase() == '+attribute.value.toUpperCase().inspect());break;case'!=':conditions.push(value+' != '+attribute.value.inspect());break;case'':case undefined:conditions.push(value+' != null');break;default:throw'Unknown operator '+attribute.operator+' in selector';}});}
-return conditions.join(' && ');},compileMatcher:function(){this.match=new Function('element','if (!element.tagName) return false; \
- return '+this.buildMatchExpression());},findElements:function(scope){var element;if(element=$(this.params.id))
-if(this.match(element))
-if(!scope||Element.childOf(element,scope))
-return[element];scope=(scope||document).getElementsByTagName(this.params.tagName||'*');var results=[];for(var i=0;i<scope.length;i++)
-if(this.match(element=scope[i]))
-results.push(Element.extend(element));return results;},toString:function(){return this.expression;}}
-function $$(){return $A(arguments).map(function(expression){return expression.strip().split(/\s+/).inject([null],function(results,expr){var selector=new Selector(expr);return results.map(selector.findElements.bind(selector)).flatten();});}).flatten();}
-var Builder={NODEMAP:{AREA:'map',CAPTION:'table',COL:'table',COLGROUP:'table',LEGEND:'fieldset',OPTGROUP:'select',OPTION:'select',PARAM:'object',TBODY:'table',TD:'table',TFOOT:'table',TH:'table',THEAD:'table',TR:'table'},node:function(elementName){elementName=elementName.toUpperCase();var parentTag=this.NODEMAP[elementName]||'div';var parentElement=document.createElement(parentTag);try{parentElement.innerHTML="<"+elementName+"></"+elementName+">";}catch(e){}
-var element=parentElement.firstChild||null;if(element&&(element.tagName!=elementName))
-element=element.getElementsByTagName(elementName)[0];if(!element)element=document.createElement(elementName);if(!element)return;if(arguments[1])
-if(this._isStringOrNumber(arguments[1])||(arguments[1]instanceof Array)){this._children(element,arguments[1]);}else{var attrs=this._attributes(arguments[1]);if(attrs.length){try{parentElement.innerHTML="<"+elementName+" "+
-attrs+"></"+elementName+">";}catch(e){}
-element=parentElement.firstChild||null;if(!element){element=document.createElement(elementName);for(attr in arguments[1])
-element[attr=='class'?'className':attr]=arguments[1][attr];}
-if(element.tagName!=elementName)
-element=parentElement.getElementsByTagName(elementName)[0];}}
-if(arguments[2])
-this._children(element,arguments[2]);return element;},_text:function(text){return document.createTextNode(text);},_attributes:function(attributes){var attrs=[];for(attribute in attributes)
-attrs.push((attribute=='className'?'class':attribute)+'="'+attributes[attribute].toString().escapeHTML()+'"');return attrs.join(" ");},_children:function(element,children){if(typeof children=='object'){children.flatten().each(function(e){if(typeof e=='object')
-element.appendChild(e)
-else
-if(Builder._isStringOrNumber(e))
-element.appendChild(Builder._text(e));});}else
-if(Builder._isStringOrNumber(children))
-element.appendChild(Builder._text(children));},_isStringOrNumber:function(param){return(typeof param=='string'||typeof param=='number');}}
-Object.extend(Builder,{exportTags:function()
-{var tags=["BUTTON","TT","PRE","H1","H2","H3","BR","CANVAS","HR","LABEL","TEXTAREA","FORM","STRONG","SELECT","OPTION","OPTGROUP","LEGEND","FIELDSET","P","UL","OL","LI","TD","TR","THEAD","TBODY","TFOOT","TABLE","TH","INPUT","SPAN","A","DIV","IMG","CAPTION"];tags.each(function(tag)
-{window[tag]=function()
-{var args=$A(arguments);if(args.length==0)
-return Builder.node(tag,null);if(args.length==1)
-return Builder.node(tag,args[0]);if(args.length>1)
-return Builder.node(tag,args.shift(),args);};});}});Builder.exportTags();Object.extend(Date.prototype,{SimpleFormat:function(format,data)
+element['on'+type]();}});Object.extend(Date.prototype,{SimpleFormat:function(format,data)
{data=data||{};var bits=new Array();bits['d']=this.getDate();bits['dd']=String(this.getDate()).zerofill(2);bits['M']=this.getMonth()+1;bits['MM']=String(this.getMonth()+1).zerofill(2);if(data.AbbreviatedMonthNames)
bits['MMM']=data.AbbreviatedMonthNames[this.getMonth()];if(data.MonthNames)
bits['MMMM']=data.MonthNames[this.getMonth()];var yearStr=""+this.getFullYear();yearStr=(yearStr.length==2)?'19'+yearStr:yearStr;bits['yyyy']=yearStr;bits['yy']=bits['yyyy'].toString().substr(2,2);var frm=new String(format);for(var sect in bits)
@@ -240,7 +333,13 @@ if(month==2)
else{if(date>28){return null;}}}
if((month==4)||(month==6)||(month==9)||(month==11))
{if(date>30){return null;}}
-var newdate=new Date(year,month-1,date,0,0,0);return newdate;}});var Prado={Version:'3.1',Browser:function()
+var newdate=new Date(year,month-1,date,0,0,0);return newdate;}});Object.extend(Builder,{exportTags:function()
+{var tags=["BUTTON","TT","PRE","H1","H2","H3","BR","CANVAS","HR","LABEL","TEXTAREA","FORM","STRONG","SELECT","OPTION","OPTGROUP","LEGEND","FIELDSET","P","UL","OL","LI","TD","TR","THEAD","TBODY","TFOOT","TABLE","TH","INPUT","SPAN","A","DIV","IMG","CAPTION"];tags.each(function(tag)
+{window[tag]=function()
+{var args=$A(arguments);if(args.length==0)
+return Builder.node(tag,null);if(args.length==1)
+return Builder.node(tag,args[0]);if(args.length>1)
+return Builder.node(tag,args.shift(),args);};});}});Builder.exportTags();var Prado={Version:'3.1',Browser:function()
{var info={Version:"1.0"};var is_major=parseInt(navigator.appVersion);info.nver=is_major;info.ver=navigator.appVersion;info.agent=navigator.userAgent;info.dom=document.getElementById?1:0;info.opera=window.opera?1:0;info.ie5=(info.ver.indexOf("MSIE 5")>-1&&info.dom&&!info.opera)?1:0;info.ie6=(info.ver.indexOf("MSIE 6")>-1&&info.dom&&!info.opera)?1:0;info.ie4=(document.all&&!info.dom&&!info.opera)?1:0;info.ie=info.ie4||info.ie5||info.ie6;info.mac=info.agent.indexOf("Mac")>-1;info.ns6=(info.dom&&parseInt(info.ver)>=5)?1:0;info.ie3=(info.ver.indexOf("MSIE")&&(is_major<4));info.hotjava=(info.agent.toLowerCase().indexOf('hotjava')!=-1)?1:0;info.ns4=(document.layers&&!info.dom&&!info.hotjava)?1:0;info.bw=(info.ie6||info.ie5||info.ie4||info.ns4||info.ns6||info.opera);info.ver3=(info.hotjava||info.ie3);info.opera7=((info.agent.toLowerCase().indexOf('opera 7')>-1)||(info.agent.toLowerCase().indexOf('opera/7')>-1));info.operaOld=info.opera&&!info.opera7;return info;},ImportCss:function(doc,css_file)
{if(Prado.Browser().ie)
var styleSheet=doc.createStyleSheet(css_file);else
@@ -402,4 +501,34 @@ window.clipboardData.setData("Text",text);},hover:function(obj)
{for(var i=0;i<options.ItemCount;i++)
{var checkBoxOptions=Object.extend({ID:options.ListID+"_c"+i,EventTarget:options.ListName+"$c"+i},options);new Prado.WebUI.TCheckBox(checkBoxOptions);}}});Prado.WebUI.TRadioButtonList=Base.extend({constructor:function(options)
{for(var i=0;i<options.ItemCount;i++)
-{var radioButtonOptions=Object.extend({ID:options.ListID+"_c"+i,EventTarget:options.ListName+"$c"+i},options);new Prado.WebUI.TRadioButton(radioButtonOptions);}}}); \ No newline at end of file
+{var radioButtonOptions=Object.extend({ID:options.ListID+"_c"+i,EventTarget:options.ListName+"$c"+i},options);new Prado.WebUI.TRadioButton(radioButtonOptions);}}});Prado.WebUI.TRatingList=Base.extend({selectedIndex:-1,rating:-1,enabled:true,readOnly:false,constructor:function(options)
+{var cap=$(options.CaptionID);this.options=Object.extend({caption:cap?cap.innerHTML:''},options||{});Prado.WebUI.TRatingList.register(this);this._init();this.selectedIndex=options.SelectedIndex;this.rating=options.Rating;if(options.Rating<=0&&options.SelectedIndex>=0)
+this.rating=options.SelectedIndex+1;this.showRating(this.rating);},_init:function(options)
+{Element.addClassName($(this.options.ListID),this.options.Style);this.radios=new Array();var index=0;for(var i=0;i<this.options.ItemCount;i++)
+{var radio=$(this.options.ListID+'_c'+i);var td=radio.parentNode;if(radio&&td.tagName.toLowerCase()=='td')
+{this.radios.push(radio);Event.observe(td,"mouseover",this.hover.bindEvent(this,index));Event.observe(td,"mouseout",this.recover.bindEvent(this,index));Event.observe(td,"click",this.click.bindEvent(this,index));index++;Element.addClassName(td,"rating");}}},hover:function(ev,index)
+{if(this.enabled==false)return;for(var i=0;i<this.radios.length;i++)
+{var node=this.radios[i].parentNode;var action=i<=index?'addClassName':'removeClassName'
+Element[action](node,"rating_hover");Element.removeClassName(node,"rating_selected");Element.removeClassName(node,"rating_half");}
+this.showCaption(this.getIndexCaption(index));},recover:function(ev,index)
+{if(this.enabled==false)return;this.showRating(this.rating);this.showCaption(this.options.caption);},click:function(ev,index)
+{if(this.enabled==false)return;for(var i=0;i<this.radios.length;i++)
+this.radios[i].checked=(i==index);this.selectedIndex=index;this.setRating(index+1);this.dispatchRequest(ev);},dispatchRequest:function(ev)
+{var requestOptions=Object.extend({ID:this.options.ListID+"_c"+this.selectedIndex,EventTarget:this.options.ListName+"$c"+this.selectedIndex},this.options);var request=new Prado.CallbackRequest(requestOptions.EventTarget,requestOptions);if(request.dispatch()==false)
+Event.stop(ev);},setRating:function(value)
+{this.rating=value;var base=Math.floor(value-1);var remainder=value-base-1;var halfMax=this.options.HalfRating["1"];var index=remainder>halfMax?base+1:base;for(var i=0;i<this.radios.length;i++)
+this.radios[i].checked=(i==index);var caption=this.getIndexCaption(index);this.setCaption(caption);this.showCaption(caption);this.showRating(value);},showRating:function(value)
+{var base=Math.floor(value-1);var remainder=value-base-1;var halfMin=this.options.HalfRating["0"];var halfMax=this.options.HalfRating["1"];var index=remainder>halfMax?base+1:base;var hasHalf=remainder>=halfMin&&remainder<=halfMax;for(var i=0;i<this.radios.length;i++)
+{var node=this.radios[i].parentNode;var action=i>index?'removeClassName':'addClassName';Element[action](node,"rating_selected");if(i==index+1&&hasHalf)
+Element.addClassName(node,"rating_half");else
+Element.removeClassName(node,"rating_half");Element.removeClassName(node,"rating_hover");}},getIndexCaption:function(index)
+{return index>-1?this.radios[index].value:this.options.caption;},showCaption:function(value)
+{var caption=$(this.options.CaptionID);if(caption)caption.innerHTML=value;$(this.options.ListID).title=value;},setCaption:function(value)
+{this.options.caption=value;this.showCaption(value);},setEnabled:function(value)
+{this.enabled=value;for(var i=0;i<this.radios.length;i++)
+{var action=value?'removeClassName':'addClassName'
+Element[action](this.radios[i].parentNode,"rating_disabled");}}},{ratings:{},register:function(rating)
+{Prado.WebUI.TRatingList.ratings[rating.options.ListID]=rating;},setEnabled:function(id,value)
+{Prado.WebUI.TRatingList.ratings[id].setEnabled(value);},setRating:function(id,value)
+{Prado.WebUI.TRatingList.ratings[id].setRating(value);},setCaption:function(id,value)
+{Prado.WebUI.TRatingList.ratings[id].setCaption(value);}}); \ No newline at end of file
diff --git a/framework/Web/Javascripts/js/debug/effects.js b/framework/Web/Javascripts/js/debug/effects.js
index c0f3c4af..685e57ae 100644
--- a/framework/Web/Javascripts/js/debug/effects.js
+++ b/framework/Web/Javascripts/js/debug/effects.js
@@ -1,15 +1,18 @@
-// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// script.aculo.us effects.js v1.7.0, Fri Jan 19 19:16:36 CET 2007
+
+// Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// Contributors:
// Justin Palmer (http://encytemedia.com/)
// Mark Pilgrim (http://diveintomark.org/)
// Martin Bialasinki
//
-// See scriptaculous.js for full license.
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
// converts rgb() and #xxx to #xxxxxx format,
// returns self (or first argument) if not convertable
String.prototype.parseColor = function() {
- var color = '#';
+ var color = '#';
if(this.slice(0,4) == 'rgb(') {
var cols = this.slice(4,this.length-1).split(',');
var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
@@ -41,48 +44,21 @@ Element.collectTextNodesIgnoreClass = function(element, className) {
Element.setContentZoom = function(element, percent) {
element = $(element);
- Element.setStyle(element, {fontSize: (percent/100) + 'em'});
+ element.setStyle({fontSize: (percent/100) + 'em'});
if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
+ return element;
}
-Element.getOpacity = function(element){
- var opacity;
- if (opacity = Element.getStyle(element, 'opacity'))
- return parseFloat(opacity);
- if (opacity = (Element.getStyle(element, 'filter') || '').match(/alpha\(opacity=(.*)\)/))
- if(opacity[1]) return parseFloat(opacity[1]) / 100;
- return 1.0;
+Element.getOpacity = function(element){
+ return $(element).getStyle('opacity');
}
-Element.setOpacity = function(element, value){
- element= $(element);
- if (value == 1){
- Element.setStyle(element, { opacity:
- (/Gecko/.test(navigator.userAgent) && !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ?
- 0.999999 : null });
- if(/MSIE/.test(navigator.userAgent))
- Element.setStyle(element, {filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});
- } else {
- if(value < 0.00001) value = 0;
- Element.setStyle(element, {opacity: value});
- if(/MSIE/.test(navigator.userAgent))
- Element.setStyle(element,
- { filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'') +
- 'alpha(opacity='+value*100+')' });
- }
-}
-
-Element.getInlineOpacity = function(element){
- return $(element).style.opacity || '';
-}
+Element.setOpacity = function(element, value){
+ return $(element).setStyle({opacity:value});
+}
-Element.childrenWithClassName = function(element, className, findFirst) {
- var classNameRegExp = new RegExp("(^|\\s)" + className + "(\\s|$)");
- var results = $A($(element).getElementsByTagName('*'))[findFirst ? 'detect' : 'select']( function(c) {
- return (c.className && c.className.match(classNameRegExp));
- });
- if(!results) results = [];
- return results;
+Element.getInlineOpacity = function(element){
+ return $(element).style.opacity || '';
}
Element.forceRerendering = function(element) {
@@ -104,12 +80,17 @@ Array.prototype.call = function() {
/*--------------------------------------------------------------------------*/
var Effect = {
+ _elementDoesNotExistError: {
+ name: 'ElementDoesNotExistError',
+ message: 'The specified DOM element does not exist, but is required for this effect to operate'
+ },
tagifyText: function(element) {
if(typeof Builder == 'undefined')
throw("Effect.tagifyText requires including script.aculo.us' builder.js library");
var tagifyStyle = 'position:relative';
- if(/MSIE/.test(navigator.userAgent)) tagifyStyle += ';zoom:1';
+ if(/MSIE/.test(navigator.userAgent) && !window.opera) tagifyStyle += ';zoom:1';
+
element = $(element);
$A(element.childNodes).each( function(child) {
if(child.nodeType==3) {
@@ -162,32 +143,35 @@ var Effect2 = Effect; // deprecated
/* ------------- transitions ------------- */
-Effect.Transitions = {}
-
-Effect.Transitions.linear = Prototype.K;
-
-Effect.Transitions.sinoidal = function(pos) {
- return (-Math.cos(pos*Math.PI)/2) + 0.5;
-}
-Effect.Transitions.reverse = function(pos) {
- return 1-pos;
-}
-Effect.Transitions.flicker = function(pos) {
- return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
-}
-Effect.Transitions.wobble = function(pos) {
- return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
-}
-Effect.Transitions.pulse = function(pos) {
- return (Math.floor(pos*10) % 2 == 0 ?
- (pos*10-Math.floor(pos*10)) : 1-(pos*10-Math.floor(pos*10)));
-}
-Effect.Transitions.none = function(pos) {
- return 0;
-}
-Effect.Transitions.full = function(pos) {
- return 1;
-}
+Effect.Transitions = {
+ linear: Prototype.K,
+ sinoidal: function(pos) {
+ return (-Math.cos(pos*Math.PI)/2) + 0.5;
+ },
+ reverse: function(pos) {
+ return 1-pos;
+ },
+ flicker: function(pos) {
+ return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
+ },
+ wobble: function(pos) {
+ return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
+ },
+ pulse: function(pos, pulses) {
+ pulses = pulses || 5;
+ return (
+ Math.round((pos % (1/pulses)) * pulses) == 0 ?
+ ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) :
+ 1 - ((pos * pulses * 2) - Math.floor(pos * pulses * 2))
+ );
+ },
+ none: function(pos) {
+ return 0;
+ },
+ full: function(pos) {
+ return 1;
+ }
+};
/* ------------- core effects ------------- */
@@ -214,6 +198,9 @@ Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
e.finishOn += effect.finishOn;
});
break;
+ case 'with-last':
+ timestamp = this.effects.pluck('startOn').max() || timestamp;
+ break;
case 'end':
// start effect after last queued effect has finished
timestamp = this.effects.pluck('finishOn').max() || timestamp;
@@ -227,7 +214,7 @@ Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
this.effects.push(effect);
if(!this.interval)
- this.interval = setInterval(this.loop.bind(this), 40);
+ this.interval = setInterval(this.loop.bind(this), 15);
},
remove: function(effect) {
this.effects = this.effects.reject(function(e) { return e==effect });
@@ -238,7 +225,8 @@ Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
},
loop: function() {
var timePos = new Date().getTime();
- this.effects.invoke('loop', timePos);
+ for(var i=0, len=this.effects.length;i<len;i++)
+ if(this.effects[i]) this.effects[i].loop(timePos);
}
});
@@ -258,7 +246,7 @@ Effect.Queue = Effect.Queues.get('global');
Effect.DefaultOptions = {
transition: Effect.Transitions.sinoidal,
duration: 1.0, // seconds
- fps: 25.0, // max. 25fps due to Effect.Queue implementation
+ fps: 60.0, // max. 60fps due to Effect.Queue implementation
sync: false, // true for combining
from: 0.0,
to: 1.0,
@@ -326,7 +314,10 @@ Effect.Base.prototype = {
if(this.options[eventName]) this.options[eventName](this);
},
inspect: function() {
- return '#<Effect:' + $H(this).inspect() + ',options:' + $H(this.options).inspect() + '>';
+ var data = $H();
+ for(property in this)
+ if(typeof this[property] != 'function') data[property] = this[property];
+ return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
}
}
@@ -350,12 +341,24 @@ Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
}
});
+Effect.Event = Class.create();
+Object.extend(Object.extend(Effect.Event.prototype, Effect.Base.prototype), {
+ initialize: function() {
+ var options = Object.extend({
+ duration: 0
+ }, arguments[0] || {});
+ this.start(options);
+ },
+ update: Prototype.emptyFunction
+});
+
Effect.Opacity = Class.create();
Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
initialize: function(element) {
this.element = $(element);
+ if(!this.element) throw(Effect._elementDoesNotExistError);
// make this work on IE on elements without 'layout'
- if(/MSIE/.test(navigator.userAgent) && (!this.element.currentStyle.hasLayout))
+ if(/MSIE/.test(navigator.userAgent) && !window.opera && (!this.element.currentStyle.hasLayout))
this.element.setStyle({zoom: 1});
var options = Object.extend({
from: this.element.getOpacity() || 0.0,
@@ -372,6 +375,7 @@ Effect.Move = Class.create();
Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
initialize: function(element) {
this.element = $(element);
+ if(!this.element) throw(Effect._elementDoesNotExistError);
var options = Object.extend({
x: 0,
y: 0,
@@ -410,7 +414,8 @@ Effect.MoveBy = function(element, toTop, toLeft) {
Effect.Scale = Class.create();
Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
initialize: function(element, percent) {
- this.element = $(element)
+ this.element = $(element);
+ if(!this.element) throw(Effect._elementDoesNotExistError);
var options = Object.extend({
scaleX: true,
scaleY: true,
@@ -460,7 +465,7 @@ Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
},
finish: function(position) {
- if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
+ if(this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
},
setDimensions: function(height, width) {
var d = {};
@@ -485,6 +490,7 @@ Effect.Highlight = Class.create();
Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
initialize: function(element) {
this.element = $(element);
+ if(!this.element) throw(Effect._elementDoesNotExistError);
var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {});
this.start(options);
},
@@ -492,9 +498,11 @@ Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype),
// Prevent executing on elements not in the layout flow
if(this.element.getStyle('display')=='none') { this.cancel(); return; }
// Disable background image during the effect
- this.oldStyle = {
- backgroundImage: this.element.getStyle('background-image') };
- this.element.setStyle({backgroundImage: 'none'});
+ this.oldStyle = {};
+ if (!this.options.keepBackgroundImage) {
+ this.oldStyle.backgroundImage = this.element.getStyle('background-image');
+ this.element.setStyle({backgroundImage: 'none'});
+ }
if(!this.options.endcolor)
this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
if(!this.options.restorecolor)
@@ -549,8 +557,7 @@ Effect.Fade = function(element) {
to: 0.0,
afterFinishInternal: function(effect) {
if(effect.options.to!=0) return;
- effect.element.hide();
- effect.element.setStyle({opacity: oldOpacity});
+ effect.element.hide().setStyle({opacity: oldOpacity});
}}, arguments[1] || {});
return new Effect.Opacity(element,options);
}
@@ -565,25 +572,31 @@ Effect.Appear = function(element) {
effect.element.forceRerendering();
},
beforeSetup: function(effect) {
- effect.element.setOpacity(effect.options.from);
- effect.element.show();
+ effect.element.setOpacity(effect.options.from).show();
}}, arguments[1] || {});
return new Effect.Opacity(element,options);
}
Effect.Puff = function(element) {
element = $(element);
- var oldStyle = { opacity: element.getInlineOpacity(), position: element.getStyle('position') };
+ var oldStyle = {
+ opacity: element.getInlineOpacity(),
+ position: element.getStyle('position'),
+ top: element.style.top,
+ left: element.style.left,
+ width: element.style.width,
+ height: element.style.height
+ };
return new Effect.Parallel(
[ new Effect.Scale(element, 200,
{ sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),
new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],
Object.extend({ duration: 1.0,
beforeSetupInternal: function(effect) {
- effect.effects[0].element.setStyle({position: 'absolute'}); },
+ Position.absolutize(effect.effects[0].element)
+ },
afterFinishInternal: function(effect) {
- effect.effects[0].element.hide();
- effect.effects[0].element.setStyle(oldStyle); }
+ effect.effects[0].element.hide().setStyle(oldStyle); }
}, arguments[1] || {})
);
}
@@ -596,8 +609,7 @@ Effect.BlindUp = function(element) {
scaleX: false,
restoreAfterFinish: true,
afterFinishInternal: function(effect) {
- effect.element.hide();
- effect.element.undoClipping();
+ effect.element.hide().undoClipping();
}
}, arguments[1] || {})
);
@@ -613,9 +625,7 @@ Effect.BlindDown = function(element) {
scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
restoreAfterFinish: true,
afterSetup: function(effect) {
- effect.element.makeClipping();
- effect.element.setStyle({height: '0px'});
- effect.element.show();
+ effect.element.makeClipping().setStyle({height: '0px'}).show();
},
afterFinishInternal: function(effect) {
effect.element.undoClipping();
@@ -635,14 +645,10 @@ Effect.SwitchOff = function(element) {
duration: 0.3, scaleFromCenter: true,
scaleX: false, scaleContent: false, restoreAfterFinish: true,
beforeSetup: function(effect) {
- effect.element.makePositioned();
- effect.element.makeClipping();
+ effect.element.makePositioned().makeClipping();
},
afterFinishInternal: function(effect) {
- effect.element.hide();
- effect.element.undoClipping();
- effect.element.undoPositioned();
- effect.element.setStyle({opacity: oldOpacity});
+ effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
}
})
}
@@ -664,9 +670,7 @@ Effect.DropOut = function(element) {
effect.effects[0].element.makePositioned();
},
afterFinishInternal: function(effect) {
- effect.effects[0].element.hide();
- effect.effects[0].element.undoPositioned();
- effect.effects[0].element.setStyle(oldStyle);
+ effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
}
}, arguments[1] || {}));
}
@@ -688,16 +692,14 @@ Effect.Shake = function(element) {
{ x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
new Effect.Move(effect.element,
{ x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
- effect.element.undoPositioned();
- effect.element.setStyle(oldStyle);
+ effect.element.undoPositioned().setStyle(oldStyle);
}}) }}) }}) }}) }}) }});
}
Effect.SlideDown = function(element) {
- element = $(element);
- element.cleanWhitespace();
+ element = $(element).cleanWhitespace();
// SlideDown need to have the content of the element wrapped in a container element with fixed height!
- var oldInnerBottom = $(element.firstChild).getStyle('bottom');
+ var oldInnerBottom = element.down().getStyle('bottom');
var elementDimensions = element.getDimensions();
return new Effect.Scale(element, 100, Object.extend({
scaleContent: false,
@@ -707,34 +709,24 @@ Effect.SlideDown = function(element) {
restoreAfterFinish: true,
afterSetup: function(effect) {
effect.element.makePositioned();
- effect.element.firstChild.makePositioned();
+ effect.element.down().makePositioned();
if(window.opera) effect.element.setStyle({top: ''});
- effect.element.makeClipping();
- effect.element.setStyle({height: '0px'});
- effect.element.show(); },
+ effect.element.makeClipping().setStyle({height: '0px'}).show();
+ },
afterUpdateInternal: function(effect) {
- effect.element.firstChild.setStyle({bottom:
+ effect.element.down().setStyle({bottom:
(effect.dims[0] - effect.element.clientHeight) + 'px' });
},
afterFinishInternal: function(effect) {
- effect.element.undoClipping();
- // IE will crash if child is undoPositioned first
- if(/MSIE/.test(navigator.userAgent)){
- effect.element.undoPositioned();
- effect.element.firstChild.undoPositioned();
- }else{
- effect.element.firstChild.undoPositioned();
- effect.element.undoPositioned();
- }
- effect.element.firstChild.setStyle({bottom: oldInnerBottom}); }
+ effect.element.undoClipping().undoPositioned();
+ effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
}, arguments[1] || {})
);
}
Effect.SlideUp = function(element) {
- element = $(element);
- element.cleanWhitespace();
- var oldInnerBottom = $(element.firstChild).getStyle('bottom');
+ element = $(element).cleanWhitespace();
+ var oldInnerBottom = element.down().getStyle('bottom');
return new Effect.Scale(element, window.opera ? 0 : 1,
Object.extend({ scaleContent: false,
scaleX: false,
@@ -743,32 +735,32 @@ Effect.SlideUp = function(element) {
restoreAfterFinish: true,
beforeStartInternal: function(effect) {
effect.element.makePositioned();
- effect.element.firstChild.makePositioned();
+ effect.element.down().makePositioned();
if(window.opera) effect.element.setStyle({top: ''});
- effect.element.makeClipping();
- effect.element.show(); },
+ effect.element.makeClipping().show();
+ },
afterUpdateInternal: function(effect) {
- effect.element.firstChild.setStyle({bottom:
- (effect.dims[0] - effect.element.clientHeight) + 'px' }); },
+ effect.element.down().setStyle({bottom:
+ (effect.dims[0] - effect.element.clientHeight) + 'px' });
+ },
afterFinishInternal: function(effect) {
- effect.element.hide();
- effect.element.undoClipping();
- effect.element.firstChild.undoPositioned();
- effect.element.undoPositioned();
- effect.element.setStyle({bottom: oldInnerBottom}); }
+ effect.element.hide().undoClipping().undoPositioned().setStyle({bottom: oldInnerBottom});
+ effect.element.down().undoPositioned();
+ }
}, arguments[1] || {})
);
}
// Bug in opera makes the TD containing this element expand for a instance after finish
Effect.Squish = function(element) {
- return new Effect.Scale(element, window.opera ? 1 : 0,
- { restoreAfterFinish: true,
- beforeSetup: function(effect) {
- effect.element.makeClipping(effect.element); },
- afterFinishInternal: function(effect) {
- effect.element.hide(effect.element);
- effect.element.undoClipping(effect.element); }
+ return new Effect.Scale(element, window.opera ? 1 : 0, {
+ restoreAfterFinish: true,
+ beforeSetup: function(effect) {
+ effect.element.makeClipping();
+ },
+ afterFinishInternal: function(effect) {
+ effect.element.hide().undoClipping();
+ }
});
}
@@ -824,9 +816,7 @@ Effect.Grow = function(element) {
y: initialMoveY,
duration: 0.01,
beforeSetup: function(effect) {
- effect.element.hide();
- effect.element.makeClipping();
- effect.element.makePositioned();
+ effect.element.hide().makeClipping().makePositioned();
},
afterFinishInternal: function(effect) {
new Effect.Parallel(
@@ -837,13 +827,10 @@ Effect.Grow = function(element) {
sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
], Object.extend({
beforeSetup: function(effect) {
- effect.effects[0].element.setStyle({height: '0px'});
- effect.effects[0].element.show();
+ effect.effects[0].element.setStyle({height: '0px'}).show();
},
afterFinishInternal: function(effect) {
- effect.effects[0].element.undoClipping();
- effect.effects[0].element.undoPositioned();
- effect.effects[0].element.setStyle(oldStyle);
+ effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle);
}
}, options)
)
@@ -897,13 +884,10 @@ Effect.Shrink = function(element) {
new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
], Object.extend({
beforeStartInternal: function(effect) {
- effect.effects[0].element.makePositioned();
- effect.effects[0].element.makeClipping(); },
+ effect.effects[0].element.makePositioned().makeClipping();
+ },
afterFinishInternal: function(effect) {
- effect.effects[0].element.hide();
- effect.effects[0].element.undoClipping();
- effect.effects[0].element.undoPositioned();
- effect.effects[0].element.setStyle(oldStyle); }
+ effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
}, options)
);
}
@@ -913,10 +897,10 @@ Effect.Pulsate = function(element) {
var options = arguments[1] || {};
var oldOpacity = element.getInlineOpacity();
var transition = options.transition || Effect.Transitions.sinoidal;
- var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos)) };
+ var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) };
reverser.bind(transition);
return new Effect.Opacity(element,
- Object.extend(Object.extend({ duration: 3.0, from: 0,
+ Object.extend(Object.extend({ duration: 2.0, from: 0,
afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
}, options), {transition: reverser}));
}
@@ -928,7 +912,7 @@ Effect.Fold = function(element) {
left: element.style.left,
width: element.style.width,
height: element.style.height };
- Element.makeClipping(element);
+ element.makeClipping();
return new Effect.Scale(element, 5, Object.extend({
scaleContent: false,
scaleX: false,
@@ -937,15 +921,162 @@ Effect.Fold = function(element) {
scaleContent: false,
scaleY: false,
afterFinishInternal: function(effect) {
- effect.element.hide();
- effect.element.undoClipping();
- effect.element.setStyle(oldStyle);
+ effect.element.hide().undoClipping().setStyle(oldStyle);
} });
}}, arguments[1] || {}));
};
+Effect.Morph = Class.create();
+Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), {
+ initialize: function(element) {
+ this.element = $(element);
+ if(!this.element) throw(Effect._elementDoesNotExistError);
+ var options = Object.extend({
+ style: {}
+ }, arguments[1] || {});
+ if (typeof options.style == 'string') {
+ if(options.style.indexOf(':') == -1) {
+ var cssText = '', selector = '.' + options.style;
+ $A(document.styleSheets).reverse().each(function(styleSheet) {
+ if (styleSheet.cssRules) cssRules = styleSheet.cssRules;
+ else if (styleSheet.rules) cssRules = styleSheet.rules;
+ $A(cssRules).reverse().each(function(rule) {
+ if (selector == rule.selectorText) {
+ cssText = rule.style.cssText;
+ throw $break;
+ }
+ });
+ if (cssText) throw $break;
+ });
+ this.style = cssText.parseStyle();
+ options.afterFinishInternal = function(effect){
+ effect.element.addClassName(effect.options.style);
+ effect.transforms.each(function(transform) {
+ if(transform.style != 'opacity')
+ effect.element.style[transform.style.camelize()] = '';
+ });
+ }
+ } else this.style = options.style.parseStyle();
+ } else this.style = $H(options.style)
+ this.start(options);
+ },
+ setup: function(){
+ function parseColor(color){
+ if(!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
+ color = color.parseColor();
+ return $R(0,2).map(function(i){
+ return parseInt( color.slice(i*2+1,i*2+3), 16 )
+ });
+ }
+ this.transforms = this.style.map(function(pair){
+ var property = pair[0].underscore().dasherize(), value = pair[1], unit = null;
+
+ if(value.parseColor('#zzzzzz') != '#zzzzzz') {
+ value = value.parseColor();
+ unit = 'color';
+ } else if(property == 'opacity') {
+ value = parseFloat(value);
+ if(/MSIE/.test(navigator.userAgent) && !window.opera && (!this.element.currentStyle.hasLayout))
+ this.element.setStyle({zoom: 1});
+ } else if(Element.CSS_LENGTH.test(value))
+ var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/),
+ value = parseFloat(components[1]), unit = (components.length == 3) ? components[2] : null;
+
+ var originalValue = this.element.getStyle(property);
+ return $H({
+ style: property,
+ originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0),
+ targetValue: unit=='color' ? parseColor(value) : value,
+ unit: unit
+ });
+ }.bind(this)).reject(function(transform){
+ return (
+ (transform.originalValue == transform.targetValue) ||
+ (
+ transform.unit != 'color' &&
+ (isNaN(transform.originalValue) || isNaN(transform.targetValue))
+ )
+ )
+ });
+ },
+ update: function(position) {
+ var style = $H(), value = null;
+ this.transforms.each(function(transform){
+ value = transform.unit=='color' ?
+ $R(0,2).inject('#',function(m,v,i){
+ return m+(Math.round(transform.originalValue[i]+
+ (transform.targetValue[i] - transform.originalValue[i])*position)).toColorPart() }) :
+ transform.originalValue + Math.round(
+ ((transform.targetValue - transform.originalValue) * position) * 1000)/1000 + transform.unit;
+ style[transform.style] = value;
+ });
+ this.element.setStyle(style);
+ }
+});
+
+Effect.Transform = Class.create();
+Object.extend(Effect.Transform.prototype, {
+ initialize: function(tracks){
+ this.tracks = [];
+ this.options = arguments[1] || {};
+ this.addTracks(tracks);
+ },
+ addTracks: function(tracks){
+ tracks.each(function(track){
+ var data = $H(track).values().first();
+ this.tracks.push($H({
+ ids: $H(track).keys().first(),
+ effect: Effect.Morph,
+ options: { style: data }
+ }));
+ }.bind(this));
+ return this;
+ },
+ play: function(){
+ return new Effect.Parallel(
+ this.tracks.map(function(track){
+ var elements = [$(track.ids) || $$(track.ids)].flatten();
+ return elements.map(function(e){ return new track.effect(e, Object.extend({ sync:true }, track.options)) });
+ }).flatten(),
+ this.options
+ );
+ }
+});
+
+Element.CSS_PROPERTIES = $w(
+ 'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' +
+ 'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
+ 'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
+ 'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
+ 'fontSize fontWeight height left letterSpacing lineHeight ' +
+ 'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+
+ 'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
+ 'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
+ 'right textIndent top width wordSpacing zIndex');
+
+Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
+
+String.prototype.parseStyle = function(){
+ var element = Element.extend(document.createElement('div'));
+ element.innerHTML = '<div style="' + this + '"></div>';
+ var style = element.down().style, styleRules = $H();
+
+ Element.CSS_PROPERTIES.each(function(property){
+ if(style[property]) styleRules[property] = style[property];
+ });
+ if(/MSIE/.test(navigator.userAgent) && !window.opera && this.indexOf('opacity') > -1) {
+ styleRules.opacity = this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1];
+ }
+ return styleRules;
+};
+
+Element.morph = function(element, style) {
+ new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || {}));
+ return element;
+};
+
['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom',
- 'collectTextNodes','collectTextNodesIgnoreClass','childrenWithClassName'].each(
+ 'collectTextNodes','collectTextNodesIgnoreClass','morph'].each(
function(f) { Element.Methods[f] = Element[f]; }
);
@@ -958,11 +1089,3 @@ Element.Methods.visualEffect = function(element, effect, options) {
Element.addMethods();
-Prado.Effect =
-{
- Highlight : function(element, options)
- {
- new Effect.Highlight(element,options||{});
- }
-}
-
diff --git a/framework/Web/Javascripts/js/debug/prado.js b/framework/Web/Javascripts/js/debug/prado.js
index 41415d1f..8f292b72 100644
--- a/framework/Web/Javascripts/js/debug/prado.js
+++ b/framework/Web/Javascripts/js/debug/prado.js
@@ -1,31 +1,36 @@
-/* Prototype JavaScript framework, version <%= PROTOTYPE_VERSION %>
- * (c) 2005 Sam Stephenson <sam@conio.net>
+/* Prototype JavaScript framework, version 1.5.1_rc2
+ * (c) 2005-2007 Sam Stephenson
*
* Prototype is freely distributable under the terms of an MIT-style license.
- * For details, see the Prototype web site: http://prototype.conio.net/
+ * For details, see the Prototype web site: http://www.prototypejs.org/
*
/*--------------------------------------------------------------------------*/
var Prototype = {
- Version: '1.50',
+ Version: '1.5.1_rc2',
+
+ Browser: {
+ IE: !!(window.attachEvent && !window.opera),
+ Opera: !!window.opera,
+ WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
+ Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1
+ },
+ BrowserFeatures: {
+ XPath: !!document.evaluate,
+ ElementExtensions: !!window.HTMLElement,
+ SpecificElementExtensions:
+ (document.createElement('div').__proto__ !==
+ document.createElement('form').__proto__)
+ },
+
ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
-
emptyFunction: function() {},
- K: function(x) {return x}
+ K: function(x) { return x }
}
-/*
-<%= include 'base.js', 'string.js' %>
-
-<%= include 'enumerable.js', 'array.js', 'hash.js', 'range.js' %>
-
-<%= include 'ajax.js', 'dom.js', 'selector.js', 'form.js', 'event.js', 'position.js' %>
-
-*/
-
var Class = {
create: function() {
- return function() {
+ return function() {
this.initialize.apply(this, arguments);
}
}
@@ -40,16 +45,56 @@ Object.extend = function(destination, source) {
return destination;
}
-Object.inspect = function(object) {
- try {
- if (object == undefined) return 'undefined';
- if (object == null) return 'null';
- return object.inspect ? object.inspect() : object.toString();
- } catch (e) {
- if (e instanceof RangeError) return '...';
- throw e;
+Object.extend(Object, {
+ inspect: function(object) {
+ try {
+ if (object === undefined) return 'undefined';
+ if (object === null) return 'null';
+ return object.inspect ? object.inspect() : object.toString();
+ } catch (e) {
+ if (e instanceof RangeError) return '...';
+ throw e;
+ }
+ },
+
+ toJSON: function(object) {
+ var type = typeof object;
+ switch(type) {
+ case 'undefined':
+ case 'function':
+ case 'unknown': return;
+ case 'boolean': return object.toString();
+ }
+ if (object === null) return 'null';
+ if (object.toJSON) return object.toJSON();
+ if (object.ownerDocument === document) return;
+ var results = [];
+ for (var property in object) {
+ var value = Object.toJSON(object[property]);
+ if (value !== undefined)
+ results.push(property.toJSON() + ':' + value);
+ }
+ return '{' + results.join(',') + '}';
+ },
+
+ keys: function(object) {
+ var keys = [];
+ for (var property in object)
+ keys.push(property);
+ return keys;
+ },
+
+ values: function(object) {
+ var values = [];
+ for (var property in object)
+ values.push(object[property]);
+ return values;
+ },
+
+ clone: function(object) {
+ return Object.extend({}, object);
}
-}
+});
Function.prototype.bind = function() {
var __method = this, args = $A(arguments), object = args.shift();
@@ -59,34 +104,50 @@ Function.prototype.bind = function() {
}
Function.prototype.bindAsEventListener = function(object) {
- var __method = this;
+ var __method = this, args = $A(arguments), object = args.shift();
return function(event) {
- return __method.call(object, event || window.event);
+ return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments)));
}
}
Object.extend(Number.prototype, {
toColorPart: function() {
- var digits = this.toString(16);
- if (this < 16) return '0' + digits;
- return digits;
+ return this.toPaddedString(2, 16);
},
succ: function() {
return this + 1;
},
-
+
times: function(iterator) {
$R(0, this, true).each(iterator);
return this;
+ },
+
+ toPaddedString: function(length, radix) {
+ var string = this.toString(radix || 10);
+ return '0'.times(length - string.length) + string;
+ },
+
+ toJSON: function() {
+ return isFinite(this) ? this.toString() : 'null';
}
});
+Date.prototype.toJSON = function() {
+ return '"' + this.getFullYear() + '-' +
+ (this.getMonth() + 1).toPaddedString(2) + '-' +
+ this.getDate().toPaddedString(2) + 'T' +
+ this.getHours().toPaddedString(2) + ':' +
+ this.getMinutes().toPaddedString(2) + ':' +
+ this.getSeconds().toPaddedString(2) + '"';
+};
+
var Try = {
these: function() {
var returnValue;
- for (var i = 0; i < arguments.length; i++) {
+ for (var i = 0, length = arguments.length; i < length; i++) {
var lambda = arguments[i];
try {
returnValue = lambda();
@@ -111,365 +172,49 @@ PeriodicalExecuter.prototype = {
},
registerCallback: function() {
- setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+ this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+ },
+
+ stop: function() {
+ if (!this.timer) return;
+ clearInterval(this.timer);
+ this.timer = null;
},
onTimerEvent: function() {
if (!this.currentlyExecuting) {
- try {
+ try {
this.currentlyExecuting = true;
- this.callback();
- } finally {
+ this.callback(this);
+ } finally {
this.currentlyExecuting = false;
}
}
}
}
-
-
-
-/**
- * Similar to bindAsEventLister, but takes additional arguments.
- */
-Function.prototype.bindEvent = function()
-{
- var __method = this, args = $A(arguments), object = args.shift();
- return function(event)
- {
- return __method.apply(object, [event || window.event].concat(args));
- }
-}
-
-/**
- * Creates a new function by copying function definition from
- * the <tt>base</tt> and optional <tt>definition</tt>.
- * @param function a base function to copy from.
- * @param array additional definition
- * @param function return a new function with definition from both
- * <tt>base</tt> and <tt>definition</tt>.
- */
-Class.extend = function(base, definition)
-{
- var component = Class.create();
- Object.extend(component.prototype, base.prototype);
- if(definition)
- Object.extend(component.prototype, definition);
- return component;
-}
-
-/*
- Base, version 1.0.2
- Copyright 2006, Dean Edwards
- License: http://creativecommons.org/licenses/LGPL/2.1/
-*/
-
-var Base = function() {
- if (arguments.length) {
- if (this == window) { // cast an object to this class
- Base.prototype.extend.call(arguments[0], arguments.callee.prototype);
- } else {
- this.extend(arguments[0]);
- }
- }
-};
-
-Base.version = "1.0.2";
-
-Base.prototype = {
- extend: function(source, value) {
- var extend = Base.prototype.extend;
- if (arguments.length == 2) {
- var ancestor = this[source];
- // overriding?
- if ((ancestor instanceof Function) && (value instanceof Function) &&
- ancestor.valueOf() != value.valueOf() && /\bbase\b/.test(value)) {
- var method = value;
- // var _prototype = this.constructor.prototype;
- // var fromPrototype = !Base._prototyping && _prototype[source] == ancestor;
- value = function() {
- var previous = this.base;
- // this.base = fromPrototype ? _prototype[source] : ancestor;
- this.base = ancestor;
- var returnValue = method.apply(this, arguments);
- this.base = previous;
- return returnValue;
- };
- // point to the underlying method
- value.valueOf = function() {
- return method;
- };
- value.toString = function() {
- return String(method);
- };
- }
- return this[source] = value;
- } else if (source) {
- var _prototype = {toSource: null};
- // do the "toString" and other methods manually
- var _protected = ["toString", "valueOf"];
- // if we are prototyping then include the constructor
- if (Base._prototyping) _protected[2] = "constructor";
- for (var i = 0; (name = _protected[i]); i++) {
- if (source[name] != _prototype[name]) {
- extend.call(this, name, source[name]);
- }
- }
- // copy each of the source object's properties to this object
- for (var name in source) {
- if (!_prototype[name]) {
- extend.call(this, name, source[name]);
- }
- }
- }
- return this;
- },
-
- base: function() {
- // call this method from any other method to invoke that method's ancestor
- }
-};
-
-Base.extend = function(_instance, _static) {
- var extend = Base.prototype.extend;
- if (!_instance) _instance = {};
- // build the prototype
- Base._prototyping = true;
- var _prototype = new this;
- extend.call(_prototype, _instance);
- var constructor = _prototype.constructor;
- _prototype.constructor = this;
- delete Base._prototyping;
- // create the wrapper for the constructor function
- var klass = function() {
- if (!Base._prototyping) constructor.apply(this, arguments);
- this.constructor = klass;
- };
- klass.prototype = _prototype;
- // build the class interface
- klass.extend = this.extend;
- klass.implement = this.implement;
- klass.toString = function() {
- return String(constructor);
- };
- extend.call(klass, _static);
- // single instance
- var object = constructor ? klass : _prototype;
- // class initialisation
- if (object.init instanceof Function) object.init();
- return object;
-};
-
-Base.implement = function(_interface) {
- if (_interface instanceof Function) _interface = _interface.prototype;
- this.prototype.extend(_interface);
-};
-
-/*
- * Signals and Slots for Prototype: Easy custom javascript events
- * http://tetlaw.id.au/view/blog/signals-and-slots-for-prototype-easy-custom-javascript-events
- * Andrew Tetlaw
- * Version 1.2 (2006-06-19)
- *
- * http://creativecommons.org/licenses/by-sa/2.5/
- *
-Signal = {
- throwErrors : true,
- MT : function(){ return true },
- connect : function(obj1, func1, obj2, func2, options) {
- var options = Object.extend({
- connectOnce : false,
- before : false,
- mutate : function() {return arguments;}
- }, options || {});
- if(typeof func1 != 'string' || typeof func2 != 'string') return;
-
- var sigObj = obj1 || window;
- var slotObj = obj2 || window;
- var signame = func1+'__signal_';
- var slotsname = func1+'__slots_';
- if(!sigObj[signame]) {
- // having the slotFunc in a var and setting it by using an anonymous function in this way
- // is apparently a good way to prevent memory leaks in IE if the objects are DOM nodes.
- var slotFunc = function() {
- var args = [];
- for(var x = 0; x < arguments.length; x++){
- args.push(arguments[x]);
- }
- args = options.mutate.apply(null,args)
- var result;
- if(!options.before) result = sigObj[signame].apply(sigObj,arguments); //default: call sign before slot
- sigObj[slotsname].each(function(slot){
- try {
- if(slot && slot[0]) { // testing for null, a disconnect may have nulled this slot
- slot[0][slot[1]].apply(slot[0],args); //[0] = obj, [1] = func name
- }
- } catch(e) {
- if(Signal.throwErrors) throw e;
- }
- });
- if(options.before) result = sigObj[signame].apply(sigObj,arguments); //call slot before sig
- return result; //return sig result
- };
- (function() {
- sigObj[slotsname] = $A([]);
- sigObj[signame] = sigObj[func1] || Signal.MT;
- sigObj[func1] = slotFunc;
- })();
- }
- var con = (sigObj[slotsname].length > 0) ?
- (options.connectOnce ? !sigObj[slotsname].any(function(slot) { return (slot[0] == slotObj && slot[1] == func2) }) : true) :
- true;
- if(con) {
- sigObj[slotsname].push([slotObj,func2]);
- }
- },
- connectOnce : function(obj1, func1, obj2, func2, options) {
- Signal.connect(obj1, func1, obj2, func2, Object.extend(options || {}, {connectOnce : true}))
- },
- disconnect : function(obj1, func1, obj2, func2, options) {
- var options = Object.extend({
- disconnectAll : false
- }, options || {});
- if(typeof func1 != 'string' || typeof func2 != 'string') return;
-
- var sigObj = obj1 || window;
- var slotObj = obj2 || window;
- var signame = func1+'__signal_';
- var slotsname = func1+'__slots_';
-
- // I null them in this way so that any currectly active signal will read a null slot,
- // otherwise the slot will be applied even though it's been disconnected
- if(sigObj[slotsname]) {
- if(options.disconnectAll) {
- sigObj[slotsname] = sigObj[slotsname].collect(function(slot) {
- if(slot[0] == slotObj && slot[1] == func2) {
- slot[0] = null;
- return null;
- } else {
- return slot;
- }
- }).compact();
- } else {
- var idx = -1;
- sigObj[slotsname] = sigObj[slotsname].collect(function(slot, index) {
- if(slot[0] == slotObj && slot[1] == func2 && idx < 0) { //disconnect first match
- idx = index;
- slot[0] = null;
- return null;
- } else {
- return slot;
- }
- }).compact();
- }
- }
- },
- disconnectAll : function(obj1, func1, obj2, func2, options) {
- Signal.disconnect(obj1, func1, obj2, func2, Object.extend(options || {}, {disconnectAll : true}))
- }
-}
-*/
-
-/*
- Tests
-
-// 1. Simple Test 1 "hello Fred" should trigger "Fred is a stupid head"
-
-
- sayHello = function(n) {
- alert("Hello! " + n);
- }
- moron = function(n) {
- alert(n + " is a stupid head");
- }
- Signal.connect(null,'sayHello',null,'moron');
-
- onclick="sayHello('Fred')"
-
-
-// 2. Simple Test 2 repeated insults about Fred
-
-
- Signal.connect(null,'sayHello2',null,'moron2');
- Signal.connect(null,'sayHello2',null,'moron2');
- Signal.connect(null,'sayHello2',null,'moron2');
-
-
-// 3. Simple Test 3 multiple insults about Fred
-
-
- Signal.connect(null,'sayHello3',null,'moron3');
- Signal.connect(null,'sayHello3',null,'bonehead3');
- Signal.connect(null,'sayHello3',null,'idiot3');
-
-
-// 4. Simple Test 4 3 insults about Fred first - 3 then none
-
-
- Signal.connect(null,'sayHello4',null,'moron4');
- Signal.connect(null,'sayHello4',null,'moron4');
- Signal.connect(null,'sayHello4',null,'moron4');
- Signal.disconnect(null,'sayHello4',null,'moron4');
- Signal.disconnect(null,'sayHello4',null,'moron4');
- Signal.disconnect(null,'sayHello4',null,'moron4');
-
-
-// 5. Simple Test 5 connect 3 insults about Fred first - only one, then none
-
-
- Signal.connect(null,'sayHello5',null,'moron5');
- Signal.connect(null,'sayHello5',null,'moron5');
- Signal.connect(null,'sayHello5',null,'moron5');
- Signal.disconnectAll(null,'sayHello5',null,'moron5');
-
-
-// 6. Simple Test 6 connect 3 insults but only one comes out
-
-
- Signal.connectOnce(null,'sayHello6',null,'moron6');
- Signal.connectOnce(null,'sayHello6',null,'moron6');
- Signal.connectOnce(null,'sayHello6',null,'moron6');
-
-
-// 7. Simple Test 7 connect via objects
-
-
- var o = {};
- o.sayHello = function(n) {
- alert("Hello! " + n + " (from object o)");
- }
- var m = {};
- m.moron = function(n) {
- alert(n + " is a stupid head (from object m)");
- }
-
- Signal.connect(o,'sayHello',m,'moron');
-
- onclick="o.sayHello('Fred')"
-
-
-// 8. Simple Test 8 connect but the insult comes first using {before:true}
-
-
- Signal.connect(null,'sayHello8',null,'moron8', {before:true});
-
-
-// 9. Simple Test 9 connect but the insult is mutated
-
-
- Signal.connect(null,'sayHello9',null,'moron9', {mutate:function() { return ['smelly ' + arguments[0]] }});
-
- */
-
+Object.extend(String, {
+ interpret: function(value) {
+ return value == null ? '' : String(value);
+ },
+ specialChar: {
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '\\': '\\\\'
+ }
+});
Object.extend(String.prototype, {
gsub: function(pattern, replacement) {
var result = '', source = this, match;
replacement = arguments.callee.prepareReplacement(replacement);
-
+
while (source.length > 0) {
if (match = source.match(pattern)) {
result += source.slice(0, match.index);
- result += (replacement(match) || '').toString();
+ result += String.interpret(replacement(match));
source = source.slice(match.index + match[0].length);
} else {
result += source, source = '';
@@ -477,33 +222,33 @@ Object.extend(String.prototype, {
}
return result;
},
-
+
sub: function(pattern, replacement, count) {
replacement = this.gsub.prepareReplacement(replacement);
count = count === undefined ? 1 : count;
-
+
return this.gsub(pattern, function(match) {
if (--count < 0) return match[0];
return replacement(match);
});
},
-
+
scan: function(pattern, iterator) {
this.gsub(pattern, iterator);
return this;
},
-
+
truncate: function(length, truncation) {
length = length || 30;
truncation = truncation === undefined ? '...' : truncation;
- return this.length > length ?
+ return this.length > length ?
this.slice(0, length - truncation.length) + truncation : this;
},
strip: function() {
return this.replace(/^\s+/, '').replace(/\s+$/, '');
},
-
+
stripTags: function() {
return this.replace(/<\/?[^>]+>/gi, '');
},
@@ -511,7 +256,7 @@ Object.extend(String.prototype, {
stripScripts: function() {
return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
},
-
+
extractScripts: function() {
var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
@@ -519,55 +264,125 @@ Object.extend(String.prototype, {
return (scriptTag.match(matchOne) || ['', ''])[1];
});
},
-
+
evalScripts: function() {
return this.extractScripts().map(function(script) { return eval(script) });
},
escapeHTML: function() {
- var div = document.createElement('div');
- var text = document.createTextNode(this);
- div.appendChild(text);
- return div.innerHTML;
+ var self = arguments.callee;
+ self.text.data = this;
+ return self.div.innerHTML;
},
unescapeHTML: function() {
var div = document.createElement('div');
div.innerHTML = this.stripTags();
- return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
+ return div.childNodes[0] ? (div.childNodes.length > 1 ?
+ $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
+ div.childNodes[0].nodeValue) : '';
},
-
- toQueryParams: function() {
- var pairs = this.match(/^\??(.*)$/)[1].split('&');
- return pairs.inject({}, function(params, pairString) {
- var pair = pairString.split('=');
- params[pair[0]] = pair[1];
- return params;
+
+ toQueryParams: function(separator) {
+ var match = this.strip().match(/([^?#]*)(#.*)?$/);
+ if (!match) return {};
+
+ return match[1].split(separator || '&').inject({}, function(hash, pair) {
+ if ((pair = pair.split('='))[0]) {
+ var name = decodeURIComponent(pair[0]);
+ var value = pair[1] ? decodeURIComponent(pair[1]) : undefined;
+
+ if (hash[name] !== undefined) {
+ if (hash[name].constructor != Array)
+ hash[name] = [hash[name]];
+ if (value) hash[name].push(value);
+ }
+ else hash[name] = value;
+ }
+ return hash;
});
},
-
+
toArray: function() {
return this.split('');
},
-
+
+ succ: function() {
+ return this.slice(0, this.length - 1) +
+ String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
+ },
+
+ times: function(count) {
+ var result = '';
+ for (var i = 0; i < count; i++) result += this;
+ return result;
+ },
+
camelize: function() {
- var oStringList = this.split('-');
- if (oStringList.length == 1) return oStringList[0];
-
- var camelizedString = this.indexOf('-') == 0
- ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
- : oStringList[0];
-
- for (var i = 1, len = oStringList.length; i < len; i++) {
- var s = oStringList[i];
- camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
- }
-
- return camelizedString;
+ var parts = this.split('-'), len = parts.length;
+ if (len == 1) return parts[0];
+
+ var camelized = this.charAt(0) == '-'
+ ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
+ : parts[0];
+
+ for (var i = 1; i < len; i++)
+ camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
+
+ return camelized;
},
- inspect: function() {
- return "'" + this.replace(/\\/g, '\\\\').replace(/'/g, '\\\'') + "'";
+ capitalize: function() {
+ return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
+ },
+
+ underscore: function() {
+ return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
+ },
+
+ dasherize: function() {
+ return this.gsub(/_/,'-');
+ },
+
+ inspect: function(useDoubleQuotes) {
+ var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
+ var character = String.specialChar[match[0]];
+ return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
+ });
+ if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
+ return "'" + escapedString.replace(/'/g, '\\\'') + "'";
+ },
+
+ toJSON: function() {
+ return this.inspect(true);
+ },
+
+ evalJSON: function(sanitize) {
+ try {
+ if (!sanitize || (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(this)))
+ return eval('(' + this + ')');
+ } catch (e) {}
+ throw new SyntaxError('Badly formated JSON string: ' + this.inspect());
+ },
+
+ include: function(pattern) {
+ return this.indexOf(pattern) > -1;
+ },
+
+ startsWith: function(pattern) {
+ return this.indexOf(pattern) == 0;
+ },
+
+ endsWith: function(pattern) {
+ return this.lastIndexOf(pattern) == (this.length - pattern.length);
+ },
+
+ empty: function() {
+ return this == '';
+ },
+
+ blank: function() {
+ return /^\s*$/.test(this);
}
});
@@ -579,6 +394,13 @@ String.prototype.gsub.prepareReplacement = function(replacement) {
String.prototype.parseQuery = String.prototype.toQueryParams;
+Object.extend(String.prototype.escapeHTML, {
+ div: document.createElement('div'),
+ text: document.createTextNode('')
+});
+
+with (String.prototype.escapeHTML) div.appendChild(text);
+
var Template = Class.create();
Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
Template.prototype = {
@@ -586,190 +408,16 @@ Template.prototype = {
this.template = template.toString();
this.pattern = pattern || Template.Pattern;
},
-
+
evaluate: function(object) {
return this.template.gsub(this.pattern, function(match) {
var before = match[1];
if (before == '\\') return match[2];
- return before + (object[match[3]] || '').toString();
+ return before + String.interpret(object[match[3]]);
});
}
}
-
-/**
- * @class String extensions
- */
-Object.extend(String.prototype,
-{
- /**
- * @param {String} "left" to pad the string on the left, "right" to pad right.
- * @param {Number} minimum string length.
- * @param {String} character(s) to pad
- * @return {String} padded character(s) on the left or right to satisfy minimum string length
- */
-
- pad : function(side, len, chr) {
- if (!chr) chr = ' ';
- var s = this;
- var left = side.toLowerCase()=='left';
- while (s.length<len) s = left? chr + s : s + chr;
- return s;
- },
-
- /**
- * @param {Number} minimum string length.
- * @param {String} character(s) to pad
- * @return {String} padded character(s) on the left to satisfy minimum string length
- */
- padLeft : function(len, chr) {
- return this.pad('left',len,chr);
- },
-
- /**
- * @param {Number} minimum string length.
- * @param {String} character(s) to pad
- * @return {String} padded character(s) on the right to satisfy minimum string length
- */
- padRight : function(len, chr) {
- return this.pad('right',len,chr);
- },
-
- /**
- * @param {Number} minimum string length.
- * @return {String} append zeros to the left to satisfy minimum string length.
- */
- zerofill : function(len) {
- return this.padLeft(len,'0');
- },
-
- /**
- * @return {String} removed white spaces from both ends.
- */
- trim : function() {
- return this.replace(/^\s+|\s+$/g,'');
- },
-
- /**
- * @return {String} removed white spaces from the left end.
- */
- trimLeft : function() {
- return this.replace(/^\s+/,'');
- },
-
- /**
- * @return {String} removed white spaces from the right end.
- */
- trimRight : function() {
- return this.replace(/\s+$/,'');
- },
-
- /**
- * Convert period separated function names into a function reference.
- * e.g. "Prado.AJAX.Callback.Action.setValue".toFunction() will return
- * the actual function Prado.AJAX.Callback.Action.setValue()
- * @return {Function} the corresponding function represented by the string.
- */
- toFunction : function()
- {
- var commands = this.split(/\./);
- var command = window;
- commands.each(function(action)
- {
- if(command[new String(action)])
- command=command[new String(action)];
- });
- if(typeof(command) == "function")
- return command;
- else
- {
- if(typeof Logger != "undefined")
- Logger.error("Missing function", this);
-
- throw new Error ("Missing function '"+this+"'");
- }
- },
-
- /**
- * Convert a string into integer, returns null if not integer.
- * @return {Number} null if string does not represent an integer.
- */
- toInteger : function()
- {
- var exp = /^\s*[-\+]?\d+\s*$/;
- if (this.match(exp) == null)
- return null;
- var num = parseInt(this, 10);
- return (isNaN(num) ? null : num);
- },
-
- /**
- * Convert a string into a double/float value. <b>Internationalization
- * is not supported</b>
- * @param {String} the decimal character
- * @return {Double} null if string does not represent a float value
- */
- toDouble : function(decimalchar)
- {
- if(this.length <= 0) return null;
- decimalchar = decimalchar || ".";
- var exp = new RegExp("^\\s*([-\\+])?(\\d+)?(\\" + decimalchar + "(\\d+))?\\s*$");
- var m = this.match(exp);
-
- if (m == null)
- return null;
- m[1] = m[1] || "";
- m[2] = m[2] || "0";
- m[4] = m[4] || "0";
-
- var cleanInput = m[1] + (m[2].length>0 ? m[2] : "0") + "." + m[4];
- var num = parseFloat(cleanInput);
- return (isNaN(num) ? null : num);
- },
-
- /**
- * Convert strings that represent a currency value (e.g. a float with grouping
- * characters) to float. E.g. "10,000.50" will become "10000.50". The number
- * of dicimal digits, grouping and decimal characters can be specified.
- * <i>The currency input format is <b>very</b> strict, null will be returned if
- * the pattern does not match</i>.
- * @param {String} the grouping character, default is ","
- * @param {Number} number of decimal digits
- * @param {String} the decimal character, default is "."
- * @type {Double} the currency value as float.
- */
- toCurrency : function(groupchar, digits, decimalchar)
- {
- groupchar = groupchar || ",";
- decimalchar = decimalchar || ".";
- digits = typeof(digits) == "undefined" ? 2 : digits;
-
- var exp = new RegExp("^\\s*([-\\+])?(((\\d+)\\" + groupchar + ")*)(\\d+)"
- + ((digits > 0) ? "(\\" + decimalchar + "(\\d{1," + digits + "}))?" : "")
- + "\\s*$");
- var m = this.match(exp);
- if (m == null)
- return null;
- var intermed = m[2] + m[5] ;
- var cleanInput = m[1] + intermed.replace(
- new RegExp("(\\" + groupchar + ")", "g"), "")
- + ((digits > 0) ? "." + m[7] : "");
- var num = parseFloat(cleanInput);
- return (isNaN(num) ? null : num);
- },
-
- /**
- * Converts the string to a date by finding values that matches the
- * date format pattern.
- * @param string date format pattern, e.g. MM-dd-yyyy
- * @return {Date} the date extracted from the string
- */
- toDate : function(format)
- {
- return Date.SimpleParse(this, format);
- }
-});
-
var $break = new Object();
var $continue = new Object();
@@ -778,17 +426,21 @@ var Enumerable = {
var index = 0;
try {
this._each(function(value) {
- try {
- iterator(value, index++);
- } catch (e) {
- if (e != $continue) throw e;
- }
+ iterator(value, index++);
});
} catch (e) {
if (e != $break) throw e;
}
+ return this;
},
-
+
+ eachSlice: function(number, iterator) {
+ var index = -number, slices = [], array = this.toArray();
+ while ((index += number) < array.length)
+ slices.push(array.slice(index, index+number));
+ return slices.map(iterator);
+ },
+
all: function(iterator) {
var result = true;
this.each(function(value, index) {
@@ -797,25 +449,25 @@ var Enumerable = {
});
return result;
},
-
+
any: function(iterator) {
- var result = true;
+ var result = false;
this.each(function(value, index) {
- if (result = !!(iterator || Prototype.K)(value, index))
+ if (result = !!(iterator || Prototype.K)(value, index))
throw $break;
});
return result;
},
-
+
collect: function(iterator) {
var results = [];
this.each(function(value, index) {
- results.push(iterator(value, index));
+ results.push((iterator || Prototype.K)(value, index));
});
return results;
},
-
- detect: function (iterator) {
+
+ detect: function(iterator) {
var result;
this.each(function(value, index) {
if (iterator(value, index)) {
@@ -825,7 +477,7 @@ var Enumerable = {
});
return result;
},
-
+
findAll: function(iterator) {
var results = [];
this.each(function(value, index) {
@@ -834,7 +486,7 @@ var Enumerable = {
});
return results;
},
-
+
grep: function(pattern, iterator) {
var results = [];
this.each(function(value, index) {
@@ -844,7 +496,7 @@ var Enumerable = {
})
return results;
},
-
+
include: function(object) {
var found = false;
this.each(function(value) {
@@ -855,21 +507,29 @@ var Enumerable = {
});
return found;
},
-
+
+ inGroupsOf: function(number, fillWith) {
+ fillWith = fillWith === undefined ? null : fillWith;
+ return this.eachSlice(number, function(slice) {
+ while(slice.length < number) slice.push(fillWith);
+ return slice;
+ });
+ },
+
inject: function(memo, iterator) {
this.each(function(value, index) {
memo = iterator(memo, value, index);
});
return memo;
},
-
+
invoke: function(method) {
var args = $A(arguments).slice(1);
- return this.collect(function(value) {
+ return this.map(function(value) {
return value[method].apply(value, args);
});
},
-
+
max: function(iterator) {
var result;
this.each(function(value, index) {
@@ -879,7 +539,7 @@ var Enumerable = {
});
return result;
},
-
+
min: function(iterator) {
var result;
this.each(function(value, index) {
@@ -889,16 +549,16 @@ var Enumerable = {
});
return result;
},
-
+
partition: function(iterator) {
var trues = [], falses = [];
this.each(function(value, index) {
- ((iterator || Prototype.K)(value, index) ?
+ ((iterator || Prototype.K)(value, index) ?
trues : falses).push(value);
});
return [trues, falses];
},
-
+
pluck: function(property) {
var results = [];
this.each(function(value, index) {
@@ -906,7 +566,7 @@ var Enumerable = {
});
return results;
},
-
+
reject: function(iterator) {
var results = [];
this.each(function(value, index) {
@@ -915,20 +575,20 @@ var Enumerable = {
});
return results;
},
-
+
sortBy: function(iterator) {
- return this.collect(function(value, index) {
+ return this.map(function(value, index) {
return {value: value, criteria: iterator(value, index)};
}).sort(function(left, right) {
var a = left.criteria, b = right.criteria;
return a < b ? -1 : a > b ? 1 : 0;
}).pluck('value');
},
-
+
toArray: function() {
- return this.collect(Prototype.K);
+ return this.map();
},
-
+
zip: function() {
var iterator = Prototype.K, args = $A(arguments);
if (typeof args.last() == 'function')
@@ -939,7 +599,11 @@ var Enumerable = {
return iterator(collections.pluck(index));
});
},
-
+
+ size: function() {
+ return this.toArray().length;
+ },
+
inspect: function() {
return '#<Enumerable:' + this.toArray().inspect() + '>';
}
@@ -952,20 +616,33 @@ Object.extend(Enumerable, {
member: Enumerable.include,
entries: Enumerable.toArray
});
-
-
var $A = Array.from = function(iterable) {
if (!iterable) return [];
if (iterable.toArray) {
return iterable.toArray();
} else {
var results = [];
- for (var i = 0; i < iterable.length; i++)
+ for (var i = 0, length = iterable.length; i < length; i++)
results.push(iterable[i]);
return results;
}
}
+if (Prototype.Browser.WebKit) {
+ $A = Array.from = function(iterable) {
+ if (!iterable) return [];
+ if (!(typeof iterable == 'function' && iterable == '[object NodeList]') &&
+ iterable.toArray) {
+ return iterable.toArray();
+ } else {
+ var results = [];
+ for (var i = 0, length = iterable.length; i < length; i++)
+ results.push(iterable[i]);
+ return results;
+ }
+ }
+}
+
Object.extend(Array.prototype, Enumerable);
if (!Array.prototype._reverse)
@@ -973,64 +650,157 @@ if (!Array.prototype._reverse)
Object.extend(Array.prototype, {
_each: function(iterator) {
- for (var i = 0; i < this.length; i++)
+ for (var i = 0, length = this.length; i < length; i++)
iterator(this[i]);
},
-
+
clear: function() {
this.length = 0;
return this;
},
-
+
first: function() {
return this[0];
},
-
+
last: function() {
return this[this.length - 1];
},
-
+
compact: function() {
return this.select(function(value) {
- return value != undefined || value != null;
+ return value != null;
});
},
-
+
flatten: function() {
return this.inject([], function(array, value) {
return array.concat(value && value.constructor == Array ?
value.flatten() : [value]);
});
},
-
+
without: function() {
var values = $A(arguments);
return this.select(function(value) {
return !values.include(value);
});
},
-
+
indexOf: function(object) {
- for (var i = 0; i < this.length; i++)
+ for (var i = 0, length = this.length; i < length; i++)
if (this[i] == object) return i;
return -1;
},
-
+
reverse: function(inline) {
return (inline !== false ? this : this.toArray())._reverse();
},
-
+
+ reduce: function() {
+ return this.length > 1 ? this : this[0];
+ },
+
+ uniq: function(sorted) {
+ return this.inject([], function(array, value, index) {
+ if (0 == index || (sorted ? array.last() != value : !array.include(value)))
+ array.push(value);
+ return array;
+ });
+ },
+
+ clone: function() {
+ return [].concat(this);
+ },
+
+ size: function() {
+ return this.length;
+ },
+
inspect: function() {
return '[' + this.map(Object.inspect).join(', ') + ']';
+ },
+
+ toJSON: function() {
+ var results = [];
+ this.each(function(object) {
+ var value = Object.toJSON(object);
+ if (value !== undefined) results.push(value);
+ });
+ return '[' + results.join(',') + ']';
}
});
+Array.prototype.toArray = Array.prototype.clone;
-var Hash = {
+function $w(string) {
+ string = string.strip();
+ return string ? string.split(/\s+/) : [];
+}
+
+if (Prototype.Browser.Opera){
+ Array.prototype.concat = function() {
+ var array = [];
+ for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
+ for (var i = 0, length = arguments.length; i < length; i++) {
+ if (arguments[i].constructor == Array) {
+ for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
+ array.push(arguments[i][j]);
+ } else {
+ array.push(arguments[i]);
+ }
+ }
+ return array;
+ }
+}
+var Hash = function(object) {
+ if (object instanceof Hash) this.merge(object);
+ else Object.extend(this, object || {});
+};
+
+Object.extend(Hash, {
+ toQueryString: function(obj) {
+ var parts = [];
+ parts.add = arguments.callee.addPair;
+
+ this.prototype._each.call(obj, function(pair) {
+ if (!pair.key) return;
+ var value = pair.value;
+
+ if (value && typeof value == 'object') {
+ if (value.constructor == Array) value.each(function(value) {
+ parts.add(pair.key, value);
+ });
+ return;
+ }
+ parts.add(pair.key, value);
+ });
+
+ return parts.join('&');
+ },
+
+ toJSON: function(object) {
+ var results = [];
+ this.prototype._each.call(object, function(pair) {
+ var value = Object.toJSON(pair.value);
+ if (value !== undefined) results.push(pair.key.toJSON() + ':' + value);
+ });
+ return '{' + results.join(',') + '}';
+ }
+});
+
+Hash.toQueryString.addPair = function(key, value, prefix) {
+ if (value == null) return;
+ key = encodeURIComponent(key);
+ this.push(key + '=' + (value == null ? '' : encodeURIComponent(value)));
+}
+
+Object.extend(Hash.prototype, Enumerable);
+Object.extend(Hash.prototype, {
_each: function(iterator) {
for (var key in this) {
var value = this[key];
- if (typeof value == 'function') continue;
+ if (value && value == Hash.prototype[key]) continue;
var pair = [key, value];
pair.key = key;
@@ -1048,43 +818,66 @@ var Hash = {
},
merge: function(hash) {
- return $H(hash).inject($H(this), function(mergedHash, pair) {
+ return $H(hash).inject(this, function(mergedHash, pair) {
mergedHash[pair.key] = pair.value;
return mergedHash;
});
},
+ remove: function() {
+ var result;
+ for(var i = 0, length = arguments.length; i < length; i++) {
+ var value = this[arguments[i]];
+ if (value !== undefined){
+ if (result === undefined) result = value;
+ else {
+ if (result.constructor != Array) result = [result];
+ result.push(value)
+ }
+ }
+ delete this[arguments[i]];
+ }
+ return result;
+ },
+
toQueryString: function() {
- return this.map(function(pair)
- {
- //special case for PHP, array post data.
- if(typeof(pair[1]) == 'object' || typeof(pair[1]) == 'array')
- {
- return $A(pair[1]).collect(function(value)
- {
- return encodeURIComponent(pair[0])+'='+encodeURIComponent(value);
- }).join('&');
- }
- else
- return pair.map(encodeURIComponent).join('=');
- }).join('&');
+ return Hash.toQueryString(this);
},
inspect: function() {
return '#<Hash:{' + this.map(function(pair) {
return pair.map(Object.inspect).join(': ');
}).join(', ') + '}>';
+ },
+
+ toJSON: function() {
+ return Hash.toJSON(this);
}
-}
+});
function $H(object) {
- var hash = Object.extend({}, object || {});
- Object.extend(hash, Enumerable);
- Object.extend(hash, Hash);
- return hash;
-}
-
-
+ if (object instanceof Hash) return object;
+ return new Hash(object);
+};
+
+// Safari iterates over shadowed properties
+if (function() {
+ var i = 0, Test = function(value) { this.key = value };
+ Test.prototype.key = 'foo';
+ for (var property in new Test('bar')) i++;
+ return i > 1;
+}()) Hash.prototype._each = function(iterator) {
+ var cache = [];
+ for (var key in this) {
+ var value = this[key];
+ if ((value && value == Hash.prototype[key]) || cache.include(key)) continue;
+ cache.push(key);
+ var pair = [key, value];
+ pair.key = key;
+ pair.value = value;
+ iterator(pair);
+ }
+};
ObjectRange = Class.create();
Object.extend(ObjectRange.prototype, Enumerable);
Object.extend(ObjectRange.prototype, {
@@ -1093,17 +886,17 @@ Object.extend(ObjectRange.prototype, {
this.end = end;
this.exclusive = exclusive;
},
-
+
_each: function(iterator) {
var value = this.start;
- do {
+ while (this.include(value)) {
iterator(value);
value = value.succ();
- } while (this.include(value));
+ }
},
-
+
include: function(value) {
- if (value < this.start)
+ if (value < this.start)
return false;
if (this.exclusive)
return value < this.end;
@@ -1115,47 +908,389 @@ var $R = function(start, end, exclusive) {
return new ObjectRange(start, end, exclusive);
}
-function $() {
- var results = [], element;
- for (var i = 0; i < arguments.length; i++) {
- element = arguments[i];
- if (typeof element == 'string')
- element = document.getElementById(element);
- results.push(Element.extend(element));
+var Ajax = {
+ getTransport: function() {
+ return Try.these(
+ function() {return new XMLHttpRequest()},
+ function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+ function() {return new ActiveXObject('Microsoft.XMLHTTP')}
+ ) || false;
+ },
+
+ activeRequestCount: 0
+}
+
+Ajax.Responders = {
+ responders: [],
+
+ _each: function(iterator) {
+ this.responders._each(iterator);
+ },
+
+ register: function(responder) {
+ if (!this.include(responder))
+ this.responders.push(responder);
+ },
+
+ unregister: function(responder) {
+ this.responders = this.responders.without(responder);
+ },
+
+ dispatch: function(callback, request, transport, json) {
+ this.each(function(responder) {
+ if (typeof responder[callback] == 'function') {
+ try {
+ responder[callback].apply(responder, [request, transport, json]);
+ } catch (e) {}
+ }
+ });
+ }
+};
+
+Object.extend(Ajax.Responders, Enumerable);
+
+Ajax.Responders.register({
+ onCreate: function() {
+ Ajax.activeRequestCount++;
+ },
+ onComplete: function() {
+ Ajax.activeRequestCount--;
+ }
+});
+
+Ajax.Base = function() {};
+Ajax.Base.prototype = {
+ setOptions: function(options) {
+ this.options = {
+ method: 'post',
+ asynchronous: true,
+ contentType: 'application/x-www-form-urlencoded',
+ encoding: 'UTF-8',
+ parameters: ''
+ }
+ Object.extend(this.options, options || {});
+
+ this.options.method = this.options.method.toLowerCase();
+ if (typeof this.options.parameters == 'string')
+ this.options.parameters = this.options.parameters.toQueryParams();
}
- return results.length < 2 ? results[0] : results;
}
-document.getElementsByClassName = function(className, parentElement) {
- var children = ($(parentElement) || document.body).getElementsByTagName('*');
- return $A(children).inject([], function(elements, child) {
- if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
- elements.push(Element.extend(child));
+Ajax.Request = Class.create();
+Ajax.Request.Events =
+ ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
+ _complete: false,
+
+ initialize: function(url, options) {
+ this.transport = Ajax.getTransport();
+ this.setOptions(options);
+ this.request(url);
+ },
+
+ request: function(url) {
+ this.url = url;
+ this.method = this.options.method;
+ var params = Object.clone(this.options.parameters);
+
+ if (!['get', 'post'].include(this.method)) {
+ // simulate other verbs over post
+ params['_method'] = this.method;
+ this.method = 'post';
+ }
+
+ this.parameters = params;
+
+ if (params = Hash.toQueryString(params)) {
+ // when GET, append parameters to URL
+ if (this.method == 'get')
+ this.url += (this.url.include('?') ? '&' : '?') + params;
+ else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
+ params += '&_=';
+ }
+
+ try {
+ Ajax.Responders.dispatch('onCreate', this, this.transport);
+
+ this.transport.open(this.method.toUpperCase(), this.url,
+ this.options.asynchronous);
+
+ if (this.options.asynchronous)
+ setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
+
+ this.transport.onreadystatechange = this.onStateChange.bind(this);
+ this.setRequestHeaders();
+
+ this.body = this.method == 'post' ? (this.options.postBody || params) : null;
+ this.transport.send(this.body);
+
+ /* Force Firefox to handle ready state 4 for synchronous requests */
+ if (!this.options.asynchronous && this.transport.overrideMimeType)
+ this.onStateChange();
+
+ }
+ catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ onStateChange: function() {
+ var readyState = this.transport.readyState;
+ if (readyState > 1 && !((readyState == 4) && this._complete))
+ this.respondToReadyState(this.transport.readyState);
+ },
+
+ setRequestHeaders: function() {
+ var headers = {
+ 'X-Requested-With': 'XMLHttpRequest',
+ 'X-Prototype-Version': Prototype.Version,
+ 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
+ };
+
+ if (this.method == 'post') {
+ headers['Content-type'] = this.options.contentType +
+ (this.options.encoding ? '; charset=' + this.options.encoding : '');
+
+ /* Force "Connection: close" for older Mozilla browsers to work
+ * around a bug where XMLHttpRequest sends an incorrect
+ * Content-length header. See Mozilla Bugzilla #246651.
+ */
+ if (this.transport.overrideMimeType &&
+ (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
+ headers['Connection'] = 'close';
+ }
+
+ // user-defined headers
+ if (typeof this.options.requestHeaders == 'object') {
+ var extras = this.options.requestHeaders;
+
+ if (typeof extras.push == 'function')
+ for (var i = 0, length = extras.length; i < length; i += 2)
+ headers[extras[i]] = extras[i+1];
+ else
+ $H(extras).each(function(pair) { headers[pair.key] = pair.value });
+ }
+
+ for (var name in headers)
+ this.transport.setRequestHeader(name, headers[name]);
+ },
+
+ success: function() {
+ return !this.transport.status
+ || (this.transport.status >= 200 && this.transport.status < 300);
+ },
+
+ respondToReadyState: function(readyState) {
+ var state = Ajax.Request.Events[readyState];
+ var transport = this.transport, json = this.evalJSON();
+
+ if (state == 'Complete') {
+ try {
+ this._complete = true;
+ (this.options['on' + this.transport.status]
+ || this.options['on' + (this.success() ? 'Success' : 'Failure')]
+ || Prototype.emptyFunction)(transport, json);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ if ((this.getHeader('Content-type') || 'text/javascript').strip().
+ match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
+ this.evalResponse();
+ }
+
+ try {
+ (this.options['on' + state] || Prototype.emptyFunction)(transport, json);
+ Ajax.Responders.dispatch('on' + state, this, transport, json);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ if (state == 'Complete') {
+ // avoid memory leak in MSIE: clean up
+ this.transport.onreadystatechange = Prototype.emptyFunction;
+ }
+ },
+
+ getHeader: function(name) {
+ try {
+ return this.transport.getResponseHeader(name);
+ } catch (e) { return null }
+ },
+
+ evalJSON: function() {
+ try {
+ var json = this.getHeader('X-JSON');
+ return json ? eval('(' + json + ')') : null;
+ } catch (e) { return null }
+ },
+
+ evalResponse: function() {
+ try {
+ return eval(this.transport.responseText);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ dispatchException: function(exception) {
+ (this.options.onException || Prototype.emptyFunction)(this, exception);
+ Ajax.Responders.dispatch('onException', this, exception);
+ }
+});
+
+Ajax.Updater = Class.create();
+
+Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
+ initialize: function(container, url, options) {
+ this.container = {
+ success: (container.success || container),
+ failure: (container.failure || (container.success ? null : container))
+ }
+
+ this.transport = Ajax.getTransport();
+ this.setOptions(options);
+
+ var onComplete = this.options.onComplete || Prototype.emptyFunction;
+ this.options.onComplete = (function(transport, param) {
+ this.updateContent();
+ onComplete(transport, param);
+ }).bind(this);
+
+ this.request(url);
+ },
+
+ updateContent: function() {
+ var receiver = this.container[this.success() ? 'success' : 'failure'];
+ var response = this.transport.responseText;
+
+ if (!this.options.evalScripts) response = response.stripScripts();
+
+ if (receiver = $(receiver)) {
+ if (this.options.insertion)
+ new this.options.insertion(receiver, response);
+ else
+ receiver.update(response);
+ }
+
+ if (this.success()) {
+ if (this.onComplete)
+ setTimeout(this.onComplete.bind(this), 10);
+ }
+ }
+});
+
+Ajax.PeriodicalUpdater = Class.create();
+Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
+ initialize: function(container, url, options) {
+ this.setOptions(options);
+ this.onComplete = this.options.onComplete;
+
+ this.frequency = (this.options.frequency || 2);
+ this.decay = (this.options.decay || 1);
+
+ this.updater = {};
+ this.container = container;
+ this.url = url;
+
+ this.start();
+ },
+
+ start: function() {
+ this.options.onComplete = this.updateComplete.bind(this);
+ this.onTimerEvent();
+ },
+
+ stop: function() {
+ this.updater.options.onComplete = undefined;
+ clearTimeout(this.timer);
+ (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
+ },
+
+ updateComplete: function(request) {
+ if (this.options.decay) {
+ this.decay = (request.responseText == this.lastText ?
+ this.decay * this.options.decay : 1);
+
+ this.lastText = request.responseText;
+ }
+ this.timer = setTimeout(this.onTimerEvent.bind(this),
+ this.decay * this.frequency * 1000);
+ },
+
+ onTimerEvent: function() {
+ this.updater = new Ajax.Updater(this.container, this.url, this.options);
+ }
+});
+function $(element) {
+ if (arguments.length > 1) {
+ for (var i = 0, elements = [], length = arguments.length; i < length; i++)
+ elements.push($(arguments[i]));
return elements;
- });
+ }
+ if (typeof element == 'string')
+ element = document.getElementById(element);
+ return Element.extend(element);
}
+if (Prototype.BrowserFeatures.XPath) {
+ document._getElementsByXPath = function(expression, parentElement) {
+ var results = [];
+ var query = document.evaluate(expression, $(parentElement) || document,
+ null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+ for (var i = 0, length = query.snapshotLength; i < length; i++)
+ results.push(query.snapshotItem(i));
+ return results;
+ };
+
+ document.getElementsByClassName = function(className, parentElement) {
+ var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
+ return document._getElementsByXPath(q, parentElement);
+ }
+
+} else document.getElementsByClassName = function(className, parentElement) {
+ var children = ($(parentElement) || document.body).getElementsByTagName('*');
+ var elements = [], child;
+ for (var i = 0, length = children.length; i < length; i++) {
+ child = children[i];
+ if (Element.hasClassName(child, className))
+ elements.push(Element.extend(child));
+ }
+ return elements;
+};
+
/*--------------------------------------------------------------------------*/
-if (!window.Element)
- var Element = new Object();
+if (!window.Element) var Element = {};
Element.extend = function(element) {
- if (!element) return;
- if (_nativeExtensions) return element;
-
- if (!element._extended && element.tagName && element != window) {
- var methods = Element.Methods, cache = Element.extend.cache;
- for (property in methods) {
- var value = methods[property];
- if (typeof value == 'function')
- element[property] = cache.findOrStore(value);
- }
+ var F = Prototype.BrowserFeatures;
+ if (!element || !element.tagName || element.nodeType == 3 ||
+ element._extended || F.SpecificElementExtensions || element == window)
+ return element;
+
+ var methods = {}, tagName = element.tagName, cache = Element.extend.cache,
+ T = Element.Methods.ByTag;
+
+ // extend methods for all tags (Safari doesn't need this)
+ if (!F.ElementExtensions) {
+ Object.extend(methods, Element.Methods),
+ Object.extend(methods, Element.Methods.Simulated);
}
-
- element._extended = true;
+
+ // extend methods for specific tags
+ if (T[tagName]) Object.extend(methods, T[tagName]);
+
+ for (var property in methods) {
+ var value = methods[property];
+ if (typeof value == 'function' && !(property in element))
+ element[property] = cache.findOrStore(value);
+ }
+
+ element._extended = Prototype.emptyFunction;
return element;
-}
+};
Element.extend.cache = {
findOrStore: function(value) {
@@ -1163,46 +1298,45 @@ Element.extend.cache = {
return value.apply(null, [this].concat($A(arguments)));
}
}
-}
+};
Element.Methods = {
visible: function(element) {
return $(element).style.display != 'none';
},
-
- toggle: function() {
- for (var i = 0; i < arguments.length; i++) {
- var element = $(arguments[i]);
- Element[Element.visible(element) ? 'hide' : 'show'](element);
- }
+
+ toggle: function(element) {
+ element = $(element);
+ Element[Element.visible(element) ? 'hide' : 'show'](element);
+ return element;
},
- hide: function() {
- for (var i = 0; i < arguments.length; i++) {
- var element = $(arguments[i]);
- element.style.display = 'none';
- }
+ hide: function(element) {
+ $(element).style.display = 'none';
+ return element;
},
-
- show: function() {
- for (var i = 0; i < arguments.length; i++) {
- var element = $(arguments[i]);
- element.style.display = '';
- }
+
+ show: function(element) {
+ $(element).style.display = '';
+ return element;
},
remove: function(element) {
element = $(element);
element.parentNode.removeChild(element);
+ return element;
},
update: function(element, html) {
+ html = typeof html == 'undefined' ? '' : html.toString();
$(element).innerHTML = html.stripScripts();
setTimeout(function() {html.evalScripts()}, 10);
+ return element;
},
-
+
replace: function(element, html) {
element = $(element);
+ html = typeof html == 'undefined' ? '' : html.toString();
if (element.outerHTML) {
element.outerHTML = html.stripScripts();
} else {
@@ -1212,105 +1346,250 @@ Element.Methods = {
range.createContextualFragment(html.stripScripts()), element);
}
setTimeout(function() {html.evalScripts()}, 10);
+ return element;
},
-
- getHeight: function(element) {
+
+ inspect: function(element) {
element = $(element);
- return element.offsetHeight;
+ var result = '<' + element.tagName.toLowerCase();
+ $H({'id': 'id', 'className': 'class'}).each(function(pair) {
+ var property = pair.first(), attribute = pair.last();
+ var value = (element[property] || '').toString();
+ if (value) result += ' ' + attribute + '=' + value.inspect(true);
+ });
+ return result + '>';
},
-
+
+ recursivelyCollect: function(element, property) {
+ element = $(element);
+ var elements = [];
+ while (element = element[property])
+ if (element.nodeType == 1)
+ elements.push(Element.extend(element));
+ return elements;
+ },
+
+ ancestors: function(element) {
+ return $(element).recursivelyCollect('parentNode');
+ },
+
+ descendants: function(element) {
+ return $A($(element).getElementsByTagName('*')).each(Element.extend);
+ },
+
+ immediateDescendants: function(element) {
+ if (!(element = $(element).firstChild)) return [];
+ while (element && element.nodeType != 1) element = element.nextSibling;
+ if (element) return [element].concat($(element).nextSiblings());
+ return [];
+ },
+
+ previousSiblings: function(element) {
+ return $(element).recursivelyCollect('previousSibling');
+ },
+
+ nextSiblings: function(element) {
+ return $(element).recursivelyCollect('nextSibling');
+ },
+
+ siblings: function(element) {
+ element = $(element);
+ return element.previousSiblings().reverse().concat(element.nextSiblings());
+ },
+
+ match: function(element, selector) {
+ if (typeof selector == 'string')
+ selector = new Selector(selector);
+ return selector.match($(element));
+ },
+
+ up: function(element, expression, index) {
+ var ancestors = $(element).ancestors();
+ return expression ? Selector.findElement(ancestors, expression, index) :
+ ancestors[index || 0];
+ },
+
+ down: function(element, expression, index) {
+ var descendants = $(element).descendants();
+ return expression ? Selector.findElement(descendants, expression, index) :
+ descendants[index || 0];
+ },
+
+ previous: function(element, expression, index) {
+ var previousSiblings = $(element).previousSiblings();
+ return expression ? Selector.findElement(previousSiblings, expression, index) :
+ previousSiblings[index || 0];
+ },
+
+ next: function(element, expression, index) {
+ var nextSiblings = $(element).nextSiblings();
+ return expression ? Selector.findElement(nextSiblings, expression, index) :
+ nextSiblings[index || 0];
+ },
+
+ getElementsBySelector: function() {
+ var args = $A(arguments), element = $(args.shift());
+ return Selector.findChildElements(element, args);
+ },
+
+ getElementsByClassName: function(element, className) {
+ return document.getElementsByClassName(className, element);
+ },
+
+ readAttribute: function(element, name) {
+ element = $(element);
+ if (Prototype.Browser.IE) {
+ if (!element.attributes) return null;
+ var t = Element._attributeTranslations;
+ if (t.values[name]) return t.values[name](element, name);
+ if (t.names[name]) name = t.names[name];
+ var attribute = element.attributes[name];
+ return attribute ? attribute.nodeValue : null;
+ }
+ return element.getAttribute(name);
+ },
+
+ getHeight: function(element) {
+ return $(element).getDimensions().height;
+ },
+
+ getWidth: function(element) {
+ return $(element).getDimensions().width;
+ },
+
classNames: function(element) {
return new Element.ClassNames(element);
},
hasClassName: function(element, className) {
if (!(element = $(element))) return;
- return Element.classNames(element).include(className);
+ var elementClassName = element.className;
+ if (elementClassName.length == 0) return false;
+ if (elementClassName == className ||
+ elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
+ return true;
+ return false;
},
addClassName: function(element, className) {
if (!(element = $(element))) return;
- return Element.classNames(element).add(className);
+ Element.classNames(element).add(className);
+ return element;
},
removeClassName: function(element, className) {
if (!(element = $(element))) return;
- return Element.classNames(element).remove(className);
+ Element.classNames(element).remove(className);
+ return element;
},
-
+
+ toggleClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
+ return element;
+ },
+
+ observe: function() {
+ Event.observe.apply(Event, arguments);
+ return $A(arguments).first();
+ },
+
+ stopObserving: function() {
+ Event.stopObserving.apply(Event, arguments);
+ return $A(arguments).first();
+ },
+
// removes whitespace-only text node children
cleanWhitespace: function(element) {
element = $(element);
- for (var i = 0; i < element.childNodes.length; i++) {
- var node = element.childNodes[i];
- if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
- Element.remove(node);
+ var node = element.firstChild;
+ while (node) {
+ var nextNode = node.nextSibling;
+ if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
+ element.removeChild(node);
+ node = nextNode;
}
+ return element;
},
-
+
empty: function(element) {
- return $(element).innerHTML.match(/^\s*$/);
+ return $(element).innerHTML.blank();
},
-
- childOf: function(element, ancestor) {
+
+ descendantOf: function(element, ancestor) {
element = $(element), ancestor = $(ancestor);
while (element = element.parentNode)
if (element == ancestor) return true;
return false;
},
-
+
scrollTo: function(element) {
element = $(element);
- var x = element.x ? element.x : element.offsetLeft,
- y = element.y ? element.y : element.offsetTop;
- window.scrollTo(x, y);
+ var pos = Position.cumulativeOffset(element);
+ window.scrollTo(pos[0], pos[1]);
+ return element;
},
-
+
getStyle: function(element, style) {
element = $(element);
- var value = element.style[style.camelize()];
+ style = style == 'float' ? 'cssFloat' : style.camelize();
+ var value = element.style[style];
if (!value) {
- if (document.defaultView && document.defaultView.getComputedStyle) {
- var css = document.defaultView.getComputedStyle(element, null);
- value = css ? css.getPropertyValue(style) : null;
- } else if (element.currentStyle) {
- value = element.currentStyle[style.camelize()];
- }
+ var css = document.defaultView.getComputedStyle(element, null);
+ value = css ? css[style] : null;
}
+ if (style == 'opacity') return value ? parseFloat(value) : 1.0;
+ return value == 'auto' ? null : value;
+ },
- if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
- if (Element.getStyle(element, 'position') == 'static') value = 'auto';
+ getOpacity: function(element) {
+ return $(element).getStyle('opacity');
+ },
- return value == 'auto' ? null : value;
+ setStyle: function(element, styles, camelized) {
+ element = $(element);
+ var elementStyle = element.style;
+
+ for (var property in styles)
+ if (property == 'opacity') element.setOpacity(styles[property])
+ else
+ elementStyle[(property == 'float' || property == 'cssFloat') ?
+ (elementStyle.styleFloat === undefined ? 'cssFloat' : 'styleFloat') :
+ (camelized ? property : property.camelize())] = styles[property];
+
+ return element;
},
-
- setStyle: function(element, style) {
+
+ setOpacity: function(element, value) {
element = $(element);
- for (var name in style)
- element.style[name.camelize()] = style[name];
+ element.style.opacity = (value == 1 || value === '') ? '' :
+ (value < 0.00001) ? 0 : value;
+ return element;
},
-
+
getDimensions: function(element) {
element = $(element);
- if (Element.getStyle(element, 'display') != 'none')
+ var display = $(element).getStyle('display');
+ if (display != 'none' && display != null) // Safari bug
return {width: element.offsetWidth, height: element.offsetHeight};
-
+
// All *Width and *Height properties give 0 on elements with display none,
// so enable the element temporarily
var els = element.style;
var originalVisibility = els.visibility;
var originalPosition = els.position;
+ var originalDisplay = els.display;
els.visibility = 'hidden';
els.position = 'absolute';
- els.display = '';
+ els.display = 'block';
var originalWidth = element.clientWidth;
var originalHeight = element.clientHeight;
- els.display = 'none';
+ els.display = originalDisplay;
els.position = originalPosition;
els.visibility = originalVisibility;
- return {width: originalWidth, height: originalHeight};
+ return {width: originalWidth, height: originalHeight};
},
-
+
makePositioned: function(element) {
element = $(element);
var pos = Element.getStyle(element, 'position');
@@ -1322,8 +1601,9 @@ Element.Methods = {
if (window.opera) {
element.style.top = 0;
element.style.left = 0;
- }
+ }
}
+ return element;
},
undoPositioned: function(element) {
@@ -1334,53 +1614,253 @@ Element.Methods = {
element.style.top =
element.style.left =
element.style.bottom =
- element.style.right = '';
+ element.style.right = '';
}
+ return element;
},
makeClipping: function(element) {
element = $(element);
- if (element._overflow) return;
- element._overflow = element.style.overflow;
+ if (element._overflow) return element;
+ element._overflow = element.style.overflow || 'auto';
if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
element.style.overflow = 'hidden';
+ return element;
},
undoClipping: function(element) {
element = $(element);
- if (element._overflow) return;
- element.style.overflow = element._overflow;
- element._overflow = undefined;
+ if (!element._overflow) return element;
+ element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
+ element._overflow = null;
+ return element;
}
+};
+
+Object.extend(Element.Methods, {childOf: Element.Methods.descendantOf});
+
+if (Prototype.Browser.Opera) {
+ Element.Methods._getStyle = Element.Methods.getStyle;
+ Element.Methods.getStyle = function(element, style) {
+ switch(style) {
+ case 'left':
+ case 'top':
+ case 'right':
+ case 'bottom':
+ if (Element._getStyle(element, 'position') == 'static') return null;
+ default: return Element._getStyle(element, style);
+ }
+ };
}
+else if (Prototype.Browser.IE) {
+ Element.Methods.getStyle = function(element, style) {
+ element = $(element);
+ style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
+ var value = element.style[style];
+ if (!value && element.currentStyle) value = element.currentStyle[style];
+
+ if (style == 'opacity') {
+ if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
+ if (value[1]) return parseFloat(value[1]) / 100;
+ return 1.0;
+ }
-Object.extend(Element, Element.Methods);
+ if (value == 'auto') {
+ if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
+ return element['offset'+style.capitalize()] + 'px';
+ return null;
+ }
+ return value;
+ };
-var _nativeExtensions = false;
+ Element.Methods.setOpacity = function(element, value) {
+ element = $(element);
+ var filter = element.getStyle('filter'), style = element.style;
+ if (value == 1 || value === '') {
+ style.filter = filter.replace(/alpha\([^\)]*\)/gi,'');
+ return element;
+ } else if (value < 0.00001) value = 0;
+ style.filter = filter.replace(/alpha\([^\)]*\)/gi, '') +
+ 'alpha(opacity=' + (value * 100) + ')';
+ return element;
+ };
-if(!HTMLElement && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
- var HTMLElement = {}
- HTMLElement.prototype = document.createElement('div').__proto__;
+ // IE is missing .innerHTML support for TABLE-related elements
+ Element.Methods.update = function(element, html) {
+ element = $(element);
+ html = typeof html == 'undefined' ? '' : html.toString();
+ var tagName = element.tagName.toUpperCase();
+ if (['THEAD','TBODY','TR','TD'].include(tagName)) {
+ var div = document.createElement('div');
+ switch (tagName) {
+ case 'THEAD':
+ case 'TBODY':
+ div.innerHTML = '<table><tbody>' + html.stripScripts() + '</tbody></table>';
+ depth = 2;
+ break;
+ case 'TR':
+ div.innerHTML = '<table><tbody><tr>' + html.stripScripts() + '</tr></tbody></table>';
+ depth = 3;
+ break;
+ case 'TD':
+ div.innerHTML = '<table><tbody><tr><td>' + html.stripScripts() + '</td></tr></tbody></table>';
+ depth = 4;
+ }
+ $A(element.childNodes).each(function(node) { element.removeChild(node) });
+ depth.times(function() { div = div.firstChild });
+ $A(div.childNodes).each(function(node) { element.appendChild(node) });
+ } else {
+ element.innerHTML = html.stripScripts();
+ }
+ setTimeout(function() { html.evalScripts() }, 10);
+ return element;
+ }
+}
+else if (Prototype.Browser.Gecko) {
+ Element.Methods.setOpacity = function(element, value) {
+ element = $(element);
+ element.style.opacity = (value == 1) ? 0.999999 :
+ (value === '') ? '' : (value < 0.00001) ? 0 : value;
+ return element;
+ };
}
+Element._attributeTranslations = {
+ names: {
+ colspan: "colSpan",
+ rowspan: "rowSpan",
+ valign: "vAlign",
+ datetime: "dateTime",
+ accesskey: "accessKey",
+ tabindex: "tabIndex",
+ enctype: "encType",
+ maxlength: "maxLength",
+ readonly: "readOnly",
+ longdesc: "longDesc"
+ },
+ values: {
+ _getAttr: function(element, attribute) {
+ return element.getAttribute(attribute, 2);
+ },
+ _flag: function(element, attribute) {
+ return $(element).hasAttribute(attribute) ? attribute : null;
+ },
+ style: function(element) {
+ return element.style.cssText.toLowerCase();
+ },
+ title: function(element) {
+ var node = element.getAttributeNode('title');
+ return node.specified ? node.nodeValue : null;
+ }
+ }
+};
+
+(function() {
+ Object.extend(this, {
+ href: this._getAttr,
+ src: this._getAttr,
+ disabled: this._flag,
+ checked: this._flag,
+ readonly: this._flag,
+ multiple: this._flag
+ });
+}).call(Element._attributeTranslations.values);
+
+Element.Methods.Simulated = {
+ hasAttribute: function(element, attribute) {
+ var t = Element._attributeTranslations, node;
+ attribute = t.names[attribute] || attribute;
+ node = $(element).getAttributeNode(attribute);
+ return node && node.specified;
+ }
+};
+
+Element.Methods.ByTag = {};
+
+Object.extend(Element, Element.Methods);
+
+if (!Prototype.BrowserFeatures.ElementExtensions &&
+ document.createElement('div').__proto__) {
+ window.HTMLElement = {};
+ window.HTMLElement.prototype = document.createElement('div').__proto__;
+ Prototype.BrowserFeatures.ElementExtensions = true;
+}
+
+Element.hasAttribute = function(element, attribute) {
+ if (element.hasAttribute) return element.hasAttribute(attribute);
+ return Element.Methods.Simulated.hasAttribute(element, attribute);
+};
+
Element.addMethods = function(methods) {
- Object.extend(Element.Methods, methods || {});
-
- if(typeof HTMLElement != 'undefined') {
- var methods = Element.Methods, cache = Element.extend.cache;
- for (property in methods) {
+ var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
+ if (arguments.length == 2) {
+ var tagName = methods;
+ methods = arguments[1];
+ }
+
+ if (!tagName) Object.extend(Element.Methods, methods || {});
+ else {
+ if (tagName.constructor == Array) tagName.each(extend);
+ else extend(tagName);
+ }
+
+ function extend(tagName) {
+ tagName = tagName.toUpperCase();
+ if (!Element.Methods.ByTag[tagName])
+ Element.Methods.ByTag[tagName] = {};
+ Object.extend(Element.Methods.ByTag[tagName], methods);
+ }
+
+ function copy(methods, destination, onlyIfAbsent) {
+ onlyIfAbsent = onlyIfAbsent || false;
+ var cache = Element.extend.cache;
+ for (var property in methods) {
var value = methods[property];
- if (typeof value == 'function')
- HTMLElement.prototype[property] = cache.findOrStore(value);
+ if (!onlyIfAbsent || !(property in destination))
+ destination[property] = cache.findOrStore(value);
}
- _nativeExtensions = true;
}
-}
-Element.addMethods();
+ function findDOMClass(tagName) {
+ var klass;
+ var trans = {
+ "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
+ "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
+ "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
+ "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
+ "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
+ "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
+ "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
+ "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
+ "FrameSet", "IFRAME": "IFrame"
+ };
+ if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
+ if (window[klass]) return window[klass];
+ klass = 'HTML' + tagName + 'Element';
+ if (window[klass]) return window[klass];
+ klass = 'HTML' + tagName.capitalize() + 'Element';
+ if (window[klass]) return window[klass];
+
+ window[klass] = {};
+ window[klass].prototype = document.createElement(tagName).__proto__;
+ return window[klass];
+ }
+
+ if (F.ElementExtensions) {
+ copy(Element.Methods, HTMLElement.prototype);
+ copy(Element.Methods.Simulated, HTMLElement.prototype, true);
+ }
+
+ if (F.SpecificElementExtensions) {
+ for (var tag in Element.Methods.ByTag) {
+ var klass = findDOMClass(tag);
+ if (typeof klass == "undefined") continue;
+ copy(T[tag], klass.prototype);
+ }
+ }
+};
-var Toggle = new Object();
-Toggle.display = Element.toggle;
+var Toggle = { display: Element.toggle };
/*--------------------------------------------------------------------------*/
@@ -1392,13 +1872,13 @@ Abstract.Insertion.prototype = {
initialize: function(element, content) {
this.element = $(element);
this.content = content.stripScripts();
-
+
if (this.adjacency && this.element.insertAdjacentHTML) {
try {
this.element.insertAdjacentHTML(this.adjacency, this.content);
} catch (e) {
- var tagName = this.element.tagName.toLowerCase();
- if (tagName == 'tbody' || tagName == 'tr') {
+ var tagName = this.element.tagName.toUpperCase();
+ if (['TBODY', 'TR'].include(tagName)) {
this.insertContent(this.contentFromAnonymousTable());
} else {
throw e;
@@ -1410,9 +1890,9 @@ Abstract.Insertion.prototype = {
this.insertContent([this.range.createContextualFragment(this.content)]);
}
- setTimeout(function() {content.evalScripts()}, 10);
+ setTimeout(function() {content.evalScripts()}, 10);
},
-
+
contentFromAnonymousTable: function() {
var div = document.createElement('div');
div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
@@ -1427,7 +1907,7 @@ Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin')
initializeRange: function() {
this.range.setStartBefore(this.element);
},
-
+
insertContent: function(fragments) {
fragments.each((function(fragment) {
this.element.parentNode.insertBefore(fragment, this.element);
@@ -1441,7 +1921,7 @@ Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
this.range.selectNodeContents(this.element);
this.range.collapse(true);
},
-
+
insertContent: function(fragments) {
fragments.reverse(false).each((function(fragment) {
this.element.insertBefore(fragment, this.element.firstChild);
@@ -1455,7 +1935,7 @@ Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'),
this.range.selectNodeContents(this.element);
this.range.collapse(this.element);
},
-
+
insertContent: function(fragments) {
fragments.each((function(fragment) {
this.element.appendChild(fragment);
@@ -1468,10 +1948,10 @@ Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
initializeRange: function() {
this.range.setStartAfter(this.element);
},
-
+
insertContent: function(fragments) {
fragments.each((function(fragment) {
- this.element.parentNode.insertBefore(fragment,
+ this.element.parentNode.insertBefore(fragment,
this.element.nextSibling);
}).bind(this));
}
@@ -1490,225 +1970,818 @@ Element.ClassNames.prototype = {
return name.length > 0;
})._each(iterator);
},
-
+
set: function(className) {
this.element.className = className;
},
-
+
add: function(classNameToAdd) {
if (this.include(classNameToAdd)) return;
- this.set(this.toArray().concat(classNameToAdd).join(' '));
+ this.set($A(this).concat(classNameToAdd).join(' '));
},
-
+
remove: function(classNameToRemove) {
if (!this.include(classNameToRemove)) return;
- this.set(this.select(function(className) {
- return className != classNameToRemove;
- }).join(' '));
+ this.set($A(this).without(classNameToRemove).join(' '));
},
-
+
toString: function() {
- return this.toArray().join(' ');
+ return $A(this).join(' ');
}
-}
+};
Object.extend(Element.ClassNames.prototype, Enumerable);
+/* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
+ * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
+ * license. Please see http://www.yui-ext.com/ for more information. */
+var Selector = Class.create();
-var Field = {
- clear: function() {
- for (var i = 0; i < arguments.length; i++)
- $(arguments[i]).value = '';
+Selector.prototype = {
+ initialize: function(expression) {
+ this.expression = expression.strip();
+ this.compileMatcher();
},
- focus: function(element) {
- $(element).focus();
+ compileMatcher: function() {
+ // Selectors with namespaced attributes can't use the XPath version
+ if (Prototype.BrowserFeatures.XPath && !(/\[[\w-]*?:/).test(this.expression))
+ return this.compileXPathMatcher();
+
+ var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
+ c = Selector.criteria, le, p, m;
+
+ if (Selector._cache[e]) {
+ this.matcher = Selector._cache[e]; return;
+ }
+ this.matcher = ["this.matcher = function(root) {",
+ "var r = root, h = Selector.handlers, c = false, n;"];
+
+ while (e && le != e && (/\S/).test(e)) {
+ le = e;
+ for (var i in ps) {
+ p = ps[i];
+ if (m = e.match(p)) {
+ this.matcher.push(typeof c[i] == 'function' ? c[i](m) :
+ new Template(c[i]).evaluate(m));
+ e = e.replace(m[0], '');
+ break;
+ }
+ }
+ }
+
+ this.matcher.push("return h.unique(n);\n}");
+ eval(this.matcher.join('\n'));
+ Selector._cache[this.expression] = this.matcher;
},
- present: function() {
- for (var i = 0; i < arguments.length; i++)
- if ($(arguments[i]).value == '') return false;
- return true;
+ compileXPathMatcher: function() {
+ var e = this.expression, ps = Selector.patterns,
+ x = Selector.xpath, le, p, m;
+
+ if (Selector._cache[e]) {
+ this.xpath = Selector._cache[e]; return;
+ }
+
+ this.matcher = ['.//*'];
+ while (e && le != e && (/\S/).test(e)) {
+ le = e;
+ for (var i in ps) {
+ if (m = e.match(ps[i])) {
+ this.matcher.push(typeof x[i] == 'function' ? x[i](m) :
+ new Template(x[i]).evaluate(m));
+ e = e.replace(m[0], '');
+ break;
+ }
+ }
+ }
+
+ this.xpath = this.matcher.join('');
+ Selector._cache[this.expression] = this.xpath;
},
- select: function(element) {
- $(element).select();
+ findElements: function(root) {
+ root = root || document;
+ if (this.xpath) return document._getElementsByXPath(this.xpath, root);
+ return this.matcher(root);
},
- activate: function(element) {
- element = $(element);
- element.focus();
- if (element.select)
- element.select();
+ match: function(element) {
+ return this.findElements(document).include(element);
+ },
+
+ toString: function() {
+ return this.expression;
+ },
+
+ inspect: function() {
+ return "#<Selector:" + this.expression.inspect() + ">";
}
-}
+};
+
+Object.extend(Selector, {
+ _cache: {},
+
+ xpath: {
+ descendant: "//*",
+ child: "/*",
+ adjacent: "/following-sibling::*[1]",
+ laterSibling: '/following-sibling::*',
+ tagName: function(m) {
+ if (m[1] == '*') return '';
+ return "[local-name()='" + m[1].toLowerCase() +
+ "' or local-name()='" + m[1].toUpperCase() + "']";
+ },
+ className: "[contains(concat(' ', @class, ' '), ' #{1} ')]",
+ id: "[@id='#{1}']",
+ attrPresence: "[@#{1}]",
+ attr: function(m) {
+ m[3] = m[5] || m[6];
+ return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
+ },
+ pseudo: function(m) {
+ var h = Selector.xpath.pseudos[m[1]];
+ if (!h) return '';
+ if (typeof h === 'function') return h(m);
+ return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
+ },
+ operators: {
+ '=': "[@#{1}='#{3}']",
+ '!=': "[@#{1}!='#{3}']",
+ '^=': "[starts-with(@#{1}, '#{3}')]",
+ '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
+ '*=': "[contains(@#{1}, '#{3}')]",
+ '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
+ '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
+ },
+ pseudos: {
+ 'first-child': '[not(preceding-sibling::*)]',
+ 'last-child': '[not(following-sibling::*)]',
+ 'only-child': '[not(preceding-sibling::* or following-sibling::*)]',
+ 'empty': "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
+ 'checked': "[@checked]",
+ 'disabled': "[@disabled]",
+ 'enabled': "[not(@disabled)]",
+ 'not': function(m) {
+ if (!m[6]) return '';
+ var p = Selector.patterns, x = Selector.xpath;
+ for (var i in p) {
+ if (mm = m[6].match(p[i])) {
+ var ss = typeof x[i] == 'function' ? x[i](mm) : new Template(x[i]).evaluate(mm);
+ m[6] = ss.substring(1, ss.length - 1);
+ break;
+ }
+ }
+ return "[not(" + m[6] + ")]";
+ },
+ 'nth-child': function(m) {
+ return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
+ },
+ 'nth-last-child': function(m) {
+ return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
+ },
+ 'nth-of-type': function(m) {
+ return Selector.xpath.pseudos.nth("position() ", m);
+ },
+ 'nth-last-of-type': function(m) {
+ return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
+ },
+ 'first-of-type': function(m) {
+ m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
+ },
+ 'last-of-type': function(m) {
+ m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
+ },
+ 'only-of-type': function(m) {
+ var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
+ },
+ nth: function(predicate, m) {
+ var mm, formula = m[6];
+ if (formula == 'even') formula = '2n+0';
+ if (formula == 'odd') formula = '2n+1';
+ if (mm = formula.match(/^(\d+)$/)) // digit only
+ predicate += "= " + mm[1];
+ if (mm = formula.match(/^(\d+)?n(\+(\d+))?/)) { // an+b
+ var a = mm[1] ? Number(mm[1]) : 1;
+ var b = mm[3] ? Number(mm[3]) : 0;
+ predicate += "mod " + a + " = " + b;
+ }
+ return "[" + predicate + "]";
+ }
+ }
+ },
-/*--------------------------------------------------------------------------*/
+ criteria: {
+ tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;',
+ className: 'n = h.className(n, r, "#{1}", c); c = false;',
+ id: 'n = h.id(n, r, "#{1}", c); c = false;',
+ attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;',
+ attr: function(m) {
+ m[3] = m[5] || m[6];
+ return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m);
+ },
+ pseudo: 'n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;',
+ descendant: 'c = "descendant";',
+ child: 'c = "child";',
+ adjacent: 'c = "adjacent";',
+ laterSibling: 'c = "laterSibling";'
+ },
+
+ patterns: {
+ // combinators must be listed first
+ // (and descendant needs to be last combinator)
+ laterSibling: /^\s*~\s*/,
+ child: /^\s*>\s*/,
+ adjacent: /^\s*\+\s*/,
+ descendant: /^\s/,
+
+ // selectors follow
+ tagName: /^\s*(\*|[\w\-]+)(\b|$)?/,
+ id: /^#([\w\-\*]+)(\b|$)/,
+ className: /^\.([\w\-\*]+)(\b|$)/,
+ pseudo: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$)/,
+ attrPresence: /^\[([\w]+)\]/,
+ attr: new RegExp(/\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\]]*?)\4|([^'"][^\]]*?)))?\]/)
+ },
+
+ handlers: {
+ // UTILITY FUNCTIONS
+ // joins two collections
+ concat: function(a, b) {
+ for (var i = 0, node; node = b[i]; i++)
+ a.push(node);
+ return a;
+ },
+
+ // marks an array of nodes for counting
+ mark: function(nodes) {
+ for (var i = 0, node; node = nodes[i]; i++)
+ node._counted = true;
+ return nodes;
+ },
+
+ unmark: function(nodes) {
+ for (var i = 0, node; node = nodes[i]; i++)
+ node._counted = undefined;
+ return nodes;
+ },
+
+ // mark each child node with its position (for nth calls)
+ // "ofType" flag indicates whether we're indexing for nth-of-type
+ // rather than nth-child
+ index: function(parentNode, reverse, ofType) {
+ parentNode._counted = true;
+ if (reverse) {
+ for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
+ node = nodes[i];
+ if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
+ }
+ } else {
+ for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
+ if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
+ }
+ },
+
+ // filters out duplicates and extends all nodes
+ unique: function(nodes) {
+ if (nodes.length == 0) return nodes;
+ var results = [], n;
+ for (var i = 0, l = nodes.length; i < l; i++)
+ if (!(n = nodes[i])._counted) {
+ n._counted = true;
+ results.push(Element.extend(n));
+ }
+ return Selector.handlers.unmark(results);
+ },
+
+ // COMBINATOR FUNCTIONS
+ descendant: function(nodes) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ h.concat(results, Element.descendants(node));
+ return results;
+ },
+
+ child: function(nodes) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ h.concat(results, Element.immediateDescendants(node));
+ return results;
+ },
+
+ adjacent: function(nodes) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ var next = this.nextElementSibling(node);
+ if (next) results.push(next);
+ }
+ return results;
+ },
+
+ laterSibling: function(nodes) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ h.concat(results, Element.nextSiblings(node));
+ return results;
+ },
+
+ nextElementSibling: function(node) {
+ while (node = node.nextSibling)
+ if (node.nodeType == 1) return node;
+ return null;
+ },
+
+ previousElementSibling: function(node) {
+ while (node = node.previousSibling)
+ if (node.nodeType == 1) return node;
+ return null;
+ },
+
+ // TOKEN FUNCTIONS
+ tagName: function(nodes, root, tagName, combinator) {
+ tagName = tagName.toUpperCase();
+ var results = [], h = Selector.handlers;
+ if (nodes) {
+ if (combinator) {
+ // fastlane for ordinary descendant combinators
+ if (combinator == "descendant") {
+ for (var i = 0, node; node = nodes[i]; i++)
+ h.concat(results, node.getElementsByTagName(tagName));
+ return results;
+ } else nodes = this[combinator](nodes);
+ if (tagName == "*") return nodes;
+ }
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (node.tagName.toUpperCase() == tagName) results.push(node);
+ return results;
+ } else return root.getElementsByTagName(tagName);
+ },
+
+ id: function(nodes, root, id, combinator) {
+ var targetNode = $(id), h = Selector.handlers;
+ if (!nodes && root == document) return targetNode ? [targetNode] : [];
+ if (nodes) {
+ if (combinator) {
+ if (combinator == 'child') {
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (targetNode.parentNode == node) return [targetNode];
+ } else if (combinator == 'descendant') {
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (Element.descendantOf(targetNode, node)) return [targetNode];
+ } else if (combinator == 'adjacent') {
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (Selector.handlers.previousElementSibling(targetNode) == node)
+ return [targetNode];
+ } else nodes = h[combinator](nodes);
+ }
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (node == targetNode) return [targetNode];
+ return [];
+ }
+ return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
+ },
+
+ className: function(nodes, root, className, combinator) {
+ if (nodes && combinator) nodes = this[combinator](nodes);
+ return Selector.handlers.byClassName(nodes, root, className);
+ },
+
+ byClassName: function(nodes, root, className) {
+ if (!nodes) nodes = Selector.handlers.descendant([root]);
+ var needle = ' ' + className + ' ';
+ for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
+ nodeClassName = node.className;
+ if (nodeClassName.length == 0) continue;
+ if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
+ results.push(node);
+ }
+ return results;
+ },
+
+ attrPresence: function(nodes, root, attr) {
+ var results = [];
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (Element.hasAttribute(node, attr)) results.push(node);
+ return results;
+ },
+
+ attr: function(nodes, root, attr, value, operator) {
+ var handler = Selector.operators[operator], results = [];
+ for (var i = 0, node; node = nodes[i]; i++) {
+ var nodeValue = Element.readAttribute(node, attr);
+ if (nodeValue === null) continue;
+ if (handler(nodeValue, value)) results.push(node);
+ }
+ return results;
+ },
-var Form = {
- serialize: function(form) {
- var elements = Form.getElements($(form));
- var queryComponents = new Array();
+ pseudo: function(nodes, name, value, root, combinator) {
+ if (combinator) nodes = this[combinator](nodes);
+ return Selector.pseudos[name](nodes, value, root);
+ }
+ },
- for (var i = 0; i < elements.length; i++) {
- var queryComponent = Form.Element.serialize(elements[i]);
- if (queryComponent)
- queryComponents.push(queryComponent);
+ pseudos: {
+ 'first-child': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ if (Selector.handlers.previousElementSibling(node)) continue;
+ results.push(node);
+ }
+ return results;
+ },
+ 'last-child': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ if (Selector.handlers.nextElementSibling(node)) continue;
+ results.push(node);
+ }
+ return results;
+ },
+ 'only-child': function(nodes, value, root) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
+ results.push(node);
+ return results;
+ },
+ 'nth-child': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root);
+ },
+ 'nth-last-child': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root, true);
+ },
+ 'nth-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root, false, true);
+ },
+ 'nth-last-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root, true, true);
+ },
+ 'first-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, "1", root, false, true);
+ },
+ 'last-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, "1", root, true, true);
+ },
+ 'only-of-type': function(nodes, formula, root) {
+ var p = Selector.pseudos;
+ return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
+ },
+
+ // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
+ nth: function(nodes, formula, root, reverse, ofType) {
+ if (formula == 'even') formula = '2n+0';
+ if (formula == 'odd') formula = '2n+1';
+ var h = Selector.handlers, results = [], indexed = [], m;
+ h.mark(nodes);
+ for (var i = 0, node; node = nodes[i]; i++) {
+ if (!node.parentNode._counted) {
+ h.index(node.parentNode, reverse, ofType);
+ indexed.push(node.parentNode);
+ }
+ }
+ if (formula.match(/^\d+$/)) { // just a number
+ formula = Number(formula);
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (node.nodeIndex == formula) results.push(node);
+ } else if (m = formula.match(/^(\d+)?n(\+(\d+))?$/)) { // an+b
+ var a = m[1] ? Number(m[1]) : 1;
+ var b = m[3] ? Number(m[3]) : 0;
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (node.nodeIndex % a == b) results.push(node);
+ }
+ h.unmark(nodes);
+ h.unmark(indexed);
+ return results;
+ },
+
+ 'empty': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ // IE treats comments as element nodes
+ if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
+ results.push(node);
+ }
+ return results;
+ },
+
+ 'not': function(nodes, selector, root) {
+ var h = Selector.handlers, exclusions = $A(nodes), selectorType, m;
+ for (var i in Selector.patterns) {
+ if (m = selector.match(Selector.patterns[i])) {
+ selectorType = i; break;
+ }
+ }
+ switch(selectorType) {
+ case 'className': case 'tagName': case 'id': // fallthroughs
+ case 'attrPresence': exclusions = h[selectorType](exclusions, root, m[1], false); break;
+ case 'attr': m[3] = m[5] || m[6]; exclusions = h.attr(exclusions, root, m[1], m[3], m[2]); break;
+ case 'pseudo': exclusions = h.pseudo(exclusions, m[1], m[6], root, false); break;
+ // only 'simple selectors' (one token) allowed in a :not clause
+ default: throw 'Illegal selector in :not clause.';
+ }
+ h.mark(exclusions);
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (!node._counted) results.push(node);
+ h.unmark(exclusions);
+ return results;
+ },
+
+ 'enabled': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (!node.disabled) results.push(node);
+ return results;
+ },
+
+ 'disabled': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (node.disabled) results.push(node);
+ return results;
+ },
+
+ 'checked': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (node.checked) results.push(node);
+ return results;
}
+ },
- return queryComponents.join('&');
+ operators: {
+ '=': function(nv, v) { return nv == v; },
+ '!=': function(nv, v) { return nv != v; },
+ '^=': function(nv, v) { return nv.startsWith(v); },
+ '$=': function(nv, v) { return nv.endsWith(v); },
+ '*=': function(nv, v) { return nv.include(v); },
+ '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
+ '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
},
- getElements: function(form) {
- form = $(form);
- var elements = new Array();
+ matchElements: function(elements, expression) {
+ var matches = new Selector(expression).findElements(), h = Selector.handlers;
+ h.mark(matches);
+ for (var i = 0, results = [], element; element = elements[i]; i++)
+ if (element._counted) results.push(element);
+ h.unmark(matches);
+ return results;
+ },
- for (var tagName in Form.Element.Serializers) {
- var tagElements = form.getElementsByTagName(tagName);
- for (var j = 0; j < tagElements.length; j++)
- elements.push(tagElements[j]);
+ findElement: function(elements, expression, index) {
+ if (typeof expression == 'number') {
+ index = expression; expression = false;
}
- return elements;
+ return Selector.matchElements(elements, expression || '*')[index || 0];
+ },
+
+ findChildElements: function(element, expressions) {
+ var exprs = expressions.join(','), expressions = [];
+ exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
+ expressions.push(m[1].strip());
+ });
+ var results = [], h = Selector.handlers;
+ for (var i = 0, l = expressions.length, selector; i < l; i++) {
+ selector = new Selector(expressions[i].strip());
+ h.concat(results, selector.findElements(element));
+ }
+ return (l > 1) ? h.unique(results) : results;
+ }
+});
+
+function $$() {
+ return Selector.findChildElements(document, $A(arguments));
+}
+var Form = {
+ reset: function(form) {
+ $(form).reset();
+ return form;
+ },
+
+ serializeElements: function(elements, getHash) {
+ var data = elements.inject({}, function(result, element) {
+ if (!element.disabled && element.name) {
+ var key = element.name, value = $(element).getValue();
+ if (value != null) {
+ if (key in result) {
+ if (result[key].constructor != Array) result[key] = [result[key]];
+ result[key].push(value);
+ }
+ else result[key] = value;
+ }
+ }
+ return result;
+ });
+
+ return getHash ? data : Hash.toQueryString(data);
+ }
+};
+
+Form.Methods = {
+ serialize: function(form, getHash) {
+ return Form.serializeElements(Form.getElements(form), getHash);
+ },
+
+ getElements: function(form) {
+ return $A($(form).getElementsByTagName('*')).inject([],
+ function(elements, child) {
+ if (Form.Element.Serializers[child.tagName.toLowerCase()])
+ elements.push(Element.extend(child));
+ return elements;
+ }
+ );
},
getInputs: function(form, typeName, name) {
form = $(form);
var inputs = form.getElementsByTagName('input');
- if (!typeName && !name)
- return inputs;
+ if (!typeName && !name) return $A(inputs).map(Element.extend);
- var matchingInputs = new Array();
- for (var i = 0; i < inputs.length; i++) {
+ for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
var input = inputs[i];
- if ((typeName && input.type != typeName) ||
- (name && input.name != name))
+ if ((typeName && input.type != typeName) || (name && input.name != name))
continue;
- matchingInputs.push(input);
+ matchingInputs.push(Element.extend(input));
}
return matchingInputs;
},
disable: function(form) {
- var elements = Form.getElements(form);
- for (var i = 0; i < elements.length; i++) {
- var element = elements[i];
+ form = $(form);
+ form.getElements().each(function(element) {
element.blur();
element.disabled = 'true';
- }
+ });
+ return form;
},
enable: function(form) {
- var elements = Form.getElements(form);
- for (var i = 0; i < elements.length; i++) {
- var element = elements[i];
+ form = $(form);
+ form.getElements().each(function(element) {
element.disabled = '';
- }
+ });
+ return form;
},
findFirstElement: function(form) {
- return Form.getElements(form).find(function(element) {
+ return $(form).getElements().find(function(element) {
return element.type != 'hidden' && !element.disabled &&
['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
});
},
focusFirstElement: function(form) {
- Field.activate(Form.findFirstElement(form));
+ form = $(form);
+ form.findFirstElement().activate();
+ return form;
},
- reset: function(form) {
- $(form).reset();
+ request: function(form, options) {
+ form = $(form), options = Object.clone(options || {});
+
+ var params = options.parameters;
+ options.parameters = form.serialize(true);
+
+ if (params) {
+ if (typeof params == 'string') params = params.toQueryParams();
+ Object.extend(options.parameters, params);
+ }
+
+ if (form.hasAttribute('method') && !options.method)
+ options.method = form.method;
+
+ return new Ajax.Request(form.action, options);
}
}
+Object.extend(Form, Form.Methods);
+
+/*--------------------------------------------------------------------------*/
+
Form.Element = {
+ focus: function(element) {
+ $(element).focus();
+ return element;
+ },
+
+ select: function(element) {
+ $(element).select();
+ return element;
+ }
+}
+
+Form.Element.Methods = {
serialize: function(element) {
element = $(element);
+ if (!element.disabled && element.name) {
+ var value = element.getValue();
+ if (value != undefined) {
+ var pair = {};
+ pair[element.name] = value;
+ return Hash.toQueryString(pair);
+ }
+ }
+ return '';
+ },
+
+ getValue: function(element) {
+ element = $(element);
var method = element.tagName.toLowerCase();
- var parameter = Form.Element.Serializers[method](element);
+ return Form.Element.Serializers[method](element);
+ },
- if (parameter) {
- var key = encodeURIComponent(parameter[0]);
- if (key.length == 0) return;
+ clear: function(element) {
+ $(element).value = '';
+ return element;
+ },
- if (parameter[1].constructor != Array)
- parameter[1] = [parameter[1]];
+ present: function(element) {
+ return $(element).value != '';
+ },
- return parameter[1].map(function(value) {
- return key + '=' + encodeURIComponent(value);
- }).join('&');
- }
+ activate: function(element) {
+ element = $(element);
+ try {
+ element.focus();
+ if (element.select && (element.tagName.toLowerCase() != 'input' ||
+ !['button', 'reset', 'submit'].include(element.type)))
+ element.select();
+ } catch (e) {}
+ return element;
},
- getValue: function(element) {
+ disable: function(element) {
element = $(element);
- var method = element.tagName.toLowerCase();
- var parameter = Form.Element.Serializers[method](element);
+ element.blur();
+ element.disabled = true;
+ return element;
+ },
- if (parameter)
- return parameter[1];
+ enable: function(element) {
+ element = $(element);
+ element.disabled = false;
+ return element;
}
}
+Object.extend(Form.Element, Form.Element.Methods);
+Object.extend(Element.Methods.ByTag, {
+ "FORM": Object.clone(Form.Methods),
+ "INPUT": Object.clone(Form.Element.Methods),
+ "SELECT": Object.clone(Form.Element.Methods),
+ "TEXTAREA": Object.clone(Form.Element.Methods)
+});
+
+/*--------------------------------------------------------------------------*/
+
+var Field = Form.Element;
+var $F = Form.Element.getValue;
+
+/*--------------------------------------------------------------------------*/
+
Form.Element.Serializers = {
input: function(element) {
- if(typeof(element.type) == "undefined")
- return false;
switch (element.type.toLowerCase()) {
- case 'submit':
- case 'hidden':
- case 'password':
- case 'text':
- return Form.Element.Serializers.textarea(element);
case 'checkbox':
case 'radio':
return Form.Element.Serializers.inputSelector(element);
+ default:
+ return Form.Element.Serializers.textarea(element);
}
- return false;
},
inputSelector: function(element) {
- if (element.checked)
- return [element.name, element.value];
+ return element.checked ? element.value : null;
},
textarea: function(element) {
- return [element.name, element.value];
+ return element.value;
},
select: function(element) {
- return Form.Element.Serializers[element.type == 'select-one' ?
+ return this[element.type == 'select-one' ?
'selectOne' : 'selectMany'](element);
},
selectOne: function(element) {
- var value = '', opt, index = element.selectedIndex;
- if (index >= 0) {
- opt = element.options[index];
- value = opt.value || opt.text;
- }
- return [element.name, value];
+ var index = element.selectedIndex;
+ return index >= 0 ? this.optionValue(element.options[index]) : null;
},
selectMany: function(element) {
- var value = [];
- for (var i = 0; i < element.length; i++) {
+ var values, length = element.length;
+ if (!length) return null;
+
+ for (var i = 0, values = []; i < length; i++) {
var opt = element.options[i];
- if (opt.selected)
- value.push(opt.value || opt.text);
+ if (opt.selected) values.push(this.optionValue(opt));
}
- return [element.name, value];
+ return values;
+ },
+
+ optionValue: function(opt) {
+ // extend element because hasAttribute may not be native
+ return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
}
}
/*--------------------------------------------------------------------------*/
-var $F = Form.Element.getValue;
-
-/*--------------------------------------------------------------------------*/
-
Abstract.TimedObserver = function() {}
Abstract.TimedObserver.prototype = {
initialize: function(element, frequency, callback) {
@@ -1726,7 +2799,9 @@ Abstract.TimedObserver.prototype = {
onTimerEvent: function() {
var value = this.getValue();
- if (this.lastValue != value) {
+ var changed = ('string' == typeof this.lastValue && 'string' == typeof value
+ ? this.lastValue != value : String(this.lastValue) != String(value));
+ if (changed) {
this.callback(this.element, value);
this.lastValue = value;
}
@@ -1771,9 +2846,7 @@ Abstract.EventObserver.prototype = {
},
registerFormCallbacks: function() {
- var elements = Form.getElements(this.element);
- for (var i = 0; i < elements.length; i++)
- this.registerCallback(elements[i]);
+ Form.getElements(this.element).each(this.registerCallback.bind(this));
},
registerCallback: function(element) {
@@ -1783,11 +2856,7 @@ Abstract.EventObserver.prototype = {
case 'radio':
Event.observe(element, 'click', this.onElementEvent.bind(this));
break;
- case 'password':
- case 'text':
- case 'textarea':
- case 'select-one':
- case 'select-multiple':
+ default:
Event.observe(element, 'change', this.onElementEvent.bind(this));
break;
}
@@ -1808,9 +2877,6 @@ Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
return Form.serialize(this.element);
}
});
-
-
-
if (!window.Event) {
var Event = new Object();
}
@@ -1825,7 +2891,10 @@ Object.extend(Event, {
KEY_RIGHT: 39,
KEY_DOWN: 40,
KEY_DELETE: 46,
- KEY_SPACEBAR: 32,
+ KEY_HOME: 36,
+ KEY_END: 35,
+ KEY_PAGEUP: 33,
+ KEY_PAGEDOWN: 34,
element: function(event) {
return event.target || event.srcElement;
@@ -1837,19 +2906,19 @@ Object.extend(Event, {
},
pointerX: function(event) {
- return event.pageX || (event.clientX +
+ return event.pageX || (event.clientX +
(document.documentElement.scrollLeft || document.body.scrollLeft));
},
pointerY: function(event) {
- return event.pageY || (event.clientY +
+ return event.pageY || (event.clientY +
(document.documentElement.scrollTop || document.body.scrollTop));
},
stop: function(event) {
- if (event.preventDefault) {
- event.preventDefault();
- event.stopPropagation();
+ if (event.preventDefault) {
+ event.preventDefault();
+ event.stopPropagation();
} else {
event.returnValue = false;
event.cancelBubble = true;
@@ -1867,7 +2936,7 @@ Object.extend(Event, {
},
observers: false,
-
+
_observeAndCache: function(element, name, observer, useCapture) {
if (!this.observers) this.observers = [];
if (element.addEventListener) {
@@ -1878,10 +2947,10 @@ Object.extend(Event, {
element.attachEvent('on' + name, observer);
}
},
-
+
unloadCache: function() {
if (!Event.observers) return;
- for (var i = 0; i < Event.observers.length; i++) {
+ for (var i = 0, length = Event.observers.length; i < length; i++) {
Event.stopObserving.apply(this, Event.observers[i]);
Event.observers[i][0] = null;
}
@@ -1889,166 +2958,53 @@ Object.extend(Event, {
},
observe: function(element, name, observer, useCapture) {
- var element = $(element);
+ element = $(element);
useCapture = useCapture || false;
-
+
if (name == 'keypress' &&
- (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
- || element.attachEvent))
+ (Prototype.Browser.WebKit || element.attachEvent))
name = 'keydown';
-
- this._observeAndCache(element, name, observer, useCapture);
+
+ Event._observeAndCache(element, name, observer, useCapture);
},
stopObserving: function(element, name, observer, useCapture) {
- var element = $(element);
+ element = $(element);
useCapture = useCapture || false;
-
+
if (name == 'keypress' &&
- (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
- || element.detachEvent))
+ (Prototype.Browser.WebKit || element.attachEvent))
name = 'keydown';
-
+
if (element.removeEventListener) {
element.removeEventListener(name, observer, useCapture);
} else if (element.detachEvent) {
- element.detachEvent('on' + name, observer);
+ try {
+ element.detachEvent('on' + name, observer);
+ } catch (e) {}
}
}
});
/* prevent memory leaks in IE */
-if (navigator.appVersion.match(/\bMSIE\b/))
+if (Prototype.Browser.IE)
Event.observe(window, 'unload', Event.unloadCache, false);
-
-
-/**
- * @class Event extensions.
- */
-Object.extend(Event,
-{
- /**
- * Register a function to be executed when the page is loaded.
- * Note that the page is only loaded if all resources (e.g. images)
- * are loaded.
- *
- * Example: Show an alert box with message "Page Loaded!" when the
- * page finished loading.
- * <code>
- * Event.OnLoad(function(){ alert("Page Loaded!"); });
- * </code>
- *
- * @param {Function} function to execute when page is loaded.
- */
- OnLoad : function (fn)
- {
- // opera onload is in document, not window
- var w = document.addEventListener &&
- !window.addEventListener ? document : window;
- Event.observe(w,'load',fn);
- },
-
- /**
- * @param {Event} a keyboard event
- * @return {Number} the Unicode character code generated by the key
- * that was struck.
- */
- keyCode : function(e)
- {
- return e.keyCode != null ? e.keyCode : e.charCode
- },
-
- /**
- * @param {String} event type or event name.
- * @return {Boolean} true if event type is of HTMLEvent, false
- * otherwise
- */
- isHTMLEvent : function(type)
- {
- var events = ['abort', 'blur', 'change', 'error', 'focus',
- 'load', 'reset', 'resize', 'scroll', 'select',
- 'submit', 'unload'];
- return events.include(type);
- },
-
- /**
- * @param {String} event type or event name
- * @return {Boolean} true if event type is of MouseEvent,
- * false otherwise
- */
- isMouseEvent : function(type)
- {
- var events = ['click', 'mousedown', 'mousemove', 'mouseout',
- 'mouseover', 'mouseup'];
- return events.include(type);
- },
-
- /**
- * Dispatch the DOM event of a given <tt>type</tt> on a DOM
- * <tt>element</tt>. Only HTMLEvent and MouseEvent can be
- * dispatched, keyboard events or UIEvent can not be dispatch
- * via javascript consistently.
- * For the "submit" event the submit() method is called.
- * @param {Object} element id string or a DOM element.
- * @param {String} event type to dispatch.
- */
- fireEvent : function(element,type,canBubble)
- {
- canBubble = (typeof(canBubble) == undefined) ? true : canBubble;
- element = $(element);
- if(type == "submit")
- return element.submit();
- if(document.createEvent)
- {
- if(Event.isHTMLEvent(type))
- {
- var event = document.createEvent('HTMLEvents');
- event.initEvent(type, canBubble, true);
- }
- else if(Event.isMouseEvent(type))
- {
- var event = document.createEvent('MouseEvents');
- if (event.initMouseEvent)
- {
- event.initMouseEvent(type,canBubble,true,
- document.defaultView, 1, 0, 0, 0, 0, false,
- false, false, false, 0, null);
- }
- else
- {
- // Safari
- // TODO we should be initialising other mouse-event related attributes here
- event.initEvent(type, canBubble, true);
- }
- }
- element.dispatchEvent(event);
- }
- else if(document.createEventObject)
- {
- var evObj = document.createEventObject();
- element.fireEvent('on'+type, evObj);
- }
- else if(typeof(element['on'+type]) == "function")
- element['on'+type]();
- }
-});
-
var Position = {
// set to true if needed, warning: firefox performance problems
// NOT neeeded for page scrolling, only if draggable contained in
// scrollable elements
- includeScrollOffsets: false,
+ includeScrollOffsets: false,
// must be called before calling withinIncludingScrolloffset, every time the
// page is scrolled
prepare: function() {
- this.deltaX = window.pageXOffset
- || document.documentElement.scrollLeft
- || document.body.scrollLeft
+ this.deltaX = window.pageXOffset
+ || document.documentElement.scrollLeft
+ || document.body.scrollLeft
|| 0;
- this.deltaY = window.pageYOffset
- || document.documentElement.scrollTop
- || document.body.scrollTop
+ this.deltaY = window.pageYOffset
+ || document.documentElement.scrollTop
+ || document.body.scrollTop
|| 0;
},
@@ -2056,7 +3012,7 @@ var Position = {
var valueT = 0, valueL = 0;
do {
valueT += element.scrollTop || 0;
- valueL += element.scrollLeft || 0;
+ valueL += element.scrollLeft || 0;
element = element.parentNode;
} while (element);
return [valueL, valueT];
@@ -2079,13 +3035,14 @@ var Position = {
valueL += element.offsetLeft || 0;
element = element.offsetParent;
if (element) {
- p = Element.getStyle(element, 'position');
+ if(element.tagName=='BODY') break;
+ var p = Element.getStyle(element, 'position');
if (p == 'relative' || p == 'absolute') break;
}
} while (element);
return [valueL, valueT];
},
-
+
offsetParent: function(element) {
if (element.offsetParent) return element.offsetParent;
if (element == document.body) return element;
@@ -2096,7 +3053,7 @@ var Position = {
return document.body;
},
-
+
// caches x/y coordinate pair to use with overlap
within: function(element, x, y) {
if (this.includeScrollOffsets)
@@ -2107,7 +3064,7 @@ var Position = {
return (y >= this.offset[1] &&
y < this.offset[1] + element.offsetHeight &&
- x >= this.offset[0] &&
+ x >= this.offset[0] &&
x < this.offset[0] + element.offsetWidth);
},
@@ -2120,32 +3077,21 @@ var Position = {
return (this.ycomp >= this.offset[1] &&
this.ycomp < this.offset[1] + element.offsetHeight &&
- this.xcomp >= this.offset[0] &&
+ this.xcomp >= this.offset[0] &&
this.xcomp < this.offset[0] + element.offsetWidth);
},
// within must be called directly before
- overlap: function(mode, element) {
- if (!mode) return 0;
- if (mode == 'vertical')
- return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
+ overlap: function(mode, element) {
+ if (!mode) return 0;
+ if (mode == 'vertical')
+ return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
element.offsetHeight;
if (mode == 'horizontal')
- return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
+ return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
element.offsetWidth;
},
- clone: function(source, target) {
- source = $(source);
- target = $(target);
- target.style.position = 'absolute';
- var offsets = this.cumulativeOffset(source);
- target.style.top = offsets[1] + 'px';
- target.style.left = offsets[0] + 'px';
- target.style.width = source.offsetWidth + 'px';
- target.style.height = source.offsetHeight + 'px';
- },
-
page: function(forElement) {
var valueT = 0, valueL = 0;
@@ -2155,15 +3101,17 @@ var Position = {
valueL += element.offsetLeft || 0;
// Safari fix
- if (element.offsetParent==document.body)
+ if (element.offsetParent == document.body)
if (Element.getStyle(element,'position')=='absolute') break;
} while (element = element.offsetParent);
element = forElement;
do {
- valueT -= element.scrollTop || 0;
- valueL -= element.scrollLeft || 0;
+ if (!window.opera || element.tagName=='BODY') {
+ valueT -= element.scrollTop || 0;
+ valueL -= element.scrollLeft || 0;
+ }
} while (element = element.parentNode);
return [valueL, valueT];
@@ -2187,7 +3135,7 @@ var Position = {
target = $(target);
var delta = [0, 0];
var parent = null;
- // delta [0,0] will do fine with position: fixed elements,
+ // delta [0,0] will do fine with position: fixed elements,
// position:absolute needs offsetParent deltas
if (Element.getStyle(target,'position') == 'absolute') {
parent = Position.offsetParent(target);
@@ -2197,7 +3145,7 @@ var Position = {
// correct by body offsets (fixes Safari)
if (parent == document.body) {
delta[0] -= document.body.offsetLeft;
- delta[1] -= document.body.offsetTop;
+ delta[1] -= document.body.offsetTop;
}
// set position
@@ -2224,10 +3172,10 @@ var Position = {
element._originalHeight = element.style.height;
element.style.position = 'absolute';
- element.style.top = top + 'px';;
- element.style.left = left + 'px';;
- element.style.width = width + 'px';;
- element.style.height = height + 'px';;
+ element.style.top = top + 'px';
+ element.style.left = left + 'px';
+ element.style.width = width + 'px';
+ element.style.height = height + 'px';
},
relativize: function(element) {
@@ -2249,7 +3197,7 @@ var Position = {
// Safari returns margins on body which is incorrect if the child is absolutely
// positioned. For performance reasons, redefine Position.cumulativeOffset for
// KHTML/WebKit only.
-if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
+if (Prototype.Browser.WebKit) {
Position.cumulativeOffset = function(element) {
var valueT = 0, valueL = 0;
do {
@@ -2257,132 +3205,22 @@ if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
valueL += element.offsetLeft || 0;
if (element.offsetParent == document.body)
if (Element.getStyle(element, 'position') == 'absolute') break;
-
+
element = element.offsetParent;
} while (element);
-
- return [valueL, valueT];
- }
-}
-
-
-
-
-var Selector = Class.create();
-Selector.prototype = {
- initialize: function(expression) {
- this.params = {classNames: []};
- this.expression = expression.toString().strip();
- this.parseExpression();
- this.compileMatcher();
- },
-
- parseExpression: function() {
- function abort(message) { throw 'Parse error in selector: ' + message; }
-
- if (this.expression == '') abort('empty expression');
-
- var params = this.params, expr = this.expression, match, modifier, clause, rest;
- while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) {
- params.attributes = params.attributes || [];
- params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''});
- expr = match[1];
- }
-
- if (expr == '*') return this.params.wildcard = true;
-
- while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) {
- modifier = match[1], clause = match[2], rest = match[3];
- switch (modifier) {
- case '#': params.id = clause; break;
- case '.': params.classNames.push(clause); break;
- case '':
- case undefined: params.tagName = clause.toUpperCase(); break;
- default: abort(expr.inspect());
- }
- expr = rest;
- }
-
- if (expr.length > 0) abort(expr.inspect());
- },
-
- buildMatchExpression: function() {
- var params = this.params, conditions = [], clause;
-
- if (params.wildcard)
- conditions.push('true');
- if (clause = params.id)
- conditions.push('element.id == ' + clause.inspect());
- if (clause = params.tagName)
- conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
- if ((clause = params.classNames).length > 0)
- for (var i = 0; i < clause.length; i++)
- conditions.push('Element.hasClassName(element, ' + clause[i].inspect() + ')');
- if (clause = params.attributes) {
- clause.each(function(attribute) {
- var value = 'element.getAttribute(' + attribute.name.inspect() + ')';
- var splitValueBy = function(delimiter) {
- return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';
- }
-
- switch (attribute.operator) {
- case '=': conditions.push(value + ' == ' + attribute.value.inspect()); break;
- case '~=': conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break;
- case '|=': conditions.push(
- splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect()
- ); break;
- case '!=': conditions.push(value + ' != ' + attribute.value.inspect()); break;
- case '':
- case undefined: conditions.push(value + ' != null'); break;
- default: throw 'Unknown operator ' + attribute.operator + ' in selector';
- }
- });
- }
-
- return conditions.join(' && ');
- },
-
- compileMatcher: function() {
- this.match = new Function('element', 'if (!element.tagName) return false; \
- return ' + this.buildMatchExpression());
- },
-
- findElements: function(scope) {
- var element;
-
- if (element = $(this.params.id))
- if (this.match(element))
- if (!scope || Element.childOf(element, scope))
- return [element];
-
- scope = (scope || document).getElementsByTagName(this.params.tagName || '*');
-
- var results = [];
- for (var i = 0; i < scope.length; i++)
- if (this.match(element = scope[i]))
- results.push(Element.extend(element));
-
- return results;
- },
- toString: function() {
- return this.expression;
+ return [valueL, valueT];
}
}
-function $$() {
- return $A(arguments).map(function(expression) {
- return expression.strip().split(/\s+/).inject([null], function(results, expr) {
- var selector = new Selector(expr);
- return results.map(selector.findElements.bind(selector)).flatten();
- });
- }).flatten();
-}
+Element.addMethods();
+// script.aculo.us builder.js v1.7.0, Fri Jan 19 19:16:36 CET 2007
-// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//
-// See scriptaculous.js for full license.
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
var Builder = {
NODEMAP: {
@@ -2415,7 +3253,7 @@ var Builder = {
var element = parentElement.firstChild || null;
// see if browser added wrapping tags
- if(element && (element.tagName != elementName))
+ if(element && (element.tagName.toUpperCase() != elementName))
element = element.getElementsByTagName(elementName)[0];
// fallback to createElement approach
@@ -2443,7 +3281,7 @@ var Builder = {
for(attr in arguments[1])
element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
}
- if(element.tagName != elementName)
+ if(element.tagName.toUpperCase() != elementName)
element = parentElement.getElementsByTagName(elementName)[0];
}
}
@@ -2457,10 +3295,16 @@ var Builder = {
_text: function(text) {
return document.createTextNode(text);
},
+
+ ATTR_MAP: {
+ 'className': 'class',
+ 'htmlFor': 'for'
+ },
+
_attributes: function(attributes) {
var attrs = [];
for(attribute in attributes)
- attrs.push((attribute=='className' ? 'class' : attribute) +
+ attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) +
'="' + attributes[attribute].toString().escapeHTML() + '"');
return attrs.join(" ");
},
@@ -2479,34 +3323,648 @@ var Builder = {
},
_isStringOrNumber: function(param) {
return(typeof param=='string' || typeof param=='number');
+ },
+ build: function(html) {
+ var element = this.node('div');
+ $(element).update(html.strip());
+ return element.down();
+ },
+ dump: function(scope) {
+ if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope
+
+ var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " +
+ "BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " +
+ "FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+
+ "KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+
+ "PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+
+ "TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/);
+
+ tags.each( function(tag){
+ scope[tag] = function() {
+ return Builder.node.apply(Builder, [tag].concat($A(arguments)));
+ }
+ });
}
}
+
-Object.extend(Builder,
+/**
+ * Similar to bindAsEventLister, but takes additional arguments.
+ */
+Function.prototype.bindEvent = function()
{
- exportTags:function()
+ var __method = this, args = $A(arguments), object = args.shift();
+ return function(event)
{
- var tags=["BUTTON","TT","PRE","H1","H2","H3","BR","CANVAS","HR","LABEL","TEXTAREA","FORM","STRONG","SELECT","OPTION","OPTGROUP","LEGEND","FIELDSET","P","UL","OL","LI","TD","TR","THEAD","TBODY","TFOOT","TABLE","TH","INPUT","SPAN","A","DIV","IMG", "CAPTION"];
- tags.each(function(tag)
- {
- window[tag]=function()
- {
- var args=$A(arguments);
- if(args.length==0)
- return Builder.node(tag,null);
- if(args.length==1)
- return Builder.node(tag,args[0]);
- if(args.length>1)
- return Builder.node(tag,args.shift(),args);
+ return __method.apply(object, [event || window.event].concat(args));
+ }
+}
+/**
+ * Creates a new function by copying function definition from
+ * the <tt>base</tt> and optional <tt>definition</tt>.
+ * @param function a base function to copy from.
+ * @param array additional definition
+ * @param function return a new function with definition from both
+ * <tt>base</tt> and <tt>definition</tt>.
+ */
+Class.extend = function(base, definition)
+{
+ var component = Class.create();
+ Object.extend(component.prototype, base.prototype);
+ if(definition)
+ Object.extend(component.prototype, definition);
+ return component;
+}
+
+/*
+ Base, version 1.0.2
+ Copyright 2006, Dean Edwards
+ License: http://creativecommons.org/licenses/LGPL/2.1/
+*/
+
+var Base = function() {
+ if (arguments.length) {
+ if (this == window) { // cast an object to this class
+ Base.prototype.extend.call(arguments[0], arguments.callee.prototype);
+ } else {
+ this.extend(arguments[0]);
+ }
+ }
+};
+
+Base.version = "1.0.2";
+
+Base.prototype = {
+ extend: function(source, value) {
+ var extend = Base.prototype.extend;
+ if (arguments.length == 2) {
+ var ancestor = this[source];
+ // overriding?
+ if ((ancestor instanceof Function) && (value instanceof Function) &&
+ ancestor.valueOf() != value.valueOf() && /\bbase\b/.test(value)) {
+ var method = value;
+ // var _prototype = this.constructor.prototype;
+ // var fromPrototype = !Base._prototyping && _prototype[source] == ancestor;
+ value = function() {
+ var previous = this.base;
+ // this.base = fromPrototype ? _prototype[source] : ancestor;
+ this.base = ancestor;
+ var returnValue = method.apply(this, arguments);
+ this.base = previous;
+ return returnValue;
+ };
+ // point to the underlying method
+ value.valueOf = function() {
+ return method;
+ };
+ value.toString = function() {
+ return String(method);
+ };
+ }
+ return this[source] = value;
+ } else if (source) {
+ var _prototype = {toSource: null};
+ // do the "toString" and other methods manually
+ var _protected = ["toString", "valueOf"];
+ // if we are prototyping then include the constructor
+ if (Base._prototyping) _protected[2] = "constructor";
+ for (var i = 0; (name = _protected[i]); i++) {
+ if (source[name] != _prototype[name]) {
+ extend.call(this, name, source[name]);
+ }
+ }
+ // copy each of the source object's properties to this object
+ for (var name in source) {
+ if (!_prototype[name]) {
+ extend.call(this, name, source[name]);
+ }
+ }
+ }
+ return this;
+ },
+
+ base: function() {
+ // call this method from any other method to invoke that method's ancestor
+ }
+};
+
+Base.extend = function(_instance, _static) {
+ var extend = Base.prototype.extend;
+ if (!_instance) _instance = {};
+ // build the prototype
+ Base._prototyping = true;
+ var _prototype = new this;
+ extend.call(_prototype, _instance);
+ var constructor = _prototype.constructor;
+ _prototype.constructor = this;
+ delete Base._prototyping;
+ // create the wrapper for the constructor function
+ var klass = function() {
+ if (!Base._prototyping) constructor.apply(this, arguments);
+ this.constructor = klass;
+ };
+ klass.prototype = _prototype;
+ // build the class interface
+ klass.extend = this.extend;
+ klass.implement = this.implement;
+ klass.toString = function() {
+ return String(constructor);
+ };
+ extend.call(klass, _static);
+ // single instance
+ var object = constructor ? klass : _prototype;
+ // class initialisation
+ if (object.init instanceof Function) object.init();
+ return object;
+};
+
+Base.implement = function(_interface) {
+ if (_interface instanceof Function) _interface = _interface.prototype;
+ this.prototype.extend(_interface);
+};
+
+/*
+ * Signals and Slots for Prototype: Easy custom javascript events
+ * http://tetlaw.id.au/view/blog/signals-and-slots-for-prototype-easy-custom-javascript-events
+ * Andrew Tetlaw
+ * Version 1.2 (2006-06-19)
+ *
+ * http://creativecommons.org/licenses/by-sa/2.5/
+ *
+Signal = {
+ throwErrors : true,
+ MT : function(){ return true },
+ connect : function(obj1, func1, obj2, func2, options) {
+ var options = Object.extend({
+ connectOnce : false,
+ before : false,
+ mutate : function() {return arguments;}
+ }, options || {});
+ if(typeof func1 != 'string' || typeof func2 != 'string') return;
+
+ var sigObj = obj1 || window;
+ var slotObj = obj2 || window;
+ var signame = func1+'__signal_';
+ var slotsname = func1+'__slots_';
+ if(!sigObj[signame]) {
+ // having the slotFunc in a var and setting it by using an anonymous function in this way
+ // is apparently a good way to prevent memory leaks in IE if the objects are DOM nodes.
+ var slotFunc = function() {
+ var args = [];
+ for(var x = 0; x < arguments.length; x++){
+ args.push(arguments[x]);
+ }
+ args = options.mutate.apply(null,args)
+ var result;
+ if(!options.before) result = sigObj[signame].apply(sigObj,arguments); //default: call sign before slot
+ sigObj[slotsname].each(function(slot){
+ try {
+ if(slot && slot[0]) { // testing for null, a disconnect may have nulled this slot
+ slot[0][slot[1]].apply(slot[0],args); //[0] = obj, [1] = func name
+ }
+ } catch(e) {
+ if(Signal.throwErrors) throw e;
+ }
+ });
+ if(options.before) result = sigObj[signame].apply(sigObj,arguments); //call slot before sig
+ return result; //return sig result
};
- });
+ (function() {
+ sigObj[slotsname] = $A([]);
+ sigObj[signame] = sigObj[func1] || Signal.MT;
+ sigObj[func1] = slotFunc;
+ })();
+ }
+ var con = (sigObj[slotsname].length > 0) ?
+ (options.connectOnce ? !sigObj[slotsname].any(function(slot) { return (slot[0] == slotObj && slot[1] == func2) }) : true) :
+ true;
+ if(con) {
+ sigObj[slotsname].push([slotObj,func2]);
+ }
+ },
+ connectOnce : function(obj1, func1, obj2, func2, options) {
+ Signal.connect(obj1, func1, obj2, func2, Object.extend(options || {}, {connectOnce : true}))
+ },
+ disconnect : function(obj1, func1, obj2, func2, options) {
+ var options = Object.extend({
+ disconnectAll : false
+ }, options || {});
+ if(typeof func1 != 'string' || typeof func2 != 'string') return;
+
+ var sigObj = obj1 || window;
+ var slotObj = obj2 || window;
+ var signame = func1+'__signal_';
+ var slotsname = func1+'__slots_';
+
+ // I null them in this way so that any currectly active signal will read a null slot,
+ // otherwise the slot will be applied even though it's been disconnected
+ if(sigObj[slotsname]) {
+ if(options.disconnectAll) {
+ sigObj[slotsname] = sigObj[slotsname].collect(function(slot) {
+ if(slot[0] == slotObj && slot[1] == func2) {
+ slot[0] = null;
+ return null;
+ } else {
+ return slot;
+ }
+ }).compact();
+ } else {
+ var idx = -1;
+ sigObj[slotsname] = sigObj[slotsname].collect(function(slot, index) {
+ if(slot[0] == slotObj && slot[1] == func2 && idx < 0) { //disconnect first match
+ idx = index;
+ slot[0] = null;
+ return null;
+ } else {
+ return slot;
+ }
+ }).compact();
+ }
+ }
+ },
+ disconnectAll : function(obj1, func1, obj2, func2, options) {
+ Signal.disconnect(obj1, func1, obj2, func2, Object.extend(options || {}, {disconnectAll : true}))
}
-});
+}
+*/
-Builder.exportTags();
+/*
+ Tests
+
+// 1. Simple Test 1 "hello Fred" should trigger "Fred is a stupid head"
+
+
+ sayHello = function(n) {
+ alert("Hello! " + n);
+ }
+ moron = function(n) {
+ alert(n + " is a stupid head");
+ }
+ Signal.connect(null,'sayHello',null,'moron');
+
+ onclick="sayHello('Fred')"
+
+
+// 2. Simple Test 2 repeated insults about Fred
+
+
+ Signal.connect(null,'sayHello2',null,'moron2');
+ Signal.connect(null,'sayHello2',null,'moron2');
+ Signal.connect(null,'sayHello2',null,'moron2');
+
+
+// 3. Simple Test 3 multiple insults about Fred
+
+
+ Signal.connect(null,'sayHello3',null,'moron3');
+ Signal.connect(null,'sayHello3',null,'bonehead3');
+ Signal.connect(null,'sayHello3',null,'idiot3');
+
+
+// 4. Simple Test 4 3 insults about Fred first - 3 then none
+
+
+ Signal.connect(null,'sayHello4',null,'moron4');
+ Signal.connect(null,'sayHello4',null,'moron4');
+ Signal.connect(null,'sayHello4',null,'moron4');
+ Signal.disconnect(null,'sayHello4',null,'moron4');
+ Signal.disconnect(null,'sayHello4',null,'moron4');
+ Signal.disconnect(null,'sayHello4',null,'moron4');
+
+
+// 5. Simple Test 5 connect 3 insults about Fred first - only one, then none
+
+
+ Signal.connect(null,'sayHello5',null,'moron5');
+ Signal.connect(null,'sayHello5',null,'moron5');
+ Signal.connect(null,'sayHello5',null,'moron5');
+ Signal.disconnectAll(null,'sayHello5',null,'moron5');
+
+
+// 6. Simple Test 6 connect 3 insults but only one comes out
+
+
+ Signal.connectOnce(null,'sayHello6',null,'moron6');
+ Signal.connectOnce(null,'sayHello6',null,'moron6');
+ Signal.connectOnce(null,'sayHello6',null,'moron6');
+
+
+// 7. Simple Test 7 connect via objects
+
+
+ var o = {};
+ o.sayHello = function(n) {
+ alert("Hello! " + n + " (from object o)");
+ }
+ var m = {};
+ m.moron = function(n) {
+ alert(n + " is a stupid head (from object m)");
+ }
+
+ Signal.connect(o,'sayHello',m,'moron');
+
+ onclick="o.sayHello('Fred')"
+
+
+// 8. Simple Test 8 connect but the insult comes first using {before:true}
+
+
+ Signal.connect(null,'sayHello8',null,'moron8', {before:true});
+
+
+// 9. Simple Test 9 connect but the insult is mutated
+
+
+ Signal.connect(null,'sayHello9',null,'moron9', {mutate:function() { return ['smelly ' + arguments[0]] }});
+
+ */
+
+
+/**
+ * @class String extensions
+ */
+Object.extend(String.prototype,
+{
+ /**
+ * @param {String} "left" to pad the string on the left, "right" to pad right.
+ * @param {Number} minimum string length.
+ * @param {String} character(s) to pad
+ * @return {String} padded character(s) on the left or right to satisfy minimum string length
+ */
+
+ pad : function(side, len, chr) {
+ if (!chr) chr = ' ';
+ var s = this;
+ var left = side.toLowerCase()=='left';
+ while (s.length<len) s = left? chr + s : s + chr;
+ return s;
+ },
+
+ /**
+ * @param {Number} minimum string length.
+ * @param {String} character(s) to pad
+ * @return {String} padded character(s) on the left to satisfy minimum string length
+ */
+ padLeft : function(len, chr) {
+ return this.pad('left',len,chr);
+ },
+
+ /**
+ * @param {Number} minimum string length.
+ * @param {String} character(s) to pad
+ * @return {String} padded character(s) on the right to satisfy minimum string length
+ */
+ padRight : function(len, chr) {
+ return this.pad('right',len,chr);
+ },
+
+ /**
+ * @param {Number} minimum string length.
+ * @return {String} append zeros to the left to satisfy minimum string length.
+ */
+ zerofill : function(len) {
+ return this.padLeft(len,'0');
+ },
+
+ /**
+ * @return {String} removed white spaces from both ends.
+ */
+ trim : function() {
+ return this.replace(/^\s+|\s+$/g,'');
+ },
+
+ /**
+ * @return {String} removed white spaces from the left end.
+ */
+ trimLeft : function() {
+ return this.replace(/^\s+/,'');
+ },
+
+ /**
+ * @return {String} removed white spaces from the right end.
+ */
+ trimRight : function() {
+ return this.replace(/\s+$/,'');
+ },
+
+ /**
+ * Convert period separated function names into a function reference.
+ * e.g. "Prado.AJAX.Callback.Action.setValue".toFunction() will return
+ * the actual function Prado.AJAX.Callback.Action.setValue()
+ * @return {Function} the corresponding function represented by the string.
+ */
+ toFunction : function()
+ {
+ var commands = this.split(/\./);
+ var command = window;
+ commands.each(function(action)
+ {
+ if(command[new String(action)])
+ command=command[new String(action)];
+ });
+ if(typeof(command) == "function")
+ return command;
+ else
+ {
+ if(typeof Logger != "undefined")
+ Logger.error("Missing function", this);
+
+ throw new Error ("Missing function '"+this+"'");
+ }
+ },
+
+ /**
+ * Convert a string into integer, returns null if not integer.
+ * @return {Number} null if string does not represent an integer.
+ */
+ toInteger : function()
+ {
+ var exp = /^\s*[-\+]?\d+\s*$/;
+ if (this.match(exp) == null)
+ return null;
+ var num = parseInt(this, 10);
+ return (isNaN(num) ? null : num);
+ },
+
+ /**
+ * Convert a string into a double/float value. <b>Internationalization
+ * is not supported</b>
+ * @param {String} the decimal character
+ * @return {Double} null if string does not represent a float value
+ */
+ toDouble : function(decimalchar)
+ {
+ if(this.length <= 0) return null;
+ decimalchar = decimalchar || ".";
+ var exp = new RegExp("^\\s*([-\\+])?(\\d+)?(\\" + decimalchar + "(\\d+))?\\s*$");
+ var m = this.match(exp);
+
+ if (m == null)
+ return null;
+ m[1] = m[1] || "";
+ m[2] = m[2] || "0";
+ m[4] = m[4] || "0";
+
+ var cleanInput = m[1] + (m[2].length>0 ? m[2] : "0") + "." + m[4];
+ var num = parseFloat(cleanInput);
+ return (isNaN(num) ? null : num);
+ },
+
+ /**
+ * Convert strings that represent a currency value (e.g. a float with grouping
+ * characters) to float. E.g. "10,000.50" will become "10000.50". The number
+ * of dicimal digits, grouping and decimal characters can be specified.
+ * <i>The currency input format is <b>very</b> strict, null will be returned if
+ * the pattern does not match</i>.
+ * @param {String} the grouping character, default is ","
+ * @param {Number} number of decimal digits
+ * @param {String} the decimal character, default is "."
+ * @type {Double} the currency value as float.
+ */
+ toCurrency : function(groupchar, digits, decimalchar)
+ {
+ groupchar = groupchar || ",";
+ decimalchar = decimalchar || ".";
+ digits = typeof(digits) == "undefined" ? 2 : digits;
+
+ var exp = new RegExp("^\\s*([-\\+])?(((\\d+)\\" + groupchar + ")*)(\\d+)"
+ + ((digits > 0) ? "(\\" + decimalchar + "(\\d{1," + digits + "}))?" : "")
+ + "\\s*$");
+ var m = this.match(exp);
+ if (m == null)
+ return null;
+ var intermed = m[2] + m[5] ;
+ var cleanInput = m[1] + intermed.replace(
+ new RegExp("(\\" + groupchar + ")", "g"), "")
+ + ((digits > 0) ? "." + m[7] : "");
+ var num = parseFloat(cleanInput);
+ return (isNaN(num) ? null : num);
+ },
+
+ /**
+ * Converts the string to a date by finding values that matches the
+ * date format pattern.
+ * @param string date format pattern, e.g. MM-dd-yyyy
+ * @return {Date} the date extracted from the string
+ */
+ toDate : function(format)
+ {
+ return Date.SimpleParse(this, format);
+ }
+});
+/**
+ * @class Event extensions.
+ */
+Object.extend(Event,
+{
+ /**
+ * Register a function to be executed when the page is loaded.
+ * Note that the page is only loaded if all resources (e.g. images)
+ * are loaded.
+ *
+ * Example: Show an alert box with message "Page Loaded!" when the
+ * page finished loading.
+ * <code>
+ * Event.OnLoad(function(){ alert("Page Loaded!"); });
+ * </code>
+ *
+ * @param {Function} function to execute when page is loaded.
+ */
+ OnLoad : function (fn)
+ {
+ // opera onload is in document, not window
+ var w = document.addEventListener &&
+ !window.addEventListener ? document : window;
+ Event.observe(w,'load',fn);
+ },
+
+ /**
+ * @param {Event} a keyboard event
+ * @return {Number} the Unicode character code generated by the key
+ * that was struck.
+ */
+ keyCode : function(e)
+ {
+ return e.keyCode != null ? e.keyCode : e.charCode
+ },
+
+ /**
+ * @param {String} event type or event name.
+ * @return {Boolean} true if event type is of HTMLEvent, false
+ * otherwise
+ */
+ isHTMLEvent : function(type)
+ {
+ var events = ['abort', 'blur', 'change', 'error', 'focus',
+ 'load', 'reset', 'resize', 'scroll', 'select',
+ 'submit', 'unload'];
+ return events.include(type);
+ },
+
+ /**
+ * @param {String} event type or event name
+ * @return {Boolean} true if event type is of MouseEvent,
+ * false otherwise
+ */
+ isMouseEvent : function(type)
+ {
+ var events = ['click', 'mousedown', 'mousemove', 'mouseout',
+ 'mouseover', 'mouseup'];
+ return events.include(type);
+ },
+
+ /**
+ * Dispatch the DOM event of a given <tt>type</tt> on a DOM
+ * <tt>element</tt>. Only HTMLEvent and MouseEvent can be
+ * dispatched, keyboard events or UIEvent can not be dispatch
+ * via javascript consistently.
+ * For the "submit" event the submit() method is called.
+ * @param {Object} element id string or a DOM element.
+ * @param {String} event type to dispatch.
+ */
+ fireEvent : function(element,type,canBubble)
+ {
+ canBubble = (typeof(canBubble) == undefined) ? true : canBubble;
+ element = $(element);
+ if(type == "submit")
+ return element.submit();
+ if(document.createEvent)
+ {
+ if(Event.isHTMLEvent(type))
+ {
+ var event = document.createEvent('HTMLEvents');
+ event.initEvent(type, canBubble, true);
+ }
+ else if(Event.isMouseEvent(type))
+ {
+ var event = document.createEvent('MouseEvents');
+ if (event.initMouseEvent)
+ {
+ event.initMouseEvent(type,canBubble,true,
+ document.defaultView, 1, 0, 0, 0, 0, false,
+ false, false, false, 0, null);
+ }
+ else
+ {
+ // Safari
+ // TODO we should be initialising other mouse-event related attributes here
+ event.initEvent(type, canBubble, true);
+ }
+ }
+ element.dispatchEvent(event);
+ }
+ else if(document.createEventObject)
+ {
+ var evObj = document.createEventObject();
+ element.fireEvent('on'+type, evObj);
+ }
+ else if(typeof(element['on'+type]) == "function")
+ element['on'+type]();
+ }
+});
Object.extend(Date.prototype,
@@ -2661,6 +4119,32 @@ Object.extend(Date,
});
+Object.extend(Builder,
+{
+ exportTags:function()
+ {
+ var tags=["BUTTON","TT","PRE","H1","H2","H3","BR","CANVAS","HR","LABEL","TEXTAREA","FORM","STRONG","SELECT","OPTION","OPTGROUP","LEGEND","FIELDSET","P","UL","OL","LI","TD","TR","THEAD","TBODY","TFOOT","TABLE","TH","INPUT","SPAN","A","DIV","IMG", "CAPTION"];
+ tags.each(function(tag)
+ {
+ window[tag]=function()
+ {
+ var args=$A(arguments);
+ if(args.length==0)
+ return Builder.node(tag,null);
+ if(args.length==1)
+ return Builder.node(tag,args[0]);
+ if(args.length>1)
+ return Builder.node(tag,args.shift(),args);
+
+ };
+ });
+ }
+});
+
+Builder.exportTags();
+
+
+
var Prado =
{
Version: '3.1',
@@ -3545,3 +5029,182 @@ Prado.WebUI.TRadioButtonList = Base.extend(
}
});
+Prado.WebUI.TRatingList = Base.extend(
+{
+ selectedIndex : -1,
+ rating: -1,
+ enabled : true,
+ readOnly : false,
+
+ constructor : function(options)
+ {
+ var cap = $(options.CaptionID);
+ this.options = Object.extend(
+ {
+ caption : cap ? cap.innerHTML : ''
+ }, options || {});
+
+ Prado.WebUI.TRatingList.register(this);
+ this._init();
+ this.selectedIndex = options.SelectedIndex;
+ this.rating = options.Rating;
+ if(options.Rating <= 0 && options.SelectedIndex >= 0)
+ this.rating = options.SelectedIndex+1;
+ this.showRating(this.rating);
+ },
+
+ _init: function(options)
+ {
+ Element.addClassName($(this.options.ListID),this.options.Style);
+ this.radios = new Array();
+ var index=0;
+ for(var i = 0; i<this.options.ItemCount; i++)
+ {
+ var radio = $(this.options.ListID+'_c'+i);
+ var td = radio.parentNode;
+ if(radio && td.tagName.toLowerCase()=='td')
+ {
+ this.radios.push(radio);
+ Event.observe(td, "mouseover", this.hover.bindEvent(this,index));
+ Event.observe(td, "mouseout", this.recover.bindEvent(this,index));
+ Event.observe(td, "click", this.click.bindEvent(this, index));
+ index++;
+ Element.addClassName(td,"rating");
+ }
+ }
+ },
+
+ hover : function(ev,index)
+ {
+ if(this.enabled==false) return;
+ for(var i = 0; i<this.radios.length; i++)
+ {
+ var node = this.radios[i].parentNode;
+ var action = i <= index ? 'addClassName' : 'removeClassName'
+ Element[action](node,"rating_hover");
+ Element.removeClassName(node,"rating_selected");
+ Element.removeClassName(node,"rating_half");
+ }
+ this.showCaption(this.getIndexCaption(index));
+ },
+
+ recover : function(ev,index)
+ {
+ if(this.enabled==false) return;
+ this.showRating(this.rating);
+ this.showCaption(this.options.caption);
+ },
+
+ click : function(ev, index)
+ {
+ if(this.enabled==false) return;
+ for(var i = 0; i<this.radios.length; i++)
+ this.radios[i].checked = (i == index);
+
+ this.selectedIndex = index;
+ this.setRating(index+1);
+
+ this.dispatchRequest(ev);
+ },
+
+ dispatchRequest : function(ev)
+ {
+ var requestOptions = Object.extend(
+ {
+ ID : this.options.ListID+"_c"+this.selectedIndex,
+ EventTarget : this.options.ListName+"$c"+this.selectedIndex
+ },this.options);
+ var request = new Prado.CallbackRequest(requestOptions.EventTarget, requestOptions);
+ if(request.dispatch()==false)
+ Event.stop(ev);
+ },
+
+ setRating : function(value)
+ {
+ this.rating = value;
+ var base = Math.floor(value-1);
+ var remainder = value - base-1;
+ var halfMax = this.options.HalfRating["1"];
+ var index = remainder > halfMax ? base+1 : base;
+ for(var i = 0; i<this.radios.length; i++)
+ this.radios[i].checked = (i == index);
+
+ var caption = this.getIndexCaption(index);
+ this.setCaption(caption);
+ this.showCaption(caption);
+
+ this.showRating(value);
+ },
+
+ showRating: function(value)
+ {
+ var base = Math.floor(value-1);
+ var remainder = value - base-1;
+ var halfMin = this.options.HalfRating["0"];
+ var halfMax = this.options.HalfRating["1"];
+ var index = remainder > halfMax ? base+1 : base;
+ var hasHalf = remainder >= halfMin && remainder <= halfMax;
+ for(var i = 0; i<this.radios.length; i++)
+ {
+ var node = this.radios[i].parentNode;
+ var action = i > index ? 'removeClassName' : 'addClassName';
+ Element[action](node, "rating_selected");
+ if(i==index+1 && hasHalf)
+ Element.addClassName(node, "rating_half");
+ else
+ Element.removeClassName(node, "rating_half");
+ Element.removeClassName(node,"rating_hover");
+ }
+ },
+
+ getIndexCaption : function(index)
+ {
+ return index > -1 ? this.radios[index].value : this.options.caption;
+ },
+
+ showCaption : function(value)
+ {
+ var caption = $(this.options.CaptionID);
+ if(caption) caption.innerHTML = value;
+ $(this.options.ListID).title = value;
+ },
+
+ setCaption : function(value)
+ {
+ this.options.caption = value;
+ this.showCaption(value);
+ },
+
+ setEnabled : function(value)
+ {
+ this.enabled = value;
+ for(var i = 0; i<this.radios.length; i++)
+ {
+ var action = value ? 'removeClassName' : 'addClassName'
+ Element[action](this.radios[i].parentNode, "rating_disabled");
+ }
+ }
+},
+{
+ratings : {},
+register : function(rating)
+{
+ Prado.WebUI.TRatingList.ratings[rating.options.ListID] = rating;
+},
+
+setEnabled : function(id,value)
+{
+ Prado.WebUI.TRatingList.ratings[id].setEnabled(value);
+},
+
+setRating : function(id,value)
+{
+ Prado.WebUI.TRatingList.ratings[id].setRating(value);
+},
+
+setCaption : function(id,value)
+{
+ Prado.WebUI.TRatingList.ratings[id].setCaption(value);
+}
+});
+
diff --git a/framework/Web/Javascripts/prototype/AUTHORS b/framework/Web/Javascripts/prototype/AUTHORS
deleted file mode 100644
index 5fea268f..00000000
--- a/framework/Web/Javascripts/prototype/AUTHORS
+++ /dev/null
@@ -1,12 +0,0 @@
-Creator and maintainer
-
-* Sam Stephenson -- http://conio.net/
-
-Contributors
-
-* Thomas Fuchs -- http://mir.aculo.us/
-* David Heinemeier Hansson -- http://www.loudthinking.com/
-* Matthew McCray -- http://www.mattmccray.com/
-* Michael Schuerig -- http://www.schuerig.de/michael/
-* Sean Treadway -- http://sean.treadway.info/
-* flash@vanklinkenbergsoftware.nl
diff --git a/framework/Web/Javascripts/prototype/LICENSE b/framework/Web/Javascripts/prototype/LICENSE
deleted file mode 100644
index 6c4c6312..00000000
--- a/framework/Web/Javascripts/prototype/LICENSE
+++ /dev/null
@@ -1,16 +0,0 @@
-Copyright (c) 2005 Sam Stephenson
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/framework/Web/Javascripts/prototype/README b/framework/Web/Javascripts/prototype/README
deleted file mode 100644
index 74761a82..00000000
--- a/framework/Web/Javascripts/prototype/README
+++ /dev/null
@@ -1,64 +0,0 @@
-= Prototype
-==== An object-oriented JavaScript framework
-
-(c) 2005 Sam Stephenson (mailto:sam@conio.net)
-
-Prototype is a JavaScript framework that aims to ease development of dynamic
-web applications. Its development is driven heavily by the Ruby on Rails
-framework, but it can be used in any environment.
-
-=== Targeted platforms
-
-Prototype currently targets the following platforms:
-
-* Microsoft Internet Explorer for Windows, version 6.0 and higher
-* Mozilla Firefox 1.0/Mozilla 1.7 and higher
-* Apple Safari 1.2 and higher
-
-== Using Prototype
-
-To use Prototype in your application, download the latest release from the
-Prototype web site (http://prototype.conio.net/) and copy
-<tt>dist/prototype.js</tt> to a suitable location. Then include it in your HTML
-like so:
-
- <script type="text/javascript" src="/path/to/prototype.js"></script>
-
-=== Building Prototype from source
-
-<tt>prototype.js</tt> is a composite file generated from many source files in
-the <tt>src/</tt> directory. To build Prototype, you'll need:
-
-* a copy of the Prototype source tree, either from a distribution tarball or
- from the darcs repository (see below)
-* Ruby 1.8.2 or higher (http://www.ruby-lang.org/)
-* Rake -- Ruby Make (http://rake.rubyforge.org/)
-* RDoc, if your Ruby distribution does not include it
-* darcs 1.0.1 or higher (http://abridgegame.org/darcs/; static binaries
- available at http://www.scannedinavian.org/DarcsWiki/CategoryBinaries)
-
-From the root Prototype directory,
-
-* <tt>rake dist</tt> will preprocess the Prototype source using ERB and
- generate the composite <tt>dist/prototype.js</tt>.
-* <tt>rake package</tt> will create a distribution tarball in the
- <tt>pkg/</tt> directory.
-
-== Contributing to Prototype
-
-You'll need the tools mentioned above. Modify the files in <tt>src/</tt>, add
-tests in <tt>test/</tt> if possible, generate a new dist file, and record the
-changes with <tt>darcs record -al</tt>. Then email patches to
-mailto:sam@conio.net using <tt>darcs send</tt>.
-
-== Documentation
-
-Prototype is embarrassingly lacking in documentation. (The source code should
-be fairly easy to comprehend; I'm committed to using a clean style with
-meaningful identifiers. But I know that only goes so far.)
-
-Much of the problem is that there is no easy way to document it from within the
-source code itself. I've tried JSDoc[http://jsdoc.sf.net/] and a Perl script
-included with JsUnit[http://www.edwardh.com/jsunit/]; neither groks Prototype's
-hash-style class definitions or inheritance scheme. Making them work would
-require major changes, and I don't have the time for that right now.
diff --git a/framework/Web/Javascripts/prototype/THANKS b/framework/Web/Javascripts/prototype/THANKS
deleted file mode 100644
index d36adacb..00000000
--- a/framework/Web/Javascripts/prototype/THANKS
+++ /dev/null
@@ -1,21 +0,0 @@
-Special thanks are in order for Mikael and Daniel Brockman, whose patterns
-heavily influenced the design and implementation of this library.
-* Daniel Brockman -- http://www.deepwood.net/
-* Mikael Brockman -- http://www.phubuh.org/
----
-* Scott Barron -- http://scott.elitists.net/
-* Hemant Bhanoo
-* Jamis Buck -- http://jamis.jamisbuck.org/
-* Lucas Carlson -- http://www.rufy.com/
-* Jon Casey
-* Patrick Corcoran
-* Alper Cugun
-* Darren James -- http://www.openrico.org/
-* Marcel Molina Jr. -- http://vernix.org/marcel/
-* Alex Russell
-* Chriztian Steinmeier
-* Dan Webb
-* Gary Wright
-
-and anyone else who has sent patches or bug reports. If your name isn't on this
-list, please forgive my oversight and send me an email (sam at conio.net). \ No newline at end of file
diff --git a/framework/Web/Javascripts/prototype/ajax.js b/framework/Web/Javascripts/prototype/ajax.js
deleted file mode 100644
index 9fcb473b..00000000
--- a/framework/Web/Javascripts/prototype/ajax.js
+++ /dev/null
@@ -1,286 +0,0 @@
-var Ajax = {
- getTransport: function() {
- return Try.these(
- function() {return new XMLHttpRequest()},
- function() {return new ActiveXObject('Msxml2.XMLHTTP')},
- function() {return new ActiveXObject('Microsoft.XMLHTTP')}
- ) || false;
- },
-
- activeRequestCount: 0
-}
-
-Ajax.Responders = {
- responders: [],
-
- _each: function(iterator) {
- this.responders._each(iterator);
- },
-
- register: function(responderToAdd) {
- if (!this.include(responderToAdd))
- this.responders.push(responderToAdd);
- },
-
- unregister: function(responderToRemove) {
- this.responders = this.responders.without(responderToRemove);
- },
-
- dispatch: function(callback, request, transport, json) {
- this.each(function(responder) {
- if (responder[callback] && typeof responder[callback] == 'function') {
- try {
- responder[callback].apply(responder, [request, transport, json]);
- } catch (e) {}
- }
- });
- }
-};
-
-Object.extend(Ajax.Responders, Enumerable);
-
-Ajax.Responders.register({
- onCreate: function() {
- Ajax.activeRequestCount++;
- },
-
- onComplete: function() {
- Ajax.activeRequestCount--;
- }
-});
-
-Ajax.Base = function() {};
-Ajax.Base.prototype = {
- setOptions: function(options) {
- this.options = {
- method: 'post',
- asynchronous: true,
- contentType: 'application/x-www-form-urlencoded',
- parameters: ''
- }
- Object.extend(this.options, options || {});
- },
-
- responseIsSuccess: function() {
- return this.transport.status == undefined
- || this.transport.status == 0
- || (this.transport.status >= 200 && this.transport.status < 300);
- },
-
- responseIsFailure: function() {
- return !this.responseIsSuccess();
- }
-}
-
-Ajax.Request = Class.create();
-Ajax.Request.Events =
- ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
-
-Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
- initialize: function(url, options) {
- this.transport = Ajax.getTransport();
- this.setOptions(options);
- this.request(url);
- },
-
- request: function(url) {
- var parameters = this.options.parameters || '';
- if (parameters.length > 0) parameters += '&_=';
-
- try {
- this.url = url;
- if (this.options.method == 'get' && parameters.length > 0)
- this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;
-
- Ajax.Responders.dispatch('onCreate', this, this.transport);
-
- this.transport.open(this.options.method, this.url,
- this.options.asynchronous);
-
- if (this.options.asynchronous) {
- this.transport.onreadystatechange = this.onStateChange.bind(this);
- setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
- }
-
- this.setRequestHeaders();
-
- var body = this.options.postBody ? this.options.postBody : parameters;
- this.transport.send(this.options.method == 'post' ? body : null);
-
- } catch (e) {
- this.dispatchException(e);
- }
- },
-
- setRequestHeaders: function() {
- var requestHeaders =
- ['X-Requested-With', 'XMLHttpRequest',
- 'X-Prototype-Version', Prototype.Version,
- 'Accept', 'text/javascript, text/html, application/xml, text/xml'];
-
- if (this.options.method == 'post') {
- requestHeaders.push('Content-type', this.options.contentType);
-
- /* Force "Connection: close" for Mozilla browsers to work around
- * a bug where XMLHttpReqeuest sends an incorrect Content-length
- * header. See Mozilla Bugzilla #246651.
- */
- if (this.transport.overrideMimeType)
- requestHeaders.push('Connection', 'close');
- }
-
- if (this.options.requestHeaders)
- requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);
-
- for (var i = 0; i < requestHeaders.length; i += 2)
- this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
- },
-
- onStateChange: function() {
- var readyState = this.transport.readyState;
- if (readyState != 1)
- this.respondToReadyState(this.transport.readyState);
- },
-
- header: function(name) {
- try {
- return this.transport.getResponseHeader(name);
- } catch (e) {}
- },
-
- evalJSON: function() {
- try {
- return eval('(' + this.header('X-JSON') + ')');
- } catch (e) {}
- },
-
- evalResponse: function() {
- try {
- return eval(this.transport.responseText);
- } catch (e) {
- this.dispatchException(e);
- }
- },
-
- respondToReadyState: function(readyState) {
- var event = Ajax.Request.Events[readyState];
- var transport = this.transport, json = this.evalJSON();
-
- if (event == 'Complete') {
- try {
- (this.options['on' + this.transport.status]
- || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
- || Prototype.emptyFunction)(transport, json);
- } catch (e) {
- this.dispatchException(e);
- }
-
- if ((this.header('Content-type') || '').match(/^text\/javascript/i))
- this.evalResponse();
- }
-
- try {
- (this.options['on' + event] || Prototype.emptyFunction)(transport, json);
- Ajax.Responders.dispatch('on' + event, this, transport, json);
- } catch (e) {
- this.dispatchException(e);
- }
-
- /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
- if (event == 'Complete')
- this.transport.onreadystatechange = Prototype.emptyFunction;
- },
-
- dispatchException: function(exception) {
- (this.options.onException || Prototype.emptyFunction)(this, exception);
- Ajax.Responders.dispatch('onException', this, exception);
- }
-});
-
-Ajax.Updater = Class.create();
-
-Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
- initialize: function(container, url, options) {
- this.containers = {
- success: container.success ? $(container.success) : $(container),
- failure: container.failure ? $(container.failure) :
- (container.success ? null : $(container))
- }
-
- this.transport = Ajax.getTransport();
- this.setOptions(options);
-
- var onComplete = this.options.onComplete || Prototype.emptyFunction;
- this.options.onComplete = (function(transport, object) {
- this.updateContent();
- onComplete(transport, object);
- }).bind(this);
-
- this.request(url);
- },
-
- updateContent: function() {
- var receiver = this.responseIsSuccess() ?
- this.containers.success : this.containers.failure;
- var response = this.transport.responseText;
-
- if (!this.options.evalScripts)
- response = response.stripScripts();
-
- if (receiver) {
- if (this.options.insertion) {
- new this.options.insertion(receiver, response);
- } else {
- Element.update(receiver, response);
- }
- }
-
- if (this.responseIsSuccess()) {
- if (this.onComplete)
- setTimeout(this.onComplete.bind(this), 10);
- }
- }
-});
-
-Ajax.PeriodicalUpdater = Class.create();
-Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
- initialize: function(container, url, options) {
- this.setOptions(options);
- this.onComplete = this.options.onComplete;
-
- this.frequency = (this.options.frequency || 2);
- this.decay = (this.options.decay || 1);
-
- this.updater = {};
- this.container = container;
- this.url = url;
-
- this.start();
- },
-
- start: function() {
- this.options.onComplete = this.updateComplete.bind(this);
- this.onTimerEvent();
- },
-
- stop: function() {
- this.updater.onComplete = undefined;
- clearTimeout(this.timer);
- (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
- },
-
- updateComplete: function(request) {
- if (this.options.decay) {
- this.decay = (request.responseText == this.lastText ?
- this.decay * this.options.decay : 1);
-
- this.lastText = request.responseText;
- }
- this.timer = setTimeout(this.onTimerEvent.bind(this),
- this.decay * this.frequency * 1000);
- },
-
- onTimerEvent: function() {
- this.updater = new Ajax.Updater(this.container, this.url, this.options);
- }
-});
diff --git a/framework/Web/Javascripts/prototype/array.js b/framework/Web/Javascripts/prototype/array.js
deleted file mode 100644
index 02731141..00000000
--- a/framework/Web/Javascripts/prototype/array.js
+++ /dev/null
@@ -1,70 +0,0 @@
-var $A = Array.from = function(iterable) {
- if (!iterable) return [];
- if (iterable.toArray) {
- return iterable.toArray();
- } else {
- var results = [];
- for (var i = 0; i < iterable.length; i++)
- results.push(iterable[i]);
- return results;
- }
-}
-
-Object.extend(Array.prototype, Enumerable);
-
-if (!Array.prototype._reverse)
- Array.prototype._reverse = Array.prototype.reverse;
-
-Object.extend(Array.prototype, {
- _each: function(iterator) {
- for (var i = 0; i < this.length; i++)
- iterator(this[i]);
- },
-
- clear: function() {
- this.length = 0;
- return this;
- },
-
- first: function() {
- return this[0];
- },
-
- last: function() {
- return this[this.length - 1];
- },
-
- compact: function() {
- return this.select(function(value) {
- return value != undefined || value != null;
- });
- },
-
- flatten: function() {
- return this.inject([], function(array, value) {
- return array.concat(value && value.constructor == Array ?
- value.flatten() : [value]);
- });
- },
-
- without: function() {
- var values = $A(arguments);
- return this.select(function(value) {
- return !values.include(value);
- });
- },
-
- indexOf: function(object) {
- for (var i = 0; i < this.length; i++)
- if (this[i] == object) return i;
- return -1;
- },
-
- reverse: function(inline) {
- return (inline !== false ? this : this.toArray())._reverse();
- },
-
- inspect: function() {
- return '[' + this.map(Object.inspect).join(', ') + ']';
- }
-});
diff --git a/framework/Web/Javascripts/prototype/base.js b/framework/Web/Javascripts/prototype/base.js
deleted file mode 100644
index 41b1d904..00000000
--- a/framework/Web/Javascripts/prototype/base.js
+++ /dev/null
@@ -1,102 +0,0 @@
-var Class = {
- create: function() {
- return function() {
- this.initialize.apply(this, arguments);
- }
- }
-}
-
-var Abstract = new Object();
-
-Object.extend = function(destination, source) {
- for (var property in source) {
- destination[property] = source[property];
- }
- return destination;
-}
-
-Object.inspect = function(object) {
- try {
- if (object == undefined) return 'undefined';
- if (object == null) return 'null';
- return object.inspect ? object.inspect() : object.toString();
- } catch (e) {
- if (e instanceof RangeError) return '...';
- throw e;
- }
-}
-
-Function.prototype.bind = function() {
- var __method = this, args = $A(arguments), object = args.shift();
- return function() {
- return __method.apply(object, args.concat($A(arguments)));
- }
-}
-
-Function.prototype.bindAsEventListener = function(object) {
- var __method = this;
- return function(event) {
- return __method.call(object, event || window.event);
- }
-}
-
-Object.extend(Number.prototype, {
- toColorPart: function() {
- var digits = this.toString(16);
- if (this < 16) return '0' + digits;
- return digits;
- },
-
- succ: function() {
- return this + 1;
- },
-
- times: function(iterator) {
- $R(0, this, true).each(iterator);
- return this;
- }
-});
-
-var Try = {
- these: function() {
- var returnValue;
-
- for (var i = 0; i < arguments.length; i++) {
- var lambda = arguments[i];
- try {
- returnValue = lambda();
- break;
- } catch (e) {}
- }
-
- return returnValue;
- }
-}
-
-/*--------------------------------------------------------------------------*/
-
-var PeriodicalExecuter = Class.create();
-PeriodicalExecuter.prototype = {
- initialize: function(callback, frequency) {
- this.callback = callback;
- this.frequency = frequency;
- this.currentlyExecuting = false;
-
- this.registerCallback();
- },
-
- registerCallback: function() {
- setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
- },
-
- onTimerEvent: function() {
- if (!this.currentlyExecuting) {
- try {
- this.currentlyExecuting = true;
- this.callback();
- } finally {
- this.currentlyExecuting = false;
- }
- }
- }
-}
diff --git a/framework/Web/Javascripts/prototype/dom.js b/framework/Web/Javascripts/prototype/dom.js
deleted file mode 100644
index 2d7438d5..00000000
--- a/framework/Web/Javascripts/prototype/dom.js
+++ /dev/null
@@ -1,398 +0,0 @@
-function $() {
- var results = [], element;
- for (var i = 0; i < arguments.length; i++) {
- element = arguments[i];
- if (typeof element == 'string')
- element = document.getElementById(element);
- results.push(Element.extend(element));
- }
- return results.length < 2 ? results[0] : results;
-}
-
-document.getElementsByClassName = function(className, parentElement) {
- var children = ($(parentElement) || document.body).getElementsByTagName('*');
- return $A(children).inject([], function(elements, child) {
- if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
- elements.push(Element.extend(child));
- return elements;
- });
-}
-
-/*--------------------------------------------------------------------------*/
-
-if (!window.Element)
- var Element = new Object();
-
-Element.extend = function(element) {
- if (!element) return;
- if (_nativeExtensions) return element;
-
- if (!element._extended && element.tagName && element != window) {
- var methods = Element.Methods, cache = Element.extend.cache;
- for (property in methods) {
- var value = methods[property];
- if (typeof value == 'function')
- element[property] = cache.findOrStore(value);
- }
- }
-
- element._extended = true;
- return element;
-}
-
-Element.extend.cache = {
- findOrStore: function(value) {
- return this[value] = this[value] || function() {
- return value.apply(null, [this].concat($A(arguments)));
- }
- }
-}
-
-Element.Methods = {
- visible: function(element) {
- return $(element).style.display != 'none';
- },
-
- toggle: function() {
- for (var i = 0; i < arguments.length; i++) {
- var element = $(arguments[i]);
- Element[Element.visible(element) ? 'hide' : 'show'](element);
- }
- },
-
- hide: function() {
- for (var i = 0; i < arguments.length; i++) {
- var element = $(arguments[i]);
- element.style.display = 'none';
- }
- },
-
- show: function() {
- for (var i = 0; i < arguments.length; i++) {
- var element = $(arguments[i]);
- element.style.display = '';
- }
- },
-
- remove: function(element) {
- element = $(element);
- element.parentNode.removeChild(element);
- },
-
- update: function(element, html) {
- $(element).innerHTML = html.stripScripts();
- setTimeout(function() {html.evalScripts()}, 10);
- },
-
- replace: function(element, html) {
- element = $(element);
- if (element.outerHTML) {
- element.outerHTML = html.stripScripts();
- } else {
- var range = element.ownerDocument.createRange();
- range.selectNodeContents(element);
- element.parentNode.replaceChild(
- range.createContextualFragment(html.stripScripts()), element);
- }
- setTimeout(function() {html.evalScripts()}, 10);
- },
-
- getHeight: function(element) {
- element = $(element);
- return element.offsetHeight;
- },
-
- classNames: function(element) {
- return new Element.ClassNames(element);
- },
-
- hasClassName: function(element, className) {
- if (!(element = $(element))) return;
- return Element.classNames(element).include(className);
- },
-
- addClassName: function(element, className) {
- if (!(element = $(element))) return;
- return Element.classNames(element).add(className);
- },
-
- removeClassName: function(element, className) {
- if (!(element = $(element))) return;
- return Element.classNames(element).remove(className);
- },
-
- // removes whitespace-only text node children
- cleanWhitespace: function(element) {
- element = $(element);
- for (var i = 0; i < element.childNodes.length; i++) {
- var node = element.childNodes[i];
- if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
- Element.remove(node);
- }
- },
-
- empty: function(element) {
- return $(element).innerHTML.match(/^\s*$/);
- },
-
- childOf: function(element, ancestor) {
- element = $(element), ancestor = $(ancestor);
- while (element = element.parentNode)
- if (element == ancestor) return true;
- return false;
- },
-
- scrollTo: function(element) {
- element = $(element);
- var x = element.x ? element.x : element.offsetLeft,
- y = element.y ? element.y : element.offsetTop;
- window.scrollTo(x, y);
- },
-
- getStyle: function(element, style) {
- element = $(element);
- var value = element.style[style.camelize()];
- if (!value) {
- if (document.defaultView && document.defaultView.getComputedStyle) {
- var css = document.defaultView.getComputedStyle(element, null);
- value = css ? css.getPropertyValue(style) : null;
- } else if (element.currentStyle) {
- value = element.currentStyle[style.camelize()];
- }
- }
-
- if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
- if (Element.getStyle(element, 'position') == 'static') value = 'auto';
-
- return value == 'auto' ? null : value;
- },
-
- setStyle: function(element, style) {
- element = $(element);
- for (var name in style)
- element.style[name.camelize()] = style[name];
- },
-
- getDimensions: function(element) {
- element = $(element);
- if (Element.getStyle(element, 'display') != 'none')
- return {width: element.offsetWidth, height: element.offsetHeight};
-
- // All *Width and *Height properties give 0 on elements with display none,
- // so enable the element temporarily
- var els = element.style;
- var originalVisibility = els.visibility;
- var originalPosition = els.position;
- els.visibility = 'hidden';
- els.position = 'absolute';
- els.display = '';
- var originalWidth = element.clientWidth;
- var originalHeight = element.clientHeight;
- els.display = 'none';
- els.position = originalPosition;
- els.visibility = originalVisibility;
- return {width: originalWidth, height: originalHeight};
- },
-
- makePositioned: function(element) {
- element = $(element);
- var pos = Element.getStyle(element, 'position');
- if (pos == 'static' || !pos) {
- element._madePositioned = true;
- element.style.position = 'relative';
- // Opera returns the offset relative to the positioning context, when an
- // element is position relative but top and left have not been defined
- if (window.opera) {
- element.style.top = 0;
- element.style.left = 0;
- }
- }
- },
-
- undoPositioned: function(element) {
- element = $(element);
- if (element._madePositioned) {
- element._madePositioned = undefined;
- element.style.position =
- element.style.top =
- element.style.left =
- element.style.bottom =
- element.style.right = '';
- }
- },
-
- makeClipping: function(element) {
- element = $(element);
- if (element._overflow) return;
- element._overflow = element.style.overflow;
- if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
- element.style.overflow = 'hidden';
- },
-
- undoClipping: function(element) {
- element = $(element);
- if (element._overflow) return;
- element.style.overflow = element._overflow;
- element._overflow = undefined;
- }
-}
-
-Object.extend(Element, Element.Methods);
-
-var _nativeExtensions = false;
-
-if(!HTMLElement && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
- var HTMLElement = {}
- HTMLElement.prototype = document.createElement('div').__proto__;
-}
-
-Element.addMethods = function(methods) {
- Object.extend(Element.Methods, methods || {});
-
- if(typeof HTMLElement != 'undefined') {
- var methods = Element.Methods, cache = Element.extend.cache;
- for (property in methods) {
- var value = methods[property];
- if (typeof value == 'function')
- HTMLElement.prototype[property] = cache.findOrStore(value);
- }
- _nativeExtensions = true;
- }
-}
-
-Element.addMethods();
-
-var Toggle = new Object();
-Toggle.display = Element.toggle;
-
-/*--------------------------------------------------------------------------*/
-
-Abstract.Insertion = function(adjacency) {
- this.adjacency = adjacency;
-}
-
-Abstract.Insertion.prototype = {
- initialize: function(element, content) {
- this.element = $(element);
- this.content = content.stripScripts();
-
- if (this.adjacency && this.element.insertAdjacentHTML) {
- try {
- this.element.insertAdjacentHTML(this.adjacency, this.content);
- } catch (e) {
- var tagName = this.element.tagName.toLowerCase();
- if (tagName == 'tbody' || tagName == 'tr') {
- this.insertContent(this.contentFromAnonymousTable());
- } else {
- throw e;
- }
- }
- } else {
- this.range = this.element.ownerDocument.createRange();
- if (this.initializeRange) this.initializeRange();
- this.insertContent([this.range.createContextualFragment(this.content)]);
- }
-
- setTimeout(function() {content.evalScripts()}, 10);
- },
-
- contentFromAnonymousTable: function() {
- var div = document.createElement('div');
- div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
- return $A(div.childNodes[0].childNodes[0].childNodes);
- }
-}
-
-var Insertion = new Object();
-
-Insertion.Before = Class.create();
-Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
- initializeRange: function() {
- this.range.setStartBefore(this.element);
- },
-
- insertContent: function(fragments) {
- fragments.each((function(fragment) {
- this.element.parentNode.insertBefore(fragment, this.element);
- }).bind(this));
- }
-});
-
-Insertion.Top = Class.create();
-Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
- initializeRange: function() {
- this.range.selectNodeContents(this.element);
- this.range.collapse(true);
- },
-
- insertContent: function(fragments) {
- fragments.reverse(false).each((function(fragment) {
- this.element.insertBefore(fragment, this.element.firstChild);
- }).bind(this));
- }
-});
-
-Insertion.Bottom = Class.create();
-Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
- initializeRange: function() {
- this.range.selectNodeContents(this.element);
- this.range.collapse(this.element);
- },
-
- insertContent: function(fragments) {
- fragments.each((function(fragment) {
- this.element.appendChild(fragment);
- }).bind(this));
- }
-});
-
-Insertion.After = Class.create();
-Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
- initializeRange: function() {
- this.range.setStartAfter(this.element);
- },
-
- insertContent: function(fragments) {
- fragments.each((function(fragment) {
- this.element.parentNode.insertBefore(fragment,
- this.element.nextSibling);
- }).bind(this));
- }
-});
-
-/*--------------------------------------------------------------------------*/
-
-Element.ClassNames = Class.create();
-Element.ClassNames.prototype = {
- initialize: function(element) {
- this.element = $(element);
- },
-
- _each: function(iterator) {
- this.element.className.split(/\s+/).select(function(name) {
- return name.length > 0;
- })._each(iterator);
- },
-
- set: function(className) {
- this.element.className = className;
- },
-
- add: function(classNameToAdd) {
- if (this.include(classNameToAdd)) return;
- this.set(this.toArray().concat(classNameToAdd).join(' '));
- },
-
- remove: function(classNameToRemove) {
- if (!this.include(classNameToRemove)) return;
- this.set(this.select(function(className) {
- return className != classNameToRemove;
- }).join(' '));
- },
-
- toString: function() {
- return this.toArray().join(' ');
- }
-}
-
-Object.extend(Element.ClassNames.prototype, Enumerable);
diff --git a/framework/Web/Javascripts/prototype/enumerable.js b/framework/Web/Javascripts/prototype/enumerable.js
deleted file mode 100644
index c3535b81..00000000
--- a/framework/Web/Javascripts/prototype/enumerable.js
+++ /dev/null
@@ -1,182 +0,0 @@
-var $break = new Object();
-var $continue = new Object();
-
-var Enumerable = {
- each: function(iterator) {
- var index = 0;
- try {
- this._each(function(value) {
- try {
- iterator(value, index++);
- } catch (e) {
- if (e != $continue) throw e;
- }
- });
- } catch (e) {
- if (e != $break) throw e;
- }
- },
-
- all: function(iterator) {
- var result = true;
- this.each(function(value, index) {
- result = result && !!(iterator || Prototype.K)(value, index);
- if (!result) throw $break;
- });
- return result;
- },
-
- any: function(iterator) {
- var result = true;
- this.each(function(value, index) {
- if (result = !!(iterator || Prototype.K)(value, index))
- throw $break;
- });
- return result;
- },
-
- collect: function(iterator) {
- var results = [];
- this.each(function(value, index) {
- results.push(iterator(value, index));
- });
- return results;
- },
-
- detect: function (iterator) {
- var result;
- this.each(function(value, index) {
- if (iterator(value, index)) {
- result = value;
- throw $break;
- }
- });
- return result;
- },
-
- findAll: function(iterator) {
- var results = [];
- this.each(function(value, index) {
- if (iterator(value, index))
- results.push(value);
- });
- return results;
- },
-
- grep: function(pattern, iterator) {
- var results = [];
- this.each(function(value, index) {
- var stringValue = value.toString();
- if (stringValue.match(pattern))
- results.push((iterator || Prototype.K)(value, index));
- })
- return results;
- },
-
- include: function(object) {
- var found = false;
- this.each(function(value) {
- if (value == object) {
- found = true;
- throw $break;
- }
- });
- return found;
- },
-
- inject: function(memo, iterator) {
- this.each(function(value, index) {
- memo = iterator(memo, value, index);
- });
- return memo;
- },
-
- invoke: function(method) {
- var args = $A(arguments).slice(1);
- return this.collect(function(value) {
- return value[method].apply(value, args);
- });
- },
-
- max: function(iterator) {
- var result;
- this.each(function(value, index) {
- value = (iterator || Prototype.K)(value, index);
- if (result == undefined || value >= result)
- result = value;
- });
- return result;
- },
-
- min: function(iterator) {
- var result;
- this.each(function(value, index) {
- value = (iterator || Prototype.K)(value, index);
- if (result == undefined || value < result)
- result = value;
- });
- return result;
- },
-
- partition: function(iterator) {
- var trues = [], falses = [];
- this.each(function(value, index) {
- ((iterator || Prototype.K)(value, index) ?
- trues : falses).push(value);
- });
- return [trues, falses];
- },
-
- pluck: function(property) {
- var results = [];
- this.each(function(value, index) {
- results.push(value[property]);
- });
- return results;
- },
-
- reject: function(iterator) {
- var results = [];
- this.each(function(value, index) {
- if (!iterator(value, index))
- results.push(value);
- });
- return results;
- },
-
- sortBy: function(iterator) {
- return this.collect(function(value, index) {
- return {value: value, criteria: iterator(value, index)};
- }).sort(function(left, right) {
- var a = left.criteria, b = right.criteria;
- return a < b ? -1 : a > b ? 1 : 0;
- }).pluck('value');
- },
-
- toArray: function() {
- return this.collect(Prototype.K);
- },
-
- zip: function() {
- var iterator = Prototype.K, args = $A(arguments);
- if (typeof args.last() == 'function')
- iterator = args.pop();
-
- var collections = [this].concat(args).map($A);
- return this.map(function(value, index) {
- return iterator(collections.pluck(index));
- });
- },
-
- inspect: function() {
- return '#<Enumerable:' + this.toArray().inspect() + '>';
- }
-}
-
-Object.extend(Enumerable, {
- map: Enumerable.collect,
- find: Enumerable.detect,
- select: Enumerable.findAll,
- member: Enumerable.include,
- entries: Enumerable.toArray
-});
diff --git a/framework/Web/Javascripts/prototype/event.js b/framework/Web/Javascripts/prototype/event.js
deleted file mode 100644
index 4def0e4a..00000000
--- a/framework/Web/Javascripts/prototype/event.js
+++ /dev/null
@@ -1,109 +0,0 @@
-if (!window.Event) {
- var Event = new Object();
-}
-
-Object.extend(Event, {
- KEY_BACKSPACE: 8,
- KEY_TAB: 9,
- KEY_RETURN: 13,
- KEY_ESC: 27,
- KEY_LEFT: 37,
- KEY_UP: 38,
- KEY_RIGHT: 39,
- KEY_DOWN: 40,
- KEY_DELETE: 46,
- KEY_SPACEBAR: 32,
-
- element: function(event) {
- return event.target || event.srcElement;
- },
-
- isLeftClick: function(event) {
- return (((event.which) && (event.which == 1)) ||
- ((event.button) && (event.button == 1)));
- },
-
- pointerX: function(event) {
- return event.pageX || (event.clientX +
- (document.documentElement.scrollLeft || document.body.scrollLeft));
- },
-
- pointerY: function(event) {
- return event.pageY || (event.clientY +
- (document.documentElement.scrollTop || document.body.scrollTop));
- },
-
- stop: function(event) {
- if (event.preventDefault) {
- event.preventDefault();
- event.stopPropagation();
- } else {
- event.returnValue = false;
- event.cancelBubble = true;
- }
- },
-
- // find the first node with the given tagName, starting from the
- // node the event was triggered on; traverses the DOM upwards
- findElement: function(event, tagName) {
- var element = Event.element(event);
- while (element.parentNode && (!element.tagName ||
- (element.tagName.toUpperCase() != tagName.toUpperCase())))
- element = element.parentNode;
- return element;
- },
-
- observers: false,
-
- _observeAndCache: function(element, name, observer, useCapture) {
- if (!this.observers) this.observers = [];
- if (element.addEventListener) {
- this.observers.push([element, name, observer, useCapture]);
- element.addEventListener(name, observer, useCapture);
- } else if (element.attachEvent) {
- this.observers.push([element, name, observer, useCapture]);
- element.attachEvent('on' + name, observer);
- }
- },
-
- unloadCache: function() {
- if (!Event.observers) return;
- for (var i = 0; i < Event.observers.length; i++) {
- Event.stopObserving.apply(this, Event.observers[i]);
- Event.observers[i][0] = null;
- }
- Event.observers = false;
- },
-
- observe: function(element, name, observer, useCapture) {
- var element = $(element);
- useCapture = useCapture || false;
-
- if (name == 'keypress' &&
- (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
- || element.attachEvent))
- name = 'keydown';
-
- this._observeAndCache(element, name, observer, useCapture);
- },
-
- stopObserving: function(element, name, observer, useCapture) {
- var element = $(element);
- useCapture = useCapture || false;
-
- if (name == 'keypress' &&
- (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
- || element.detachEvent))
- name = 'keydown';
-
- if (element.removeEventListener) {
- element.removeEventListener(name, observer, useCapture);
- } else if (element.detachEvent) {
- element.detachEvent('on' + name, observer);
- }
- }
-});
-
-/* prevent memory leaks in IE */
-if (navigator.appVersion.match(/\bMSIE\b/))
- Event.observe(window, 'unload', Event.unloadCache, false);
diff --git a/framework/Web/Javascripts/prototype/form.js b/framework/Web/Javascripts/prototype/form.js
deleted file mode 100644
index 41745353..00000000
--- a/framework/Web/Javascripts/prototype/form.js
+++ /dev/null
@@ -1,294 +0,0 @@
-var Field = {
- clear: function() {
- for (var i = 0; i < arguments.length; i++)
- $(arguments[i]).value = '';
- },
-
- focus: function(element) {
- $(element).focus();
- },
-
- present: function() {
- for (var i = 0; i < arguments.length; i++)
- if ($(arguments[i]).value == '') return false;
- return true;
- },
-
- select: function(element) {
- $(element).select();
- },
-
- activate: function(element) {
- element = $(element);
- element.focus();
- if (element.select)
- element.select();
- }
-}
-
-/*--------------------------------------------------------------------------*/
-
-var Form = {
- serialize: function(form) {
- var elements = Form.getElements($(form));
- var queryComponents = new Array();
-
- for (var i = 0; i < elements.length; i++) {
- var queryComponent = Form.Element.serialize(elements[i]);
- if (queryComponent)
- queryComponents.push(queryComponent);
- }
-
- return queryComponents.join('&');
- },
-
- getElements: function(form) {
- form = $(form);
- var elements = new Array();
-
- for (var tagName in Form.Element.Serializers) {
- var tagElements = form.getElementsByTagName(tagName);
- for (var j = 0; j < tagElements.length; j++)
- elements.push(tagElements[j]);
- }
- return elements;
- },
-
- getInputs: function(form, typeName, name) {
- form = $(form);
- var inputs = form.getElementsByTagName('input');
-
- if (!typeName && !name)
- return inputs;
-
- var matchingInputs = new Array();
- for (var i = 0; i < inputs.length; i++) {
- var input = inputs[i];
- if ((typeName && input.type != typeName) ||
- (name && input.name != name))
- continue;
- matchingInputs.push(input);
- }
-
- return matchingInputs;
- },
-
- disable: function(form) {
- var elements = Form.getElements(form);
- for (var i = 0; i < elements.length; i++) {
- var element = elements[i];
- element.blur();
- element.disabled = 'true';
- }
- },
-
- enable: function(form) {
- var elements = Form.getElements(form);
- for (var i = 0; i < elements.length; i++) {
- var element = elements[i];
- element.disabled = '';
- }
- },
-
- findFirstElement: function(form) {
- return Form.getElements(form).find(function(element) {
- return element.type != 'hidden' && !element.disabled &&
- ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
- });
- },
-
- focusFirstElement: function(form) {
- Field.activate(Form.findFirstElement(form));
- },
-
- reset: function(form) {
- $(form).reset();
- }
-}
-
-Form.Element = {
- serialize: function(element) {
- element = $(element);
- var method = element.tagName.toLowerCase();
- var parameter = Form.Element.Serializers[method](element);
-
- if (parameter) {
- var key = encodeURIComponent(parameter[0]);
- if (key.length == 0) return;
-
- if (parameter[1].constructor != Array)
- parameter[1] = [parameter[1]];
-
- return parameter[1].map(function(value) {
- return key + '=' + encodeURIComponent(value);
- }).join('&');
- }
- },
-
- getValue: function(element) {
- element = $(element);
- var method = element.tagName.toLowerCase();
- var parameter = Form.Element.Serializers[method](element);
-
- if (parameter)
- return parameter[1];
- }
-}
-
-Form.Element.Serializers = {
- input: function(element) {
- if(typeof(element.type) == "undefined")
- return false;
- switch (element.type.toLowerCase()) {
- case 'submit':
- case 'hidden':
- case 'password':
- case 'text':
- return Form.Element.Serializers.textarea(element);
- case 'checkbox':
- case 'radio':
- return Form.Element.Serializers.inputSelector(element);
- }
- return false;
- },
-
- inputSelector: function(element) {
- if (element.checked)
- return [element.name, element.value];
- },
-
- textarea: function(element) {
- return [element.name, element.value];
- },
-
- select: function(element) {
- return Form.Element.Serializers[element.type == 'select-one' ?
- 'selectOne' : 'selectMany'](element);
- },
-
- selectOne: function(element) {
- var value = '', opt, index = element.selectedIndex;
- if (index >= 0) {
- opt = element.options[index];
- value = opt.value || opt.text;
- }
- return [element.name, value];
- },
-
- selectMany: function(element) {
- var value = [];
- for (var i = 0; i < element.length; i++) {
- var opt = element.options[i];
- if (opt.selected)
- value.push(opt.value || opt.text);
- }
- return [element.name, value];
- }
-}
-
-/*--------------------------------------------------------------------------*/
-
-var $F = Form.Element.getValue;
-
-/*--------------------------------------------------------------------------*/
-
-Abstract.TimedObserver = function() {}
-Abstract.TimedObserver.prototype = {
- initialize: function(element, frequency, callback) {
- this.frequency = frequency;
- this.element = $(element);
- this.callback = callback;
-
- this.lastValue = this.getValue();
- this.registerCallback();
- },
-
- registerCallback: function() {
- setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
- },
-
- onTimerEvent: function() {
- var value = this.getValue();
- if (this.lastValue != value) {
- this.callback(this.element, value);
- this.lastValue = value;
- }
- }
-}
-
-Form.Element.Observer = Class.create();
-Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
- getValue: function() {
- return Form.Element.getValue(this.element);
- }
-});
-
-Form.Observer = Class.create();
-Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
- getValue: function() {
- return Form.serialize(this.element);
- }
-});
-
-/*--------------------------------------------------------------------------*/
-
-Abstract.EventObserver = function() {}
-Abstract.EventObserver.prototype = {
- initialize: function(element, callback) {
- this.element = $(element);
- this.callback = callback;
-
- this.lastValue = this.getValue();
- if (this.element.tagName.toLowerCase() == 'form')
- this.registerFormCallbacks();
- else
- this.registerCallback(this.element);
- },
-
- onElementEvent: function() {
- var value = this.getValue();
- if (this.lastValue != value) {
- this.callback(this.element, value);
- this.lastValue = value;
- }
- },
-
- registerFormCallbacks: function() {
- var elements = Form.getElements(this.element);
- for (var i = 0; i < elements.length; i++)
- this.registerCallback(elements[i]);
- },
-
- registerCallback: function(element) {
- if (element.type) {
- switch (element.type.toLowerCase()) {
- case 'checkbox':
- case 'radio':
- Event.observe(element, 'click', this.onElementEvent.bind(this));
- break;
- case 'password':
- case 'text':
- case 'textarea':
- case 'select-one':
- case 'select-multiple':
- Event.observe(element, 'change', this.onElementEvent.bind(this));
- break;
- }
- }
- }
-}
-
-Form.Element.EventObserver = Class.create();
-Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
- getValue: function() {
- return Form.Element.getValue(this.element);
- }
-});
-
-Form.EventObserver = Class.create();
-Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
- getValue: function() {
- return Form.serialize(this.element);
- }
-});
-
diff --git a/framework/Web/Javascripts/prototype/hash.js b/framework/Web/Javascripts/prototype/hash.js
deleted file mode 100644
index 1589189c..00000000
--- a/framework/Web/Javascripts/prototype/hash.js
+++ /dev/null
@@ -1,57 +0,0 @@
-var Hash = {
- _each: function(iterator) {
- for (var key in this) {
- var value = this[key];
- if (typeof value == 'function') continue;
-
- var pair = [key, value];
- pair.key = key;
- pair.value = value;
- iterator(pair);
- }
- },
-
- keys: function() {
- return this.pluck('key');
- },
-
- values: function() {
- return this.pluck('value');
- },
-
- merge: function(hash) {
- return $H(hash).inject($H(this), function(mergedHash, pair) {
- mergedHash[pair.key] = pair.value;
- return mergedHash;
- });
- },
-
- toQueryString: function() {
- return this.map(function(pair)
- {
- //special case for PHP, array post data.
- if(typeof(pair[1]) == 'object' || typeof(pair[1]) == 'array')
- {
- return $A(pair[1]).collect(function(value)
- {
- return encodeURIComponent(pair[0])+'='+encodeURIComponent(value);
- }).join('&');
- }
- else
- return pair.map(encodeURIComponent).join('=');
- }).join('&');
- },
-
- inspect: function() {
- return '#<Hash:{' + this.map(function(pair) {
- return pair.map(Object.inspect).join(': ');
- }).join(', ') + '}>';
- }
-}
-
-function $H(object) {
- var hash = Object.extend({}, object || {});
- Object.extend(hash, Enumerable);
- Object.extend(hash, Hash);
- return hash;
-}
diff --git a/framework/Web/Javascripts/prototype/position.js b/framework/Web/Javascripts/prototype/position.js
deleted file mode 100644
index c8d4804f..00000000
--- a/framework/Web/Javascripts/prototype/position.js
+++ /dev/null
@@ -1,233 +0,0 @@
-var Position = {
- // set to true if needed, warning: firefox performance problems
- // NOT neeeded for page scrolling, only if draggable contained in
- // scrollable elements
- includeScrollOffsets: false,
-
- // must be called before calling withinIncludingScrolloffset, every time the
- // page is scrolled
- prepare: function() {
- this.deltaX = window.pageXOffset
- || document.documentElement.scrollLeft
- || document.body.scrollLeft
- || 0;
- this.deltaY = window.pageYOffset
- || document.documentElement.scrollTop
- || document.body.scrollTop
- || 0;
- },
-
- realOffset: function(element) {
- var valueT = 0, valueL = 0;
- do {
- valueT += element.scrollTop || 0;
- valueL += element.scrollLeft || 0;
- element = element.parentNode;
- } while (element);
- return [valueL, valueT];
- },
-
- cumulativeOffset: function(element) {
- var valueT = 0, valueL = 0;
- do {
- valueT += element.offsetTop || 0;
- valueL += element.offsetLeft || 0;
- element = element.offsetParent;
- } while (element);
- return [valueL, valueT];
- },
-
- positionedOffset: function(element) {
- var valueT = 0, valueL = 0;
- do {
- valueT += element.offsetTop || 0;
- valueL += element.offsetLeft || 0;
- element = element.offsetParent;
- if (element) {
- p = Element.getStyle(element, 'position');
- if (p == 'relative' || p == 'absolute') break;
- }
- } while (element);
- return [valueL, valueT];
- },
-
- offsetParent: function(element) {
- if (element.offsetParent) return element.offsetParent;
- if (element == document.body) return element;
-
- while ((element = element.parentNode) && element != document.body)
- if (Element.getStyle(element, 'position') != 'static')
- return element;
-
- return document.body;
- },
-
- // caches x/y coordinate pair to use with overlap
- within: function(element, x, y) {
- if (this.includeScrollOffsets)
- return this.withinIncludingScrolloffsets(element, x, y);
- this.xcomp = x;
- this.ycomp = y;
- this.offset = this.cumulativeOffset(element);
-
- return (y >= this.offset[1] &&
- y < this.offset[1] + element.offsetHeight &&
- x >= this.offset[0] &&
- x < this.offset[0] + element.offsetWidth);
- },
-
- withinIncludingScrolloffsets: function(element, x, y) {
- var offsetcache = this.realOffset(element);
-
- this.xcomp = x + offsetcache[0] - this.deltaX;
- this.ycomp = y + offsetcache[1] - this.deltaY;
- this.offset = this.cumulativeOffset(element);
-
- return (this.ycomp >= this.offset[1] &&
- this.ycomp < this.offset[1] + element.offsetHeight &&
- this.xcomp >= this.offset[0] &&
- this.xcomp < this.offset[0] + element.offsetWidth);
- },
-
- // within must be called directly before
- overlap: function(mode, element) {
- if (!mode) return 0;
- if (mode == 'vertical')
- return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
- element.offsetHeight;
- if (mode == 'horizontal')
- return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
- element.offsetWidth;
- },
-
- clone: function(source, target) {
- source = $(source);
- target = $(target);
- target.style.position = 'absolute';
- var offsets = this.cumulativeOffset(source);
- target.style.top = offsets[1] + 'px';
- target.style.left = offsets[0] + 'px';
- target.style.width = source.offsetWidth + 'px';
- target.style.height = source.offsetHeight + 'px';
- },
-
- page: function(forElement) {
- var valueT = 0, valueL = 0;
-
- var element = forElement;
- do {
- valueT += element.offsetTop || 0;
- valueL += element.offsetLeft || 0;
-
- // Safari fix
- if (element.offsetParent==document.body)
- if (Element.getStyle(element,'position')=='absolute') break;
-
- } while (element = element.offsetParent);
-
- element = forElement;
- do {
- valueT -= element.scrollTop || 0;
- valueL -= element.scrollLeft || 0;
- } while (element = element.parentNode);
-
- return [valueL, valueT];
- },
-
- clone: function(source, target) {
- var options = Object.extend({
- setLeft: true,
- setTop: true,
- setWidth: true,
- setHeight: true,
- offsetTop: 0,
- offsetLeft: 0
- }, arguments[2] || {})
-
- // find page position of source
- source = $(source);
- var p = Position.page(source);
-
- // find coordinate system to use
- target = $(target);
- var delta = [0, 0];
- var parent = null;
- // delta [0,0] will do fine with position: fixed elements,
- // position:absolute needs offsetParent deltas
- if (Element.getStyle(target,'position') == 'absolute') {
- parent = Position.offsetParent(target);
- delta = Position.page(parent);
- }
-
- // correct by body offsets (fixes Safari)
- if (parent == document.body) {
- delta[0] -= document.body.offsetLeft;
- delta[1] -= document.body.offsetTop;
- }
-
- // set position
- if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
- if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
- if(options.setWidth) target.style.width = source.offsetWidth + 'px';
- if(options.setHeight) target.style.height = source.offsetHeight + 'px';
- },
-
- absolutize: function(element) {
- element = $(element);
- if (element.style.position == 'absolute') return;
- Position.prepare();
-
- var offsets = Position.positionedOffset(element);
- var top = offsets[1];
- var left = offsets[0];
- var width = element.clientWidth;
- var height = element.clientHeight;
-
- element._originalLeft = left - parseFloat(element.style.left || 0);
- element._originalTop = top - parseFloat(element.style.top || 0);
- element._originalWidth = element.style.width;
- element._originalHeight = element.style.height;
-
- element.style.position = 'absolute';
- element.style.top = top + 'px';;
- element.style.left = left + 'px';;
- element.style.width = width + 'px';;
- element.style.height = height + 'px';;
- },
-
- relativize: function(element) {
- element = $(element);
- if (element.style.position == 'relative') return;
- Position.prepare();
-
- element.style.position = 'relative';
- var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
- var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
-
- element.style.top = top + 'px';
- element.style.left = left + 'px';
- element.style.height = element._originalHeight;
- element.style.width = element._originalWidth;
- }
-}
-
-// Safari returns margins on body which is incorrect if the child is absolutely
-// positioned. For performance reasons, redefine Position.cumulativeOffset for
-// KHTML/WebKit only.
-if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
- Position.cumulativeOffset = function(element) {
- var valueT = 0, valueL = 0;
- do {
- valueT += element.offsetTop || 0;
- valueL += element.offsetLeft || 0;
- if (element.offsetParent == document.body)
- if (Element.getStyle(element, 'position') == 'absolute') break;
-
- element = element.offsetParent;
- } while (element);
-
- return [valueL, valueT];
- }
-}
-
-
diff --git a/framework/Web/Javascripts/prototype/prototype.js b/framework/Web/Javascripts/prototype/prototype.js
index 16701d70..2a1fef0d 100644
--- a/framework/Web/Javascripts/prototype/prototype.js
+++ b/framework/Web/Javascripts/prototype/prototype.js
@@ -1,24 +1,3216 @@
-/* Prototype JavaScript framework, version <%= PROTOTYPE_VERSION %>
- * (c) 2005 Sam Stephenson <sam@conio.net>
+/* Prototype JavaScript framework, version 1.5.1_rc2
+ * (c) 2005-2007 Sam Stephenson
*
* Prototype is freely distributable under the terms of an MIT-style license.
- * For details, see the Prototype web site: http://prototype.conio.net/
+ * For details, see the Prototype web site: http://www.prototypejs.org/
*
/*--------------------------------------------------------------------------*/
var Prototype = {
- Version: '1.50',
+ Version: '1.5.1_rc2',
+
+ Browser: {
+ IE: !!(window.attachEvent && !window.opera),
+ Opera: !!window.opera,
+ WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
+ Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1
+ },
+ BrowserFeatures: {
+ XPath: !!document.evaluate,
+ ElementExtensions: !!window.HTMLElement,
+ SpecificElementExtensions:
+ (document.createElement('div').__proto__ !==
+ document.createElement('form').__proto__)
+ },
+
ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
-
emptyFunction: function() {},
- K: function(x) {return x}
+ K: function(x) { return x }
+}
+
+var Class = {
+ create: function() {
+ return function() {
+ this.initialize.apply(this, arguments);
+ }
+ }
+}
+
+var Abstract = new Object();
+
+Object.extend = function(destination, source) {
+ for (var property in source) {
+ destination[property] = source[property];
+ }
+ return destination;
+}
+
+Object.extend(Object, {
+ inspect: function(object) {
+ try {
+ if (object === undefined) return 'undefined';
+ if (object === null) return 'null';
+ return object.inspect ? object.inspect() : object.toString();
+ } catch (e) {
+ if (e instanceof RangeError) return '...';
+ throw e;
+ }
+ },
+
+ toJSON: function(object) {
+ var type = typeof object;
+ switch(type) {
+ case 'undefined':
+ case 'function':
+ case 'unknown': return;
+ case 'boolean': return object.toString();
+ }
+ if (object === null) return 'null';
+ if (object.toJSON) return object.toJSON();
+ if (object.ownerDocument === document) return;
+ var results = [];
+ for (var property in object) {
+ var value = Object.toJSON(object[property]);
+ if (value !== undefined)
+ results.push(property.toJSON() + ':' + value);
+ }
+ return '{' + results.join(',') + '}';
+ },
+
+ keys: function(object) {
+ var keys = [];
+ for (var property in object)
+ keys.push(property);
+ return keys;
+ },
+
+ values: function(object) {
+ var values = [];
+ for (var property in object)
+ values.push(object[property]);
+ return values;
+ },
+
+ clone: function(object) {
+ return Object.extend({}, object);
+ }
+});
+
+Function.prototype.bind = function() {
+ var __method = this, args = $A(arguments), object = args.shift();
+ return function() {
+ return __method.apply(object, args.concat($A(arguments)));
+ }
+}
+
+Function.prototype.bindAsEventListener = function(object) {
+ var __method = this, args = $A(arguments), object = args.shift();
+ return function(event) {
+ return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments)));
+ }
+}
+
+Object.extend(Number.prototype, {
+ toColorPart: function() {
+ return this.toPaddedString(2, 16);
+ },
+
+ succ: function() {
+ return this + 1;
+ },
+
+ times: function(iterator) {
+ $R(0, this, true).each(iterator);
+ return this;
+ },
+
+ toPaddedString: function(length, radix) {
+ var string = this.toString(radix || 10);
+ return '0'.times(length - string.length) + string;
+ },
+
+ toJSON: function() {
+ return isFinite(this) ? this.toString() : 'null';
+ }
+});
+
+Date.prototype.toJSON = function() {
+ return '"' + this.getFullYear() + '-' +
+ (this.getMonth() + 1).toPaddedString(2) + '-' +
+ this.getDate().toPaddedString(2) + 'T' +
+ this.getHours().toPaddedString(2) + ':' +
+ this.getMinutes().toPaddedString(2) + ':' +
+ this.getSeconds().toPaddedString(2) + '"';
+};
+
+var Try = {
+ these: function() {
+ var returnValue;
+
+ for (var i = 0, length = arguments.length; i < length; i++) {
+ var lambda = arguments[i];
+ try {
+ returnValue = lambda();
+ break;
+ } catch (e) {}
+ }
+
+ return returnValue;
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var PeriodicalExecuter = Class.create();
+PeriodicalExecuter.prototype = {
+ initialize: function(callback, frequency) {
+ this.callback = callback;
+ this.frequency = frequency;
+ this.currentlyExecuting = false;
+
+ this.registerCallback();
+ },
+
+ registerCallback: function() {
+ this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+ },
+
+ stop: function() {
+ if (!this.timer) return;
+ clearInterval(this.timer);
+ this.timer = null;
+ },
+
+ onTimerEvent: function() {
+ if (!this.currentlyExecuting) {
+ try {
+ this.currentlyExecuting = true;
+ this.callback(this);
+ } finally {
+ this.currentlyExecuting = false;
+ }
+ }
+ }
+}
+Object.extend(String, {
+ interpret: function(value) {
+ return value == null ? '' : String(value);
+ },
+ specialChar: {
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '\\': '\\\\'
+ }
+});
+
+Object.extend(String.prototype, {
+ gsub: function(pattern, replacement) {
+ var result = '', source = this, match;
+ replacement = arguments.callee.prepareReplacement(replacement);
+
+ while (source.length > 0) {
+ if (match = source.match(pattern)) {
+ result += source.slice(0, match.index);
+ result += String.interpret(replacement(match));
+ source = source.slice(match.index + match[0].length);
+ } else {
+ result += source, source = '';
+ }
+ }
+ return result;
+ },
+
+ sub: function(pattern, replacement, count) {
+ replacement = this.gsub.prepareReplacement(replacement);
+ count = count === undefined ? 1 : count;
+
+ return this.gsub(pattern, function(match) {
+ if (--count < 0) return match[0];
+ return replacement(match);
+ });
+ },
+
+ scan: function(pattern, iterator) {
+ this.gsub(pattern, iterator);
+ return this;
+ },
+
+ truncate: function(length, truncation) {
+ length = length || 30;
+ truncation = truncation === undefined ? '...' : truncation;
+ return this.length > length ?
+ this.slice(0, length - truncation.length) + truncation : this;
+ },
+
+ strip: function() {
+ return this.replace(/^\s+/, '').replace(/\s+$/, '');
+ },
+
+ stripTags: function() {
+ return this.replace(/<\/?[^>]+>/gi, '');
+ },
+
+ stripScripts: function() {
+ return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
+ },
+
+ extractScripts: function() {
+ var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
+ var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
+ return (this.match(matchAll) || []).map(function(scriptTag) {
+ return (scriptTag.match(matchOne) || ['', ''])[1];
+ });
+ },
+
+ evalScripts: function() {
+ return this.extractScripts().map(function(script) { return eval(script) });
+ },
+
+ escapeHTML: function() {
+ var self = arguments.callee;
+ self.text.data = this;
+ return self.div.innerHTML;
+ },
+
+ unescapeHTML: function() {
+ var div = document.createElement('div');
+ div.innerHTML = this.stripTags();
+ return div.childNodes[0] ? (div.childNodes.length > 1 ?
+ $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
+ div.childNodes[0].nodeValue) : '';
+ },
+
+ toQueryParams: function(separator) {
+ var match = this.strip().match(/([^?#]*)(#.*)?$/);
+ if (!match) return {};
+
+ return match[1].split(separator || '&').inject({}, function(hash, pair) {
+ if ((pair = pair.split('='))[0]) {
+ var name = decodeURIComponent(pair[0]);
+ var value = pair[1] ? decodeURIComponent(pair[1]) : undefined;
+
+ if (hash[name] !== undefined) {
+ if (hash[name].constructor != Array)
+ hash[name] = [hash[name]];
+ if (value) hash[name].push(value);
+ }
+ else hash[name] = value;
+ }
+ return hash;
+ });
+ },
+
+ toArray: function() {
+ return this.split('');
+ },
+
+ succ: function() {
+ return this.slice(0, this.length - 1) +
+ String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
+ },
+
+ times: function(count) {
+ var result = '';
+ for (var i = 0; i < count; i++) result += this;
+ return result;
+ },
+
+ camelize: function() {
+ var parts = this.split('-'), len = parts.length;
+ if (len == 1) return parts[0];
+
+ var camelized = this.charAt(0) == '-'
+ ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
+ : parts[0];
+
+ for (var i = 1; i < len; i++)
+ camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
+
+ return camelized;
+ },
+
+ capitalize: function() {
+ return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
+ },
+
+ underscore: function() {
+ return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
+ },
+
+ dasherize: function() {
+ return this.gsub(/_/,'-');
+ },
+
+ inspect: function(useDoubleQuotes) {
+ var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
+ var character = String.specialChar[match[0]];
+ return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
+ });
+ if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
+ return "'" + escapedString.replace(/'/g, '\\\'') + "'";
+ },
+
+ toJSON: function() {
+ return this.inspect(true);
+ },
+
+ evalJSON: function(sanitize) {
+ try {
+ if (!sanitize || (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(this)))
+ return eval('(' + this + ')');
+ } catch (e) {}
+ throw new SyntaxError('Badly formated JSON string: ' + this.inspect());
+ },
+
+ include: function(pattern) {
+ return this.indexOf(pattern) > -1;
+ },
+
+ startsWith: function(pattern) {
+ return this.indexOf(pattern) == 0;
+ },
+
+ endsWith: function(pattern) {
+ return this.lastIndexOf(pattern) == (this.length - pattern.length);
+ },
+
+ empty: function() {
+ return this == '';
+ },
+
+ blank: function() {
+ return /^\s*$/.test(this);
+ }
+});
+
+String.prototype.gsub.prepareReplacement = function(replacement) {
+ if (typeof replacement == 'function') return replacement;
+ var template = new Template(replacement);
+ return function(match) { return template.evaluate(match) };
+}
+
+String.prototype.parseQuery = String.prototype.toQueryParams;
+
+Object.extend(String.prototype.escapeHTML, {
+ div: document.createElement('div'),
+ text: document.createTextNode('')
+});
+
+with (String.prototype.escapeHTML) div.appendChild(text);
+
+var Template = Class.create();
+Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
+Template.prototype = {
+ initialize: function(template, pattern) {
+ this.template = template.toString();
+ this.pattern = pattern || Template.Pattern;
+ },
+
+ evaluate: function(object) {
+ return this.template.gsub(this.pattern, function(match) {
+ var before = match[1];
+ if (before == '\\') return match[2];
+ return before + String.interpret(object[match[3]]);
+ });
+ }
+}
+
+var $break = new Object();
+var $continue = new Object();
+
+var Enumerable = {
+ each: function(iterator) {
+ var index = 0;
+ try {
+ this._each(function(value) {
+ iterator(value, index++);
+ });
+ } catch (e) {
+ if (e != $break) throw e;
+ }
+ return this;
+ },
+
+ eachSlice: function(number, iterator) {
+ var index = -number, slices = [], array = this.toArray();
+ while ((index += number) < array.length)
+ slices.push(array.slice(index, index+number));
+ return slices.map(iterator);
+ },
+
+ all: function(iterator) {
+ var result = true;
+ this.each(function(value, index) {
+ result = result && !!(iterator || Prototype.K)(value, index);
+ if (!result) throw $break;
+ });
+ return result;
+ },
+
+ any: function(iterator) {
+ var result = false;
+ this.each(function(value, index) {
+ if (result = !!(iterator || Prototype.K)(value, index))
+ throw $break;
+ });
+ return result;
+ },
+
+ collect: function(iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ results.push((iterator || Prototype.K)(value, index));
+ });
+ return results;
+ },
+
+ detect: function(iterator) {
+ var result;
+ this.each(function(value, index) {
+ if (iterator(value, index)) {
+ result = value;
+ throw $break;
+ }
+ });
+ return result;
+ },
+
+ findAll: function(iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ if (iterator(value, index))
+ results.push(value);
+ });
+ return results;
+ },
+
+ grep: function(pattern, iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ var stringValue = value.toString();
+ if (stringValue.match(pattern))
+ results.push((iterator || Prototype.K)(value, index));
+ })
+ return results;
+ },
+
+ include: function(object) {
+ var found = false;
+ this.each(function(value) {
+ if (value == object) {
+ found = true;
+ throw $break;
+ }
+ });
+ return found;
+ },
+
+ inGroupsOf: function(number, fillWith) {
+ fillWith = fillWith === undefined ? null : fillWith;
+ return this.eachSlice(number, function(slice) {
+ while(slice.length < number) slice.push(fillWith);
+ return slice;
+ });
+ },
+
+ inject: function(memo, iterator) {
+ this.each(function(value, index) {
+ memo = iterator(memo, value, index);
+ });
+ return memo;
+ },
+
+ invoke: function(method) {
+ var args = $A(arguments).slice(1);
+ return this.map(function(value) {
+ return value[method].apply(value, args);
+ });
+ },
+
+ max: function(iterator) {
+ var result;
+ this.each(function(value, index) {
+ value = (iterator || Prototype.K)(value, index);
+ if (result == undefined || value >= result)
+ result = value;
+ });
+ return result;
+ },
+
+ min: function(iterator) {
+ var result;
+ this.each(function(value, index) {
+ value = (iterator || Prototype.K)(value, index);
+ if (result == undefined || value < result)
+ result = value;
+ });
+ return result;
+ },
+
+ partition: function(iterator) {
+ var trues = [], falses = [];
+ this.each(function(value, index) {
+ ((iterator || Prototype.K)(value, index) ?
+ trues : falses).push(value);
+ });
+ return [trues, falses];
+ },
+
+ pluck: function(property) {
+ var results = [];
+ this.each(function(value, index) {
+ results.push(value[property]);
+ });
+ return results;
+ },
+
+ reject: function(iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ if (!iterator(value, index))
+ results.push(value);
+ });
+ return results;
+ },
+
+ sortBy: function(iterator) {
+ return this.map(function(value, index) {
+ return {value: value, criteria: iterator(value, index)};
+ }).sort(function(left, right) {
+ var a = left.criteria, b = right.criteria;
+ return a < b ? -1 : a > b ? 1 : 0;
+ }).pluck('value');
+ },
+
+ toArray: function() {
+ return this.map();
+ },
+
+ zip: function() {
+ var iterator = Prototype.K, args = $A(arguments);
+ if (typeof args.last() == 'function')
+ iterator = args.pop();
+
+ var collections = [this].concat(args).map($A);
+ return this.map(function(value, index) {
+ return iterator(collections.pluck(index));
+ });
+ },
+
+ size: function() {
+ return this.toArray().length;
+ },
+
+ inspect: function() {
+ return '#<Enumerable:' + this.toArray().inspect() + '>';
+ }
+}
+
+Object.extend(Enumerable, {
+ map: Enumerable.collect,
+ find: Enumerable.detect,
+ select: Enumerable.findAll,
+ member: Enumerable.include,
+ entries: Enumerable.toArray
+});
+var $A = Array.from = function(iterable) {
+ if (!iterable) return [];
+ if (iterable.toArray) {
+ return iterable.toArray();
+ } else {
+ var results = [];
+ for (var i = 0, length = iterable.length; i < length; i++)
+ results.push(iterable[i]);
+ return results;
+ }
+}
+
+if (Prototype.Browser.WebKit) {
+ $A = Array.from = function(iterable) {
+ if (!iterable) return [];
+ if (!(typeof iterable == 'function' && iterable == '[object NodeList]') &&
+ iterable.toArray) {
+ return iterable.toArray();
+ } else {
+ var results = [];
+ for (var i = 0, length = iterable.length; i < length; i++)
+ results.push(iterable[i]);
+ return results;
+ }
+ }
+}
+
+Object.extend(Array.prototype, Enumerable);
+
+if (!Array.prototype._reverse)
+ Array.prototype._reverse = Array.prototype.reverse;
+
+Object.extend(Array.prototype, {
+ _each: function(iterator) {
+ for (var i = 0, length = this.length; i < length; i++)
+ iterator(this[i]);
+ },
+
+ clear: function() {
+ this.length = 0;
+ return this;
+ },
+
+ first: function() {
+ return this[0];
+ },
+
+ last: function() {
+ return this[this.length - 1];
+ },
+
+ compact: function() {
+ return this.select(function(value) {
+ return value != null;
+ });
+ },
+
+ flatten: function() {
+ return this.inject([], function(array, value) {
+ return array.concat(value && value.constructor == Array ?
+ value.flatten() : [value]);
+ });
+ },
+
+ without: function() {
+ var values = $A(arguments);
+ return this.select(function(value) {
+ return !values.include(value);
+ });
+ },
+
+ indexOf: function(object) {
+ for (var i = 0, length = this.length; i < length; i++)
+ if (this[i] == object) return i;
+ return -1;
+ },
+
+ reverse: function(inline) {
+ return (inline !== false ? this : this.toArray())._reverse();
+ },
+
+ reduce: function() {
+ return this.length > 1 ? this : this[0];
+ },
+
+ uniq: function(sorted) {
+ return this.inject([], function(array, value, index) {
+ if (0 == index || (sorted ? array.last() != value : !array.include(value)))
+ array.push(value);
+ return array;
+ });
+ },
+
+ clone: function() {
+ return [].concat(this);
+ },
+
+ size: function() {
+ return this.length;
+ },
+
+ inspect: function() {
+ return '[' + this.map(Object.inspect).join(', ') + ']';
+ },
+
+ toJSON: function() {
+ var results = [];
+ this.each(function(object) {
+ var value = Object.toJSON(object);
+ if (value !== undefined) results.push(value);
+ });
+ return '[' + results.join(',') + ']';
+ }
+});
+
+Array.prototype.toArray = Array.prototype.clone;
+
+function $w(string) {
+ string = string.strip();
+ return string ? string.split(/\s+/) : [];
+}
+
+if (Prototype.Browser.Opera){
+ Array.prototype.concat = function() {
+ var array = [];
+ for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
+ for (var i = 0, length = arguments.length; i < length; i++) {
+ if (arguments[i].constructor == Array) {
+ for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
+ array.push(arguments[i][j]);
+ } else {
+ array.push(arguments[i]);
+ }
+ }
+ return array;
+ }
+}
+var Hash = function(object) {
+ if (object instanceof Hash) this.merge(object);
+ else Object.extend(this, object || {});
+};
+
+Object.extend(Hash, {
+ toQueryString: function(obj) {
+ var parts = [];
+ parts.add = arguments.callee.addPair;
+
+ this.prototype._each.call(obj, function(pair) {
+ if (!pair.key) return;
+ var value = pair.value;
+
+ if (value && typeof value == 'object') {
+ if (value.constructor == Array) value.each(function(value) {
+ parts.add(pair.key, value);
+ });
+ return;
+ }
+ parts.add(pair.key, value);
+ });
+
+ return parts.join('&');
+ },
+
+ toJSON: function(object) {
+ var results = [];
+ this.prototype._each.call(object, function(pair) {
+ var value = Object.toJSON(pair.value);
+ if (value !== undefined) results.push(pair.key.toJSON() + ':' + value);
+ });
+ return '{' + results.join(',') + '}';
+ }
+});
+
+Hash.toQueryString.addPair = function(key, value, prefix) {
+ if (value == null) return;
+ key = encodeURIComponent(key);
+ this.push(key + '=' + (value == null ? '' : encodeURIComponent(value)));
+}
+
+Object.extend(Hash.prototype, Enumerable);
+Object.extend(Hash.prototype, {
+ _each: function(iterator) {
+ for (var key in this) {
+ var value = this[key];
+ if (value && value == Hash.prototype[key]) continue;
+
+ var pair = [key, value];
+ pair.key = key;
+ pair.value = value;
+ iterator(pair);
+ }
+ },
+
+ keys: function() {
+ return this.pluck('key');
+ },
+
+ values: function() {
+ return this.pluck('value');
+ },
+
+ merge: function(hash) {
+ return $H(hash).inject(this, function(mergedHash, pair) {
+ mergedHash[pair.key] = pair.value;
+ return mergedHash;
+ });
+ },
+
+ remove: function() {
+ var result;
+ for(var i = 0, length = arguments.length; i < length; i++) {
+ var value = this[arguments[i]];
+ if (value !== undefined){
+ if (result === undefined) result = value;
+ else {
+ if (result.constructor != Array) result = [result];
+ result.push(value)
+ }
+ }
+ delete this[arguments[i]];
+ }
+ return result;
+ },
+
+ toQueryString: function() {
+ return Hash.toQueryString(this);
+ },
+
+ inspect: function() {
+ return '#<Hash:{' + this.map(function(pair) {
+ return pair.map(Object.inspect).join(': ');
+ }).join(', ') + '}>';
+ },
+
+ toJSON: function() {
+ return Hash.toJSON(this);
+ }
+});
+
+function $H(object) {
+ if (object instanceof Hash) return object;
+ return new Hash(object);
+};
+
+// Safari iterates over shadowed properties
+if (function() {
+ var i = 0, Test = function(value) { this.key = value };
+ Test.prototype.key = 'foo';
+ for (var property in new Test('bar')) i++;
+ return i > 1;
+}()) Hash.prototype._each = function(iterator) {
+ var cache = [];
+ for (var key in this) {
+ var value = this[key];
+ if ((value && value == Hash.prototype[key]) || cache.include(key)) continue;
+ cache.push(key);
+ var pair = [key, value];
+ pair.key = key;
+ pair.value = value;
+ iterator(pair);
+ }
+};
+ObjectRange = Class.create();
+Object.extend(ObjectRange.prototype, Enumerable);
+Object.extend(ObjectRange.prototype, {
+ initialize: function(start, end, exclusive) {
+ this.start = start;
+ this.end = end;
+ this.exclusive = exclusive;
+ },
+
+ _each: function(iterator) {
+ var value = this.start;
+ while (this.include(value)) {
+ iterator(value);
+ value = value.succ();
+ }
+ },
+
+ include: function(value) {
+ if (value < this.start)
+ return false;
+ if (this.exclusive)
+ return value < this.end;
+ return value <= this.end;
+ }
+});
+
+var $R = function(start, end, exclusive) {
+ return new ObjectRange(start, end, exclusive);
+}
+
+var Ajax = {
+ getTransport: function() {
+ return Try.these(
+ function() {return new XMLHttpRequest()},
+ function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+ function() {return new ActiveXObject('Microsoft.XMLHTTP')}
+ ) || false;
+ },
+
+ activeRequestCount: 0
+}
+
+Ajax.Responders = {
+ responders: [],
+
+ _each: function(iterator) {
+ this.responders._each(iterator);
+ },
+
+ register: function(responder) {
+ if (!this.include(responder))
+ this.responders.push(responder);
+ },
+
+ unregister: function(responder) {
+ this.responders = this.responders.without(responder);
+ },
+
+ dispatch: function(callback, request, transport, json) {
+ this.each(function(responder) {
+ if (typeof responder[callback] == 'function') {
+ try {
+ responder[callback].apply(responder, [request, transport, json]);
+ } catch (e) {}
+ }
+ });
+ }
+};
+
+Object.extend(Ajax.Responders, Enumerable);
+
+Ajax.Responders.register({
+ onCreate: function() {
+ Ajax.activeRequestCount++;
+ },
+ onComplete: function() {
+ Ajax.activeRequestCount--;
+ }
+});
+
+Ajax.Base = function() {};
+Ajax.Base.prototype = {
+ setOptions: function(options) {
+ this.options = {
+ method: 'post',
+ asynchronous: true,
+ contentType: 'application/x-www-form-urlencoded',
+ encoding: 'UTF-8',
+ parameters: ''
+ }
+ Object.extend(this.options, options || {});
+
+ this.options.method = this.options.method.toLowerCase();
+ if (typeof this.options.parameters == 'string')
+ this.options.parameters = this.options.parameters.toQueryParams();
+ }
+}
+
+Ajax.Request = Class.create();
+Ajax.Request.Events =
+ ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
+ _complete: false,
+
+ initialize: function(url, options) {
+ this.transport = Ajax.getTransport();
+ this.setOptions(options);
+ this.request(url);
+ },
+
+ request: function(url) {
+ this.url = url;
+ this.method = this.options.method;
+ var params = Object.clone(this.options.parameters);
+
+ if (!['get', 'post'].include(this.method)) {
+ // simulate other verbs over post
+ params['_method'] = this.method;
+ this.method = 'post';
+ }
+
+ this.parameters = params;
+
+ if (params = Hash.toQueryString(params)) {
+ // when GET, append parameters to URL
+ if (this.method == 'get')
+ this.url += (this.url.include('?') ? '&' : '?') + params;
+ else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
+ params += '&_=';
+ }
+
+ try {
+ Ajax.Responders.dispatch('onCreate', this, this.transport);
+
+ this.transport.open(this.method.toUpperCase(), this.url,
+ this.options.asynchronous);
+
+ if (this.options.asynchronous)
+ setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
+
+ this.transport.onreadystatechange = this.onStateChange.bind(this);
+ this.setRequestHeaders();
+
+ this.body = this.method == 'post' ? (this.options.postBody || params) : null;
+ this.transport.send(this.body);
+
+ /* Force Firefox to handle ready state 4 for synchronous requests */
+ if (!this.options.asynchronous && this.transport.overrideMimeType)
+ this.onStateChange();
+
+ }
+ catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ onStateChange: function() {
+ var readyState = this.transport.readyState;
+ if (readyState > 1 && !((readyState == 4) && this._complete))
+ this.respondToReadyState(this.transport.readyState);
+ },
+
+ setRequestHeaders: function() {
+ var headers = {
+ 'X-Requested-With': 'XMLHttpRequest',
+ 'X-Prototype-Version': Prototype.Version,
+ 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
+ };
+
+ if (this.method == 'post') {
+ headers['Content-type'] = this.options.contentType +
+ (this.options.encoding ? '; charset=' + this.options.encoding : '');
+
+ /* Force "Connection: close" for older Mozilla browsers to work
+ * around a bug where XMLHttpRequest sends an incorrect
+ * Content-length header. See Mozilla Bugzilla #246651.
+ */
+ if (this.transport.overrideMimeType &&
+ (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
+ headers['Connection'] = 'close';
+ }
+
+ // user-defined headers
+ if (typeof this.options.requestHeaders == 'object') {
+ var extras = this.options.requestHeaders;
+
+ if (typeof extras.push == 'function')
+ for (var i = 0, length = extras.length; i < length; i += 2)
+ headers[extras[i]] = extras[i+1];
+ else
+ $H(extras).each(function(pair) { headers[pair.key] = pair.value });
+ }
+
+ for (var name in headers)
+ this.transport.setRequestHeader(name, headers[name]);
+ },
+
+ success: function() {
+ return !this.transport.status
+ || (this.transport.status >= 200 && this.transport.status < 300);
+ },
+
+ respondToReadyState: function(readyState) {
+ var state = Ajax.Request.Events[readyState];
+ var transport = this.transport, json = this.evalJSON();
+
+ if (state == 'Complete') {
+ try {
+ this._complete = true;
+ (this.options['on' + this.transport.status]
+ || this.options['on' + (this.success() ? 'Success' : 'Failure')]
+ || Prototype.emptyFunction)(transport, json);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ if ((this.getHeader('Content-type') || 'text/javascript').strip().
+ match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
+ this.evalResponse();
+ }
+
+ try {
+ (this.options['on' + state] || Prototype.emptyFunction)(transport, json);
+ Ajax.Responders.dispatch('on' + state, this, transport, json);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ if (state == 'Complete') {
+ // avoid memory leak in MSIE: clean up
+ this.transport.onreadystatechange = Prototype.emptyFunction;
+ }
+ },
+
+ getHeader: function(name) {
+ try {
+ return this.transport.getResponseHeader(name);
+ } catch (e) { return null }
+ },
+
+ evalJSON: function() {
+ try {
+ var json = this.getHeader('X-JSON');
+ return json ? eval('(' + json + ')') : null;
+ } catch (e) { return null }
+ },
+
+ evalResponse: function() {
+ try {
+ return eval(this.transport.responseText);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ dispatchException: function(exception) {
+ (this.options.onException || Prototype.emptyFunction)(this, exception);
+ Ajax.Responders.dispatch('onException', this, exception);
+ }
+});
+
+Ajax.Updater = Class.create();
+
+Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
+ initialize: function(container, url, options) {
+ this.container = {
+ success: (container.success || container),
+ failure: (container.failure || (container.success ? null : container))
+ }
+
+ this.transport = Ajax.getTransport();
+ this.setOptions(options);
+
+ var onComplete = this.options.onComplete || Prototype.emptyFunction;
+ this.options.onComplete = (function(transport, param) {
+ this.updateContent();
+ onComplete(transport, param);
+ }).bind(this);
+
+ this.request(url);
+ },
+
+ updateContent: function() {
+ var receiver = this.container[this.success() ? 'success' : 'failure'];
+ var response = this.transport.responseText;
+
+ if (!this.options.evalScripts) response = response.stripScripts();
+
+ if (receiver = $(receiver)) {
+ if (this.options.insertion)
+ new this.options.insertion(receiver, response);
+ else
+ receiver.update(response);
+ }
+
+ if (this.success()) {
+ if (this.onComplete)
+ setTimeout(this.onComplete.bind(this), 10);
+ }
+ }
+});
+
+Ajax.PeriodicalUpdater = Class.create();
+Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
+ initialize: function(container, url, options) {
+ this.setOptions(options);
+ this.onComplete = this.options.onComplete;
+
+ this.frequency = (this.options.frequency || 2);
+ this.decay = (this.options.decay || 1);
+
+ this.updater = {};
+ this.container = container;
+ this.url = url;
+
+ this.start();
+ },
+
+ start: function() {
+ this.options.onComplete = this.updateComplete.bind(this);
+ this.onTimerEvent();
+ },
+
+ stop: function() {
+ this.updater.options.onComplete = undefined;
+ clearTimeout(this.timer);
+ (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
+ },
+
+ updateComplete: function(request) {
+ if (this.options.decay) {
+ this.decay = (request.responseText == this.lastText ?
+ this.decay * this.options.decay : 1);
+
+ this.lastText = request.responseText;
+ }
+ this.timer = setTimeout(this.onTimerEvent.bind(this),
+ this.decay * this.frequency * 1000);
+ },
+
+ onTimerEvent: function() {
+ this.updater = new Ajax.Updater(this.container, this.url, this.options);
+ }
+});
+function $(element) {
+ if (arguments.length > 1) {
+ for (var i = 0, elements = [], length = arguments.length; i < length; i++)
+ elements.push($(arguments[i]));
+ return elements;
+ }
+ if (typeof element == 'string')
+ element = document.getElementById(element);
+ return Element.extend(element);
+}
+
+if (Prototype.BrowserFeatures.XPath) {
+ document._getElementsByXPath = function(expression, parentElement) {
+ var results = [];
+ var query = document.evaluate(expression, $(parentElement) || document,
+ null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+ for (var i = 0, length = query.snapshotLength; i < length; i++)
+ results.push(query.snapshotItem(i));
+ return results;
+ };
+
+ document.getElementsByClassName = function(className, parentElement) {
+ var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
+ return document._getElementsByXPath(q, parentElement);
+ }
+
+} else document.getElementsByClassName = function(className, parentElement) {
+ var children = ($(parentElement) || document.body).getElementsByTagName('*');
+ var elements = [], child;
+ for (var i = 0, length = children.length; i < length; i++) {
+ child = children[i];
+ if (Element.hasClassName(child, className))
+ elements.push(Element.extend(child));
+ }
+ return elements;
+};
+
+/*--------------------------------------------------------------------------*/
+
+if (!window.Element) var Element = {};
+
+Element.extend = function(element) {
+ var F = Prototype.BrowserFeatures;
+ if (!element || !element.tagName || element.nodeType == 3 ||
+ element._extended || F.SpecificElementExtensions || element == window)
+ return element;
+
+ var methods = {}, tagName = element.tagName, cache = Element.extend.cache,
+ T = Element.Methods.ByTag;
+
+ // extend methods for all tags (Safari doesn't need this)
+ if (!F.ElementExtensions) {
+ Object.extend(methods, Element.Methods),
+ Object.extend(methods, Element.Methods.Simulated);
+ }
+
+ // extend methods for specific tags
+ if (T[tagName]) Object.extend(methods, T[tagName]);
+
+ for (var property in methods) {
+ var value = methods[property];
+ if (typeof value == 'function' && !(property in element))
+ element[property] = cache.findOrStore(value);
+ }
+
+ element._extended = Prototype.emptyFunction;
+ return element;
+};
+
+Element.extend.cache = {
+ findOrStore: function(value) {
+ return this[value] = this[value] || function() {
+ return value.apply(null, [this].concat($A(arguments)));
+ }
+ }
+};
+
+Element.Methods = {
+ visible: function(element) {
+ return $(element).style.display != 'none';
+ },
+
+ toggle: function(element) {
+ element = $(element);
+ Element[Element.visible(element) ? 'hide' : 'show'](element);
+ return element;
+ },
+
+ hide: function(element) {
+ $(element).style.display = 'none';
+ return element;
+ },
+
+ show: function(element) {
+ $(element).style.display = '';
+ return element;
+ },
+
+ remove: function(element) {
+ element = $(element);
+ element.parentNode.removeChild(element);
+ return element;
+ },
+
+ update: function(element, html) {
+ html = typeof html == 'undefined' ? '' : html.toString();
+ $(element).innerHTML = html.stripScripts();
+ setTimeout(function() {html.evalScripts()}, 10);
+ return element;
+ },
+
+ replace: function(element, html) {
+ element = $(element);
+ html = typeof html == 'undefined' ? '' : html.toString();
+ if (element.outerHTML) {
+ element.outerHTML = html.stripScripts();
+ } else {
+ var range = element.ownerDocument.createRange();
+ range.selectNodeContents(element);
+ element.parentNode.replaceChild(
+ range.createContextualFragment(html.stripScripts()), element);
+ }
+ setTimeout(function() {html.evalScripts()}, 10);
+ return element;
+ },
+
+ inspect: function(element) {
+ element = $(element);
+ var result = '<' + element.tagName.toLowerCase();
+ $H({'id': 'id', 'className': 'class'}).each(function(pair) {
+ var property = pair.first(), attribute = pair.last();
+ var value = (element[property] || '').toString();
+ if (value) result += ' ' + attribute + '=' + value.inspect(true);
+ });
+ return result + '>';
+ },
+
+ recursivelyCollect: function(element, property) {
+ element = $(element);
+ var elements = [];
+ while (element = element[property])
+ if (element.nodeType == 1)
+ elements.push(Element.extend(element));
+ return elements;
+ },
+
+ ancestors: function(element) {
+ return $(element).recursivelyCollect('parentNode');
+ },
+
+ descendants: function(element) {
+ return $A($(element).getElementsByTagName('*')).each(Element.extend);
+ },
+
+ immediateDescendants: function(element) {
+ if (!(element = $(element).firstChild)) return [];
+ while (element && element.nodeType != 1) element = element.nextSibling;
+ if (element) return [element].concat($(element).nextSiblings());
+ return [];
+ },
+
+ previousSiblings: function(element) {
+ return $(element).recursivelyCollect('previousSibling');
+ },
+
+ nextSiblings: function(element) {
+ return $(element).recursivelyCollect('nextSibling');
+ },
+
+ siblings: function(element) {
+ element = $(element);
+ return element.previousSiblings().reverse().concat(element.nextSiblings());
+ },
+
+ match: function(element, selector) {
+ if (typeof selector == 'string')
+ selector = new Selector(selector);
+ return selector.match($(element));
+ },
+
+ up: function(element, expression, index) {
+ var ancestors = $(element).ancestors();
+ return expression ? Selector.findElement(ancestors, expression, index) :
+ ancestors[index || 0];
+ },
+
+ down: function(element, expression, index) {
+ var descendants = $(element).descendants();
+ return expression ? Selector.findElement(descendants, expression, index) :
+ descendants[index || 0];
+ },
+
+ previous: function(element, expression, index) {
+ var previousSiblings = $(element).previousSiblings();
+ return expression ? Selector.findElement(previousSiblings, expression, index) :
+ previousSiblings[index || 0];
+ },
+
+ next: function(element, expression, index) {
+ var nextSiblings = $(element).nextSiblings();
+ return expression ? Selector.findElement(nextSiblings, expression, index) :
+ nextSiblings[index || 0];
+ },
+
+ getElementsBySelector: function() {
+ var args = $A(arguments), element = $(args.shift());
+ return Selector.findChildElements(element, args);
+ },
+
+ getElementsByClassName: function(element, className) {
+ return document.getElementsByClassName(className, element);
+ },
+
+ readAttribute: function(element, name) {
+ element = $(element);
+ if (Prototype.Browser.IE) {
+ if (!element.attributes) return null;
+ var t = Element._attributeTranslations;
+ if (t.values[name]) return t.values[name](element, name);
+ if (t.names[name]) name = t.names[name];
+ var attribute = element.attributes[name];
+ return attribute ? attribute.nodeValue : null;
+ }
+ return element.getAttribute(name);
+ },
+
+ getHeight: function(element) {
+ return $(element).getDimensions().height;
+ },
+
+ getWidth: function(element) {
+ return $(element).getDimensions().width;
+ },
+
+ classNames: function(element) {
+ return new Element.ClassNames(element);
+ },
+
+ hasClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ var elementClassName = element.className;
+ if (elementClassName.length == 0) return false;
+ if (elementClassName == className ||
+ elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
+ return true;
+ return false;
+ },
+
+ addClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ Element.classNames(element).add(className);
+ return element;
+ },
+
+ removeClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ Element.classNames(element).remove(className);
+ return element;
+ },
+
+ toggleClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
+ return element;
+ },
+
+ observe: function() {
+ Event.observe.apply(Event, arguments);
+ return $A(arguments).first();
+ },
+
+ stopObserving: function() {
+ Event.stopObserving.apply(Event, arguments);
+ return $A(arguments).first();
+ },
+
+ // removes whitespace-only text node children
+ cleanWhitespace: function(element) {
+ element = $(element);
+ var node = element.firstChild;
+ while (node) {
+ var nextNode = node.nextSibling;
+ if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
+ element.removeChild(node);
+ node = nextNode;
+ }
+ return element;
+ },
+
+ empty: function(element) {
+ return $(element).innerHTML.blank();
+ },
+
+ descendantOf: function(element, ancestor) {
+ element = $(element), ancestor = $(ancestor);
+ while (element = element.parentNode)
+ if (element == ancestor) return true;
+ return false;
+ },
+
+ scrollTo: function(element) {
+ element = $(element);
+ var pos = Position.cumulativeOffset(element);
+ window.scrollTo(pos[0], pos[1]);
+ return element;
+ },
+
+ getStyle: function(element, style) {
+ element = $(element);
+ style = style == 'float' ? 'cssFloat' : style.camelize();
+ var value = element.style[style];
+ if (!value) {
+ var css = document.defaultView.getComputedStyle(element, null);
+ value = css ? css[style] : null;
+ }
+ if (style == 'opacity') return value ? parseFloat(value) : 1.0;
+ return value == 'auto' ? null : value;
+ },
+
+ getOpacity: function(element) {
+ return $(element).getStyle('opacity');
+ },
+
+ setStyle: function(element, styles, camelized) {
+ element = $(element);
+ var elementStyle = element.style;
+
+ for (var property in styles)
+ if (property == 'opacity') element.setOpacity(styles[property])
+ else
+ elementStyle[(property == 'float' || property == 'cssFloat') ?
+ (elementStyle.styleFloat === undefined ? 'cssFloat' : 'styleFloat') :
+ (camelized ? property : property.camelize())] = styles[property];
+
+ return element;
+ },
+
+ setOpacity: function(element, value) {
+ element = $(element);
+ element.style.opacity = (value == 1 || value === '') ? '' :
+ (value < 0.00001) ? 0 : value;
+ return element;
+ },
+
+ getDimensions: function(element) {
+ element = $(element);
+ var display = $(element).getStyle('display');
+ if (display != 'none' && display != null) // Safari bug
+ return {width: element.offsetWidth, height: element.offsetHeight};
+
+ // All *Width and *Height properties give 0 on elements with display none,
+ // so enable the element temporarily
+ var els = element.style;
+ var originalVisibility = els.visibility;
+ var originalPosition = els.position;
+ var originalDisplay = els.display;
+ els.visibility = 'hidden';
+ els.position = 'absolute';
+ els.display = 'block';
+ var originalWidth = element.clientWidth;
+ var originalHeight = element.clientHeight;
+ els.display = originalDisplay;
+ els.position = originalPosition;
+ els.visibility = originalVisibility;
+ return {width: originalWidth, height: originalHeight};
+ },
+
+ makePositioned: function(element) {
+ element = $(element);
+ var pos = Element.getStyle(element, 'position');
+ if (pos == 'static' || !pos) {
+ element._madePositioned = true;
+ element.style.position = 'relative';
+ // Opera returns the offset relative to the positioning context, when an
+ // element is position relative but top and left have not been defined
+ if (window.opera) {
+ element.style.top = 0;
+ element.style.left = 0;
+ }
+ }
+ return element;
+ },
+
+ undoPositioned: function(element) {
+ element = $(element);
+ if (element._madePositioned) {
+ element._madePositioned = undefined;
+ element.style.position =
+ element.style.top =
+ element.style.left =
+ element.style.bottom =
+ element.style.right = '';
+ }
+ return element;
+ },
+
+ makeClipping: function(element) {
+ element = $(element);
+ if (element._overflow) return element;
+ element._overflow = element.style.overflow || 'auto';
+ if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
+ element.style.overflow = 'hidden';
+ return element;
+ },
+
+ undoClipping: function(element) {
+ element = $(element);
+ if (!element._overflow) return element;
+ element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
+ element._overflow = null;
+ return element;
+ }
+};
+
+Object.extend(Element.Methods, {childOf: Element.Methods.descendantOf});
+
+if (Prototype.Browser.Opera) {
+ Element.Methods._getStyle = Element.Methods.getStyle;
+ Element.Methods.getStyle = function(element, style) {
+ switch(style) {
+ case 'left':
+ case 'top':
+ case 'right':
+ case 'bottom':
+ if (Element._getStyle(element, 'position') == 'static') return null;
+ default: return Element._getStyle(element, style);
+ }
+ };
+}
+else if (Prototype.Browser.IE) {
+ Element.Methods.getStyle = function(element, style) {
+ element = $(element);
+ style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
+ var value = element.style[style];
+ if (!value && element.currentStyle) value = element.currentStyle[style];
+
+ if (style == 'opacity') {
+ if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
+ if (value[1]) return parseFloat(value[1]) / 100;
+ return 1.0;
+ }
+
+ if (value == 'auto') {
+ if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
+ return element['offset'+style.capitalize()] + 'px';
+ return null;
+ }
+ return value;
+ };
+
+ Element.Methods.setOpacity = function(element, value) {
+ element = $(element);
+ var filter = element.getStyle('filter'), style = element.style;
+ if (value == 1 || value === '') {
+ style.filter = filter.replace(/alpha\([^\)]*\)/gi,'');
+ return element;
+ } else if (value < 0.00001) value = 0;
+ style.filter = filter.replace(/alpha\([^\)]*\)/gi, '') +
+ 'alpha(opacity=' + (value * 100) + ')';
+ return element;
+ };
+
+ // IE is missing .innerHTML support for TABLE-related elements
+ Element.Methods.update = function(element, html) {
+ element = $(element);
+ html = typeof html == 'undefined' ? '' : html.toString();
+ var tagName = element.tagName.toUpperCase();
+ if (['THEAD','TBODY','TR','TD'].include(tagName)) {
+ var div = document.createElement('div');
+ switch (tagName) {
+ case 'THEAD':
+ case 'TBODY':
+ div.innerHTML = '<table><tbody>' + html.stripScripts() + '</tbody></table>';
+ depth = 2;
+ break;
+ case 'TR':
+ div.innerHTML = '<table><tbody><tr>' + html.stripScripts() + '</tr></tbody></table>';
+ depth = 3;
+ break;
+ case 'TD':
+ div.innerHTML = '<table><tbody><tr><td>' + html.stripScripts() + '</td></tr></tbody></table>';
+ depth = 4;
+ }
+ $A(element.childNodes).each(function(node) { element.removeChild(node) });
+ depth.times(function() { div = div.firstChild });
+ $A(div.childNodes).each(function(node) { element.appendChild(node) });
+ } else {
+ element.innerHTML = html.stripScripts();
+ }
+ setTimeout(function() { html.evalScripts() }, 10);
+ return element;
+ }
+}
+else if (Prototype.Browser.Gecko) {
+ Element.Methods.setOpacity = function(element, value) {
+ element = $(element);
+ element.style.opacity = (value == 1) ? 0.999999 :
+ (value === '') ? '' : (value < 0.00001) ? 0 : value;
+ return element;
+ };
+}
+
+Element._attributeTranslations = {
+ names: {
+ colspan: "colSpan",
+ rowspan: "rowSpan",
+ valign: "vAlign",
+ datetime: "dateTime",
+ accesskey: "accessKey",
+ tabindex: "tabIndex",
+ enctype: "encType",
+ maxlength: "maxLength",
+ readonly: "readOnly",
+ longdesc: "longDesc"
+ },
+ values: {
+ _getAttr: function(element, attribute) {
+ return element.getAttribute(attribute, 2);
+ },
+ _flag: function(element, attribute) {
+ return $(element).hasAttribute(attribute) ? attribute : null;
+ },
+ style: function(element) {
+ return element.style.cssText.toLowerCase();
+ },
+ title: function(element) {
+ var node = element.getAttributeNode('title');
+ return node.specified ? node.nodeValue : null;
+ }
+ }
+};
+
+(function() {
+ Object.extend(this, {
+ href: this._getAttr,
+ src: this._getAttr,
+ disabled: this._flag,
+ checked: this._flag,
+ readonly: this._flag,
+ multiple: this._flag
+ });
+}).call(Element._attributeTranslations.values);
+
+Element.Methods.Simulated = {
+ hasAttribute: function(element, attribute) {
+ var t = Element._attributeTranslations, node;
+ attribute = t.names[attribute] || attribute;
+ node = $(element).getAttributeNode(attribute);
+ return node && node.specified;
+ }
+};
+
+Element.Methods.ByTag = {};
+
+Object.extend(Element, Element.Methods);
+
+if (!Prototype.BrowserFeatures.ElementExtensions &&
+ document.createElement('div').__proto__) {
+ window.HTMLElement = {};
+ window.HTMLElement.prototype = document.createElement('div').__proto__;
+ Prototype.BrowserFeatures.ElementExtensions = true;
}
-/*
-<%= include 'base.js', 'string.js' %>
+Element.hasAttribute = function(element, attribute) {
+ if (element.hasAttribute) return element.hasAttribute(attribute);
+ return Element.Methods.Simulated.hasAttribute(element, attribute);
+};
+
+Element.addMethods = function(methods) {
+ var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
+ if (arguments.length == 2) {
+ var tagName = methods;
+ methods = arguments[1];
+ }
+
+ if (!tagName) Object.extend(Element.Methods, methods || {});
+ else {
+ if (tagName.constructor == Array) tagName.each(extend);
+ else extend(tagName);
+ }
+
+ function extend(tagName) {
+ tagName = tagName.toUpperCase();
+ if (!Element.Methods.ByTag[tagName])
+ Element.Methods.ByTag[tagName] = {};
+ Object.extend(Element.Methods.ByTag[tagName], methods);
+ }
+
+ function copy(methods, destination, onlyIfAbsent) {
+ onlyIfAbsent = onlyIfAbsent || false;
+ var cache = Element.extend.cache;
+ for (var property in methods) {
+ var value = methods[property];
+ if (!onlyIfAbsent || !(property in destination))
+ destination[property] = cache.findOrStore(value);
+ }
+ }
+
+ function findDOMClass(tagName) {
+ var klass;
+ var trans = {
+ "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
+ "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
+ "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
+ "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
+ "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
+ "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
+ "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
+ "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
+ "FrameSet", "IFRAME": "IFrame"
+ };
+ if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
+ if (window[klass]) return window[klass];
+ klass = 'HTML' + tagName + 'Element';
+ if (window[klass]) return window[klass];
+ klass = 'HTML' + tagName.capitalize() + 'Element';
+ if (window[klass]) return window[klass];
+
+ window[klass] = {};
+ window[klass].prototype = document.createElement(tagName).__proto__;
+ return window[klass];
+ }
+
+ if (F.ElementExtensions) {
+ copy(Element.Methods, HTMLElement.prototype);
+ copy(Element.Methods.Simulated, HTMLElement.prototype, true);
+ }
+
+ if (F.SpecificElementExtensions) {
+ for (var tag in Element.Methods.ByTag) {
+ var klass = findDOMClass(tag);
+ if (typeof klass == "undefined") continue;
+ copy(T[tag], klass.prototype);
+ }
+ }
+};
+
+var Toggle = { display: Element.toggle };
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.Insertion = function(adjacency) {
+ this.adjacency = adjacency;
+}
+
+Abstract.Insertion.prototype = {
+ initialize: function(element, content) {
+ this.element = $(element);
+ this.content = content.stripScripts();
+
+ if (this.adjacency && this.element.insertAdjacentHTML) {
+ try {
+ this.element.insertAdjacentHTML(this.adjacency, this.content);
+ } catch (e) {
+ var tagName = this.element.tagName.toUpperCase();
+ if (['TBODY', 'TR'].include(tagName)) {
+ this.insertContent(this.contentFromAnonymousTable());
+ } else {
+ throw e;
+ }
+ }
+ } else {
+ this.range = this.element.ownerDocument.createRange();
+ if (this.initializeRange) this.initializeRange();
+ this.insertContent([this.range.createContextualFragment(this.content)]);
+ }
+
+ setTimeout(function() {content.evalScripts()}, 10);
+ },
+
+ contentFromAnonymousTable: function() {
+ var div = document.createElement('div');
+ div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
+ return $A(div.childNodes[0].childNodes[0].childNodes);
+ }
+}
+
+var Insertion = new Object();
+
+Insertion.Before = Class.create();
+Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
+ initializeRange: function() {
+ this.range.setStartBefore(this.element);
+ },
+
+ insertContent: function(fragments) {
+ fragments.each((function(fragment) {
+ this.element.parentNode.insertBefore(fragment, this.element);
+ }).bind(this));
+ }
+});
+
+Insertion.Top = Class.create();
+Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
+ initializeRange: function() {
+ this.range.selectNodeContents(this.element);
+ this.range.collapse(true);
+ },
+
+ insertContent: function(fragments) {
+ fragments.reverse(false).each((function(fragment) {
+ this.element.insertBefore(fragment, this.element.firstChild);
+ }).bind(this));
+ }
+});
+
+Insertion.Bottom = Class.create();
+Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
+ initializeRange: function() {
+ this.range.selectNodeContents(this.element);
+ this.range.collapse(this.element);
+ },
+
+ insertContent: function(fragments) {
+ fragments.each((function(fragment) {
+ this.element.appendChild(fragment);
+ }).bind(this));
+ }
+});
+
+Insertion.After = Class.create();
+Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
+ initializeRange: function() {
+ this.range.setStartAfter(this.element);
+ },
+
+ insertContent: function(fragments) {
+ fragments.each((function(fragment) {
+ this.element.parentNode.insertBefore(fragment,
+ this.element.nextSibling);
+ }).bind(this));
+ }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Element.ClassNames = Class.create();
+Element.ClassNames.prototype = {
+ initialize: function(element) {
+ this.element = $(element);
+ },
+
+ _each: function(iterator) {
+ this.element.className.split(/\s+/).select(function(name) {
+ return name.length > 0;
+ })._each(iterator);
+ },
+
+ set: function(className) {
+ this.element.className = className;
+ },
+
+ add: function(classNameToAdd) {
+ if (this.include(classNameToAdd)) return;
+ this.set($A(this).concat(classNameToAdd).join(' '));
+ },
+
+ remove: function(classNameToRemove) {
+ if (!this.include(classNameToRemove)) return;
+ this.set($A(this).without(classNameToRemove).join(' '));
+ },
+
+ toString: function() {
+ return $A(this).join(' ');
+ }
+};
+
+Object.extend(Element.ClassNames.prototype, Enumerable);
+/* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
+ * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
+ * license. Please see http://www.yui-ext.com/ for more information. */
+
+var Selector = Class.create();
+
+Selector.prototype = {
+ initialize: function(expression) {
+ this.expression = expression.strip();
+ this.compileMatcher();
+ },
+
+ compileMatcher: function() {
+ // Selectors with namespaced attributes can't use the XPath version
+ if (Prototype.BrowserFeatures.XPath && !(/\[[\w-]*?:/).test(this.expression))
+ return this.compileXPathMatcher();
+
+ var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
+ c = Selector.criteria, le, p, m;
+
+ if (Selector._cache[e]) {
+ this.matcher = Selector._cache[e]; return;
+ }
+ this.matcher = ["this.matcher = function(root) {",
+ "var r = root, h = Selector.handlers, c = false, n;"];
+
+ while (e && le != e && (/\S/).test(e)) {
+ le = e;
+ for (var i in ps) {
+ p = ps[i];
+ if (m = e.match(p)) {
+ this.matcher.push(typeof c[i] == 'function' ? c[i](m) :
+ new Template(c[i]).evaluate(m));
+ e = e.replace(m[0], '');
+ break;
+ }
+ }
+ }
+
+ this.matcher.push("return h.unique(n);\n}");
+ eval(this.matcher.join('\n'));
+ Selector._cache[this.expression] = this.matcher;
+ },
+
+ compileXPathMatcher: function() {
+ var e = this.expression, ps = Selector.patterns,
+ x = Selector.xpath, le, p, m;
+
+ if (Selector._cache[e]) {
+ this.xpath = Selector._cache[e]; return;
+ }
+
+ this.matcher = ['.//*'];
+ while (e && le != e && (/\S/).test(e)) {
+ le = e;
+ for (var i in ps) {
+ if (m = e.match(ps[i])) {
+ this.matcher.push(typeof x[i] == 'function' ? x[i](m) :
+ new Template(x[i]).evaluate(m));
+ e = e.replace(m[0], '');
+ break;
+ }
+ }
+ }
+
+ this.xpath = this.matcher.join('');
+ Selector._cache[this.expression] = this.xpath;
+ },
+
+ findElements: function(root) {
+ root = root || document;
+ if (this.xpath) return document._getElementsByXPath(this.xpath, root);
+ return this.matcher(root);
+ },
+
+ match: function(element) {
+ return this.findElements(document).include(element);
+ },
+
+ toString: function() {
+ return this.expression;
+ },
+
+ inspect: function() {
+ return "#<Selector:" + this.expression.inspect() + ">";
+ }
+};
+
+Object.extend(Selector, {
+ _cache: {},
+
+ xpath: {
+ descendant: "//*",
+ child: "/*",
+ adjacent: "/following-sibling::*[1]",
+ laterSibling: '/following-sibling::*',
+ tagName: function(m) {
+ if (m[1] == '*') return '';
+ return "[local-name()='" + m[1].toLowerCase() +
+ "' or local-name()='" + m[1].toUpperCase() + "']";
+ },
+ className: "[contains(concat(' ', @class, ' '), ' #{1} ')]",
+ id: "[@id='#{1}']",
+ attrPresence: "[@#{1}]",
+ attr: function(m) {
+ m[3] = m[5] || m[6];
+ return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
+ },
+ pseudo: function(m) {
+ var h = Selector.xpath.pseudos[m[1]];
+ if (!h) return '';
+ if (typeof h === 'function') return h(m);
+ return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
+ },
+ operators: {
+ '=': "[@#{1}='#{3}']",
+ '!=': "[@#{1}!='#{3}']",
+ '^=': "[starts-with(@#{1}, '#{3}')]",
+ '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
+ '*=': "[contains(@#{1}, '#{3}')]",
+ '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
+ '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
+ },
+ pseudos: {
+ 'first-child': '[not(preceding-sibling::*)]',
+ 'last-child': '[not(following-sibling::*)]',
+ 'only-child': '[not(preceding-sibling::* or following-sibling::*)]',
+ 'empty': "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
+ 'checked': "[@checked]",
+ 'disabled': "[@disabled]",
+ 'enabled': "[not(@disabled)]",
+ 'not': function(m) {
+ if (!m[6]) return '';
+ var p = Selector.patterns, x = Selector.xpath;
+ for (var i in p) {
+ if (mm = m[6].match(p[i])) {
+ var ss = typeof x[i] == 'function' ? x[i](mm) : new Template(x[i]).evaluate(mm);
+ m[6] = ss.substring(1, ss.length - 1);
+ break;
+ }
+ }
+ return "[not(" + m[6] + ")]";
+ },
+ 'nth-child': function(m) {
+ return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
+ },
+ 'nth-last-child': function(m) {
+ return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
+ },
+ 'nth-of-type': function(m) {
+ return Selector.xpath.pseudos.nth("position() ", m);
+ },
+ 'nth-last-of-type': function(m) {
+ return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
+ },
+ 'first-of-type': function(m) {
+ m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
+ },
+ 'last-of-type': function(m) {
+ m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
+ },
+ 'only-of-type': function(m) {
+ var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
+ },
+ nth: function(predicate, m) {
+ var mm, formula = m[6];
+ if (formula == 'even') formula = '2n+0';
+ if (formula == 'odd') formula = '2n+1';
+ if (mm = formula.match(/^(\d+)$/)) // digit only
+ predicate += "= " + mm[1];
+ if (mm = formula.match(/^(\d+)?n(\+(\d+))?/)) { // an+b
+ var a = mm[1] ? Number(mm[1]) : 1;
+ var b = mm[3] ? Number(mm[3]) : 0;
+ predicate += "mod " + a + " = " + b;
+ }
+ return "[" + predicate + "]";
+ }
+ }
+ },
+
+ criteria: {
+ tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;',
+ className: 'n = h.className(n, r, "#{1}", c); c = false;',
+ id: 'n = h.id(n, r, "#{1}", c); c = false;',
+ attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;',
+ attr: function(m) {
+ m[3] = m[5] || m[6];
+ return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m);
+ },
+ pseudo: 'n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;',
+ descendant: 'c = "descendant";',
+ child: 'c = "child";',
+ adjacent: 'c = "adjacent";',
+ laterSibling: 'c = "laterSibling";'
+ },
+
+ patterns: {
+ // combinators must be listed first
+ // (and descendant needs to be last combinator)
+ laterSibling: /^\s*~\s*/,
+ child: /^\s*>\s*/,
+ adjacent: /^\s*\+\s*/,
+ descendant: /^\s/,
+
+ // selectors follow
+ tagName: /^\s*(\*|[\w\-]+)(\b|$)?/,
+ id: /^#([\w\-\*]+)(\b|$)/,
+ className: /^\.([\w\-\*]+)(\b|$)/,
+ pseudo: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$)/,
+ attrPresence: /^\[([\w]+)\]/,
+ attr: new RegExp(/\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\]]*?)\4|([^'"][^\]]*?)))?\]/)
+ },
+
+ handlers: {
+ // UTILITY FUNCTIONS
+ // joins two collections
+ concat: function(a, b) {
+ for (var i = 0, node; node = b[i]; i++)
+ a.push(node);
+ return a;
+ },
+
+ // marks an array of nodes for counting
+ mark: function(nodes) {
+ for (var i = 0, node; node = nodes[i]; i++)
+ node._counted = true;
+ return nodes;
+ },
+
+ unmark: function(nodes) {
+ for (var i = 0, node; node = nodes[i]; i++)
+ node._counted = undefined;
+ return nodes;
+ },
+
+ // mark each child node with its position (for nth calls)
+ // "ofType" flag indicates whether we're indexing for nth-of-type
+ // rather than nth-child
+ index: function(parentNode, reverse, ofType) {
+ parentNode._counted = true;
+ if (reverse) {
+ for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
+ node = nodes[i];
+ if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
+ }
+ } else {
+ for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
+ if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
+ }
+ },
+
+ // filters out duplicates and extends all nodes
+ unique: function(nodes) {
+ if (nodes.length == 0) return nodes;
+ var results = [], n;
+ for (var i = 0, l = nodes.length; i < l; i++)
+ if (!(n = nodes[i])._counted) {
+ n._counted = true;
+ results.push(Element.extend(n));
+ }
+ return Selector.handlers.unmark(results);
+ },
+
+ // COMBINATOR FUNCTIONS
+ descendant: function(nodes) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ h.concat(results, Element.descendants(node));
+ return results;
+ },
+
+ child: function(nodes) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ h.concat(results, Element.immediateDescendants(node));
+ return results;
+ },
+
+ adjacent: function(nodes) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ var next = this.nextElementSibling(node);
+ if (next) results.push(next);
+ }
+ return results;
+ },
+
+ laterSibling: function(nodes) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ h.concat(results, Element.nextSiblings(node));
+ return results;
+ },
+
+ nextElementSibling: function(node) {
+ while (node = node.nextSibling)
+ if (node.nodeType == 1) return node;
+ return null;
+ },
+
+ previousElementSibling: function(node) {
+ while (node = node.previousSibling)
+ if (node.nodeType == 1) return node;
+ return null;
+ },
+
+ // TOKEN FUNCTIONS
+ tagName: function(nodes, root, tagName, combinator) {
+ tagName = tagName.toUpperCase();
+ var results = [], h = Selector.handlers;
+ if (nodes) {
+ if (combinator) {
+ // fastlane for ordinary descendant combinators
+ if (combinator == "descendant") {
+ for (var i = 0, node; node = nodes[i]; i++)
+ h.concat(results, node.getElementsByTagName(tagName));
+ return results;
+ } else nodes = this[combinator](nodes);
+ if (tagName == "*") return nodes;
+ }
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (node.tagName.toUpperCase() == tagName) results.push(node);
+ return results;
+ } else return root.getElementsByTagName(tagName);
+ },
+
+ id: function(nodes, root, id, combinator) {
+ var targetNode = $(id), h = Selector.handlers;
+ if (!nodes && root == document) return targetNode ? [targetNode] : [];
+ if (nodes) {
+ if (combinator) {
+ if (combinator == 'child') {
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (targetNode.parentNode == node) return [targetNode];
+ } else if (combinator == 'descendant') {
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (Element.descendantOf(targetNode, node)) return [targetNode];
+ } else if (combinator == 'adjacent') {
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (Selector.handlers.previousElementSibling(targetNode) == node)
+ return [targetNode];
+ } else nodes = h[combinator](nodes);
+ }
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (node == targetNode) return [targetNode];
+ return [];
+ }
+ return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
+ },
+
+ className: function(nodes, root, className, combinator) {
+ if (nodes && combinator) nodes = this[combinator](nodes);
+ return Selector.handlers.byClassName(nodes, root, className);
+ },
+
+ byClassName: function(nodes, root, className) {
+ if (!nodes) nodes = Selector.handlers.descendant([root]);
+ var needle = ' ' + className + ' ';
+ for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
+ nodeClassName = node.className;
+ if (nodeClassName.length == 0) continue;
+ if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
+ results.push(node);
+ }
+ return results;
+ },
+
+ attrPresence: function(nodes, root, attr) {
+ var results = [];
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (Element.hasAttribute(node, attr)) results.push(node);
+ return results;
+ },
+
+ attr: function(nodes, root, attr, value, operator) {
+ var handler = Selector.operators[operator], results = [];
+ for (var i = 0, node; node = nodes[i]; i++) {
+ var nodeValue = Element.readAttribute(node, attr);
+ if (nodeValue === null) continue;
+ if (handler(nodeValue, value)) results.push(node);
+ }
+ return results;
+ },
+
+ pseudo: function(nodes, name, value, root, combinator) {
+ if (combinator) nodes = this[combinator](nodes);
+ return Selector.pseudos[name](nodes, value, root);
+ }
+ },
+
+ pseudos: {
+ 'first-child': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ if (Selector.handlers.previousElementSibling(node)) continue;
+ results.push(node);
+ }
+ return results;
+ },
+ 'last-child': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ if (Selector.handlers.nextElementSibling(node)) continue;
+ results.push(node);
+ }
+ return results;
+ },
+ 'only-child': function(nodes, value, root) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
+ results.push(node);
+ return results;
+ },
+ 'nth-child': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root);
+ },
+ 'nth-last-child': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root, true);
+ },
+ 'nth-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root, false, true);
+ },
+ 'nth-last-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root, true, true);
+ },
+ 'first-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, "1", root, false, true);
+ },
+ 'last-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, "1", root, true, true);
+ },
+ 'only-of-type': function(nodes, formula, root) {
+ var p = Selector.pseudos;
+ return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
+ },
+
+ // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
+ nth: function(nodes, formula, root, reverse, ofType) {
+ if (formula == 'even') formula = '2n+0';
+ if (formula == 'odd') formula = '2n+1';
+ var h = Selector.handlers, results = [], indexed = [], m;
+ h.mark(nodes);
+ for (var i = 0, node; node = nodes[i]; i++) {
+ if (!node.parentNode._counted) {
+ h.index(node.parentNode, reverse, ofType);
+ indexed.push(node.parentNode);
+ }
+ }
+ if (formula.match(/^\d+$/)) { // just a number
+ formula = Number(formula);
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (node.nodeIndex == formula) results.push(node);
+ } else if (m = formula.match(/^(\d+)?n(\+(\d+))?$/)) { // an+b
+ var a = m[1] ? Number(m[1]) : 1;
+ var b = m[3] ? Number(m[3]) : 0;
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (node.nodeIndex % a == b) results.push(node);
+ }
+ h.unmark(nodes);
+ h.unmark(indexed);
+ return results;
+ },
+
+ 'empty': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ // IE treats comments as element nodes
+ if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
+ results.push(node);
+ }
+ return results;
+ },
+
+ 'not': function(nodes, selector, root) {
+ var h = Selector.handlers, exclusions = $A(nodes), selectorType, m;
+ for (var i in Selector.patterns) {
+ if (m = selector.match(Selector.patterns[i])) {
+ selectorType = i; break;
+ }
+ }
+ switch(selectorType) {
+ case 'className': case 'tagName': case 'id': // fallthroughs
+ case 'attrPresence': exclusions = h[selectorType](exclusions, root, m[1], false); break;
+ case 'attr': m[3] = m[5] || m[6]; exclusions = h.attr(exclusions, root, m[1], m[3], m[2]); break;
+ case 'pseudo': exclusions = h.pseudo(exclusions, m[1], m[6], root, false); break;
+ // only 'simple selectors' (one token) allowed in a :not clause
+ default: throw 'Illegal selector in :not clause.';
+ }
+ h.mark(exclusions);
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (!node._counted) results.push(node);
+ h.unmark(exclusions);
+ return results;
+ },
+
+ 'enabled': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (!node.disabled) results.push(node);
+ return results;
+ },
+
+ 'disabled': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (node.disabled) results.push(node);
+ return results;
+ },
+
+ 'checked': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (node.checked) results.push(node);
+ return results;
+ }
+ },
+
+ operators: {
+ '=': function(nv, v) { return nv == v; },
+ '!=': function(nv, v) { return nv != v; },
+ '^=': function(nv, v) { return nv.startsWith(v); },
+ '$=': function(nv, v) { return nv.endsWith(v); },
+ '*=': function(nv, v) { return nv.include(v); },
+ '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
+ '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
+ },
-<%= include 'enumerable.js', 'array.js', 'hash.js', 'range.js' %>
+ matchElements: function(elements, expression) {
+ var matches = new Selector(expression).findElements(), h = Selector.handlers;
+ h.mark(matches);
+ for (var i = 0, results = [], element; element = elements[i]; i++)
+ if (element._counted) results.push(element);
+ h.unmark(matches);
+ return results;
+ },
-<%= include 'ajax.js', 'dom.js', 'selector.js', 'form.js', 'event.js', 'position.js' %>
+ findElement: function(elements, expression, index) {
+ if (typeof expression == 'number') {
+ index = expression; expression = false;
+ }
+ return Selector.matchElements(elements, expression || '*')[index || 0];
+ },
+
+ findChildElements: function(element, expressions) {
+ var exprs = expressions.join(','), expressions = [];
+ exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
+ expressions.push(m[1].strip());
+ });
+ var results = [], h = Selector.handlers;
+ for (var i = 0, l = expressions.length, selector; i < l; i++) {
+ selector = new Selector(expressions[i].strip());
+ h.concat(results, selector.findElements(element));
+ }
+ return (l > 1) ? h.unique(results) : results;
+ }
+});
+
+function $$() {
+ return Selector.findChildElements(document, $A(arguments));
+}
+var Form = {
+ reset: function(form) {
+ $(form).reset();
+ return form;
+ },
+
+ serializeElements: function(elements, getHash) {
+ var data = elements.inject({}, function(result, element) {
+ if (!element.disabled && element.name) {
+ var key = element.name, value = $(element).getValue();
+ if (value != null) {
+ if (key in result) {
+ if (result[key].constructor != Array) result[key] = [result[key]];
+ result[key].push(value);
+ }
+ else result[key] = value;
+ }
+ }
+ return result;
+ });
+
+ return getHash ? data : Hash.toQueryString(data);
+ }
+};
+
+Form.Methods = {
+ serialize: function(form, getHash) {
+ return Form.serializeElements(Form.getElements(form), getHash);
+ },
+
+ getElements: function(form) {
+ return $A($(form).getElementsByTagName('*')).inject([],
+ function(elements, child) {
+ if (Form.Element.Serializers[child.tagName.toLowerCase()])
+ elements.push(Element.extend(child));
+ return elements;
+ }
+ );
+ },
+
+ getInputs: function(form, typeName, name) {
+ form = $(form);
+ var inputs = form.getElementsByTagName('input');
+
+ if (!typeName && !name) return $A(inputs).map(Element.extend);
+
+ for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
+ var input = inputs[i];
+ if ((typeName && input.type != typeName) || (name && input.name != name))
+ continue;
+ matchingInputs.push(Element.extend(input));
+ }
+
+ return matchingInputs;
+ },
+
+ disable: function(form) {
+ form = $(form);
+ form.getElements().each(function(element) {
+ element.blur();
+ element.disabled = 'true';
+ });
+ return form;
+ },
+
+ enable: function(form) {
+ form = $(form);
+ form.getElements().each(function(element) {
+ element.disabled = '';
+ });
+ return form;
+ },
+
+ findFirstElement: function(form) {
+ return $(form).getElements().find(function(element) {
+ return element.type != 'hidden' && !element.disabled &&
+ ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
+ });
+ },
+
+ focusFirstElement: function(form) {
+ form = $(form);
+ form.findFirstElement().activate();
+ return form;
+ },
+
+ request: function(form, options) {
+ form = $(form), options = Object.clone(options || {});
+
+ var params = options.parameters;
+ options.parameters = form.serialize(true);
+
+ if (params) {
+ if (typeof params == 'string') params = params.toQueryParams();
+ Object.extend(options.parameters, params);
+ }
+
+ if (form.hasAttribute('method') && !options.method)
+ options.method = form.method;
+
+ return new Ajax.Request(form.action, options);
+ }
+}
+
+Object.extend(Form, Form.Methods);
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element = {
+ focus: function(element) {
+ $(element).focus();
+ return element;
+ },
+
+ select: function(element) {
+ $(element).select();
+ return element;
+ }
+}
+
+Form.Element.Methods = {
+ serialize: function(element) {
+ element = $(element);
+ if (!element.disabled && element.name) {
+ var value = element.getValue();
+ if (value != undefined) {
+ var pair = {};
+ pair[element.name] = value;
+ return Hash.toQueryString(pair);
+ }
+ }
+ return '';
+ },
+
+ getValue: function(element) {
+ element = $(element);
+ var method = element.tagName.toLowerCase();
+ return Form.Element.Serializers[method](element);
+ },
+
+ clear: function(element) {
+ $(element).value = '';
+ return element;
+ },
+
+ present: function(element) {
+ return $(element).value != '';
+ },
+
+ activate: function(element) {
+ element = $(element);
+ try {
+ element.focus();
+ if (element.select && (element.tagName.toLowerCase() != 'input' ||
+ !['button', 'reset', 'submit'].include(element.type)))
+ element.select();
+ } catch (e) {}
+ return element;
+ },
+
+ disable: function(element) {
+ element = $(element);
+ element.blur();
+ element.disabled = true;
+ return element;
+ },
+
+ enable: function(element) {
+ element = $(element);
+ element.disabled = false;
+ return element;
+ }
+}
+
+Object.extend(Form.Element, Form.Element.Methods);
+Object.extend(Element.Methods.ByTag, {
+ "FORM": Object.clone(Form.Methods),
+ "INPUT": Object.clone(Form.Element.Methods),
+ "SELECT": Object.clone(Form.Element.Methods),
+ "TEXTAREA": Object.clone(Form.Element.Methods)
+});
+
+/*--------------------------------------------------------------------------*/
+
+var Field = Form.Element;
+var $F = Form.Element.getValue;
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element.Serializers = {
+ input: function(element) {
+ switch (element.type.toLowerCase()) {
+ case 'checkbox':
+ case 'radio':
+ return Form.Element.Serializers.inputSelector(element);
+ default:
+ return Form.Element.Serializers.textarea(element);
+ }
+ },
+
+ inputSelector: function(element) {
+ return element.checked ? element.value : null;
+ },
+
+ textarea: function(element) {
+ return element.value;
+ },
+
+ select: function(element) {
+ return this[element.type == 'select-one' ?
+ 'selectOne' : 'selectMany'](element);
+ },
+
+ selectOne: function(element) {
+ var index = element.selectedIndex;
+ return index >= 0 ? this.optionValue(element.options[index]) : null;
+ },
+
+ selectMany: function(element) {
+ var values, length = element.length;
+ if (!length) return null;
+
+ for (var i = 0, values = []; i < length; i++) {
+ var opt = element.options[i];
+ if (opt.selected) values.push(this.optionValue(opt));
+ }
+ return values;
+ },
+
+ optionValue: function(opt) {
+ // extend element because hasAttribute may not be native
+ return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.TimedObserver = function() {}
+Abstract.TimedObserver.prototype = {
+ initialize: function(element, frequency, callback) {
+ this.frequency = frequency;
+ this.element = $(element);
+ this.callback = callback;
+
+ this.lastValue = this.getValue();
+ this.registerCallback();
+ },
+
+ registerCallback: function() {
+ setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+ },
+
+ onTimerEvent: function() {
+ var value = this.getValue();
+ var changed = ('string' == typeof this.lastValue && 'string' == typeof value
+ ? this.lastValue != value : String(this.lastValue) != String(value));
+ if (changed) {
+ this.callback(this.element, value);
+ this.lastValue = value;
+ }
+ }
+}
+
+Form.Element.Observer = Class.create();
+Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+ getValue: function() {
+ return Form.Element.getValue(this.element);
+ }
+});
+
+Form.Observer = Class.create();
+Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+ getValue: function() {
+ return Form.serialize(this.element);
+ }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.EventObserver = function() {}
+Abstract.EventObserver.prototype = {
+ initialize: function(element, callback) {
+ this.element = $(element);
+ this.callback = callback;
+
+ this.lastValue = this.getValue();
+ if (this.element.tagName.toLowerCase() == 'form')
+ this.registerFormCallbacks();
+ else
+ this.registerCallback(this.element);
+ },
+
+ onElementEvent: function() {
+ var value = this.getValue();
+ if (this.lastValue != value) {
+ this.callback(this.element, value);
+ this.lastValue = value;
+ }
+ },
+
+ registerFormCallbacks: function() {
+ Form.getElements(this.element).each(this.registerCallback.bind(this));
+ },
+
+ registerCallback: function(element) {
+ if (element.type) {
+ switch (element.type.toLowerCase()) {
+ case 'checkbox':
+ case 'radio':
+ Event.observe(element, 'click', this.onElementEvent.bind(this));
+ break;
+ default:
+ Event.observe(element, 'change', this.onElementEvent.bind(this));
+ break;
+ }
+ }
+ }
+}
+
+Form.Element.EventObserver = Class.create();
+Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+ getValue: function() {
+ return Form.Element.getValue(this.element);
+ }
+});
+
+Form.EventObserver = Class.create();
+Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+ getValue: function() {
+ return Form.serialize(this.element);
+ }
+});
+if (!window.Event) {
+ var Event = new Object();
+}
+
+Object.extend(Event, {
+ KEY_BACKSPACE: 8,
+ KEY_TAB: 9,
+ KEY_RETURN: 13,
+ KEY_ESC: 27,
+ KEY_LEFT: 37,
+ KEY_UP: 38,
+ KEY_RIGHT: 39,
+ KEY_DOWN: 40,
+ KEY_DELETE: 46,
+ KEY_HOME: 36,
+ KEY_END: 35,
+ KEY_PAGEUP: 33,
+ KEY_PAGEDOWN: 34,
+
+ element: function(event) {
+ return event.target || event.srcElement;
+ },
+
+ isLeftClick: function(event) {
+ return (((event.which) && (event.which == 1)) ||
+ ((event.button) && (event.button == 1)));
+ },
+
+ pointerX: function(event) {
+ return event.pageX || (event.clientX +
+ (document.documentElement.scrollLeft || document.body.scrollLeft));
+ },
+
+ pointerY: function(event) {
+ return event.pageY || (event.clientY +
+ (document.documentElement.scrollTop || document.body.scrollTop));
+ },
+
+ stop: function(event) {
+ if (event.preventDefault) {
+ event.preventDefault();
+ event.stopPropagation();
+ } else {
+ event.returnValue = false;
+ event.cancelBubble = true;
+ }
+ },
+
+ // find the first node with the given tagName, starting from the
+ // node the event was triggered on; traverses the DOM upwards
+ findElement: function(event, tagName) {
+ var element = Event.element(event);
+ while (element.parentNode && (!element.tagName ||
+ (element.tagName.toUpperCase() != tagName.toUpperCase())))
+ element = element.parentNode;
+ return element;
+ },
+
+ observers: false,
+
+ _observeAndCache: function(element, name, observer, useCapture) {
+ if (!this.observers) this.observers = [];
+ if (element.addEventListener) {
+ this.observers.push([element, name, observer, useCapture]);
+ element.addEventListener(name, observer, useCapture);
+ } else if (element.attachEvent) {
+ this.observers.push([element, name, observer, useCapture]);
+ element.attachEvent('on' + name, observer);
+ }
+ },
+
+ unloadCache: function() {
+ if (!Event.observers) return;
+ for (var i = 0, length = Event.observers.length; i < length; i++) {
+ Event.stopObserving.apply(this, Event.observers[i]);
+ Event.observers[i][0] = null;
+ }
+ Event.observers = false;
+ },
+
+ observe: function(element, name, observer, useCapture) {
+ element = $(element);
+ useCapture = useCapture || false;
+
+ if (name == 'keypress' &&
+ (Prototype.Browser.WebKit || element.attachEvent))
+ name = 'keydown';
+
+ Event._observeAndCache(element, name, observer, useCapture);
+ },
+
+ stopObserving: function(element, name, observer, useCapture) {
+ element = $(element);
+ useCapture = useCapture || false;
+
+ if (name == 'keypress' &&
+ (Prototype.Browser.WebKit || element.attachEvent))
+ name = 'keydown';
+
+ if (element.removeEventListener) {
+ element.removeEventListener(name, observer, useCapture);
+ } else if (element.detachEvent) {
+ try {
+ element.detachEvent('on' + name, observer);
+ } catch (e) {}
+ }
+ }
+});
+
+/* prevent memory leaks in IE */
+if (Prototype.Browser.IE)
+ Event.observe(window, 'unload', Event.unloadCache, false);
+var Position = {
+ // set to true if needed, warning: firefox performance problems
+ // NOT neeeded for page scrolling, only if draggable contained in
+ // scrollable elements
+ includeScrollOffsets: false,
+
+ // must be called before calling withinIncludingScrolloffset, every time the
+ // page is scrolled
+ prepare: function() {
+ this.deltaX = window.pageXOffset
+ || document.documentElement.scrollLeft
+ || document.body.scrollLeft
+ || 0;
+ this.deltaY = window.pageYOffset
+ || document.documentElement.scrollTop
+ || document.body.scrollTop
+ || 0;
+ },
+
+ realOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.scrollTop || 0;
+ valueL += element.scrollLeft || 0;
+ element = element.parentNode;
+ } while (element);
+ return [valueL, valueT];
+ },
+
+ cumulativeOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ } while (element);
+ return [valueL, valueT];
+ },
+
+ positionedOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ if (element) {
+ if(element.tagName=='BODY') break;
+ var p = Element.getStyle(element, 'position');
+ if (p == 'relative' || p == 'absolute') break;
+ }
+ } while (element);
+ return [valueL, valueT];
+ },
+
+ offsetParent: function(element) {
+ if (element.offsetParent) return element.offsetParent;
+ if (element == document.body) return element;
+
+ while ((element = element.parentNode) && element != document.body)
+ if (Element.getStyle(element, 'position') != 'static')
+ return element;
+
+ return document.body;
+ },
+
+ // caches x/y coordinate pair to use with overlap
+ within: function(element, x, y) {
+ if (this.includeScrollOffsets)
+ return this.withinIncludingScrolloffsets(element, x, y);
+ this.xcomp = x;
+ this.ycomp = y;
+ this.offset = this.cumulativeOffset(element);
+
+ return (y >= this.offset[1] &&
+ y < this.offset[1] + element.offsetHeight &&
+ x >= this.offset[0] &&
+ x < this.offset[0] + element.offsetWidth);
+ },
+
+ withinIncludingScrolloffsets: function(element, x, y) {
+ var offsetcache = this.realOffset(element);
+
+ this.xcomp = x + offsetcache[0] - this.deltaX;
+ this.ycomp = y + offsetcache[1] - this.deltaY;
+ this.offset = this.cumulativeOffset(element);
+
+ return (this.ycomp >= this.offset[1] &&
+ this.ycomp < this.offset[1] + element.offsetHeight &&
+ this.xcomp >= this.offset[0] &&
+ this.xcomp < this.offset[0] + element.offsetWidth);
+ },
+
+ // within must be called directly before
+ overlap: function(mode, element) {
+ if (!mode) return 0;
+ if (mode == 'vertical')
+ return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
+ element.offsetHeight;
+ if (mode == 'horizontal')
+ return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
+ element.offsetWidth;
+ },
+
+ page: function(forElement) {
+ var valueT = 0, valueL = 0;
+
+ var element = forElement;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+
+ // Safari fix
+ if (element.offsetParent == document.body)
+ if (Element.getStyle(element,'position')=='absolute') break;
+
+ } while (element = element.offsetParent);
+
+ element = forElement;
+ do {
+ if (!window.opera || element.tagName=='BODY') {
+ valueT -= element.scrollTop || 0;
+ valueL -= element.scrollLeft || 0;
+ }
+ } while (element = element.parentNode);
+
+ return [valueL, valueT];
+ },
+
+ clone: function(source, target) {
+ var options = Object.extend({
+ setLeft: true,
+ setTop: true,
+ setWidth: true,
+ setHeight: true,
+ offsetTop: 0,
+ offsetLeft: 0
+ }, arguments[2] || {})
+
+ // find page position of source
+ source = $(source);
+ var p = Position.page(source);
+
+ // find coordinate system to use
+ target = $(target);
+ var delta = [0, 0];
+ var parent = null;
+ // delta [0,0] will do fine with position: fixed elements,
+ // position:absolute needs offsetParent deltas
+ if (Element.getStyle(target,'position') == 'absolute') {
+ parent = Position.offsetParent(target);
+ delta = Position.page(parent);
+ }
+
+ // correct by body offsets (fixes Safari)
+ if (parent == document.body) {
+ delta[0] -= document.body.offsetLeft;
+ delta[1] -= document.body.offsetTop;
+ }
+
+ // set position
+ if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
+ if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
+ if(options.setWidth) target.style.width = source.offsetWidth + 'px';
+ if(options.setHeight) target.style.height = source.offsetHeight + 'px';
+ },
+
+ absolutize: function(element) {
+ element = $(element);
+ if (element.style.position == 'absolute') return;
+ Position.prepare();
+
+ var offsets = Position.positionedOffset(element);
+ var top = offsets[1];
+ var left = offsets[0];
+ var width = element.clientWidth;
+ var height = element.clientHeight;
+
+ element._originalLeft = left - parseFloat(element.style.left || 0);
+ element._originalTop = top - parseFloat(element.style.top || 0);
+ element._originalWidth = element.style.width;
+ element._originalHeight = element.style.height;
+
+ element.style.position = 'absolute';
+ element.style.top = top + 'px';
+ element.style.left = left + 'px';
+ element.style.width = width + 'px';
+ element.style.height = height + 'px';
+ },
+
+ relativize: function(element) {
+ element = $(element);
+ if (element.style.position == 'relative') return;
+ Position.prepare();
+
+ element.style.position = 'relative';
+ var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
+ var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
+
+ element.style.top = top + 'px';
+ element.style.left = left + 'px';
+ element.style.height = element._originalHeight;
+ element.style.width = element._originalWidth;
+ }
+}
+
+// Safari returns margins on body which is incorrect if the child is absolutely
+// positioned. For performance reasons, redefine Position.cumulativeOffset for
+// KHTML/WebKit only.
+if (Prototype.Browser.WebKit) {
+ Position.cumulativeOffset = function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ if (element.offsetParent == document.body)
+ if (Element.getStyle(element, 'position') == 'absolute') break;
+
+ element = element.offsetParent;
+ } while (element);
+
+ return [valueL, valueT];
+ }
+}
-*/ \ No newline at end of file
+Element.addMethods(); \ No newline at end of file
diff --git a/framework/Web/Javascripts/prototype/range.js b/framework/Web/Javascripts/prototype/range.js
deleted file mode 100644
index ef633332..00000000
--- a/framework/Web/Javascripts/prototype/range.js
+++ /dev/null
@@ -1,29 +0,0 @@
-ObjectRange = Class.create();
-Object.extend(ObjectRange.prototype, Enumerable);
-Object.extend(ObjectRange.prototype, {
- initialize: function(start, end, exclusive) {
- this.start = start;
- this.end = end;
- this.exclusive = exclusive;
- },
-
- _each: function(iterator) {
- var value = this.start;
- do {
- iterator(value);
- value = value.succ();
- } while (this.include(value));
- },
-
- include: function(value) {
- if (value < this.start)
- return false;
- if (this.exclusive)
- return value < this.end;
- return value <= this.end;
- }
-});
-
-var $R = function(start, end, exclusive) {
- return new ObjectRange(start, end, exclusive);
-} \ No newline at end of file
diff --git a/framework/Web/Javascripts/prototype/selector.js b/framework/Web/Javascripts/prototype/selector.js
deleted file mode 100644
index cb900e17..00000000
--- a/framework/Web/Javascripts/prototype/selector.js
+++ /dev/null
@@ -1,110 +0,0 @@
-var Selector = Class.create();
-Selector.prototype = {
- initialize: function(expression) {
- this.params = {classNames: []};
- this.expression = expression.toString().strip();
- this.parseExpression();
- this.compileMatcher();
- },
-
- parseExpression: function() {
- function abort(message) { throw 'Parse error in selector: ' + message; }
-
- if (this.expression == '') abort('empty expression');
-
- var params = this.params, expr = this.expression, match, modifier, clause, rest;
- while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) {
- params.attributes = params.attributes || [];
- params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''});
- expr = match[1];
- }
-
- if (expr == '*') return this.params.wildcard = true;
-
- while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) {
- modifier = match[1], clause = match[2], rest = match[3];
- switch (modifier) {
- case '#': params.id = clause; break;
- case '.': params.classNames.push(clause); break;
- case '':
- case undefined: params.tagName = clause.toUpperCase(); break;
- default: abort(expr.inspect());
- }
- expr = rest;
- }
-
- if (expr.length > 0) abort(expr.inspect());
- },
-
- buildMatchExpression: function() {
- var params = this.params, conditions = [], clause;
-
- if (params.wildcard)
- conditions.push('true');
- if (clause = params.id)
- conditions.push('element.id == ' + clause.inspect());
- if (clause = params.tagName)
- conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
- if ((clause = params.classNames).length > 0)
- for (var i = 0; i < clause.length; i++)
- conditions.push('Element.hasClassName(element, ' + clause[i].inspect() + ')');
- if (clause = params.attributes) {
- clause.each(function(attribute) {
- var value = 'element.getAttribute(' + attribute.name.inspect() + ')';
- var splitValueBy = function(delimiter) {
- return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';
- }
-
- switch (attribute.operator) {
- case '=': conditions.push(value + ' == ' + attribute.value.inspect()); break;
- case '~=': conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break;
- case '|=': conditions.push(
- splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect()
- ); break;
- case '!=': conditions.push(value + ' != ' + attribute.value.inspect()); break;
- case '':
- case undefined: conditions.push(value + ' != null'); break;
- default: throw 'Unknown operator ' + attribute.operator + ' in selector';
- }
- });
- }
-
- return conditions.join(' && ');
- },
-
- compileMatcher: function() {
- this.match = new Function('element', 'if (!element.tagName) return false; \
- return ' + this.buildMatchExpression());
- },
-
- findElements: function(scope) {
- var element;
-
- if (element = $(this.params.id))
- if (this.match(element))
- if (!scope || Element.childOf(element, scope))
- return [element];
-
- scope = (scope || document).getElementsByTagName(this.params.tagName || '*');
-
- var results = [];
- for (var i = 0; i < scope.length; i++)
- if (this.match(element = scope[i]))
- results.push(Element.extend(element));
-
- return results;
- },
-
- toString: function() {
- return this.expression;
- }
-}
-
-function $$() {
- return $A(arguments).map(function(expression) {
- return expression.strip().split(/\s+/).inject([null], function(results, expr) {
- var selector = new Selector(expr);
- return results.map(selector.findElements.bind(selector)).flatten();
- });
- }).flatten();
-}
diff --git a/framework/Web/Javascripts/prototype/string.js b/framework/Web/Javascripts/prototype/string.js
deleted file mode 100644
index bba474f5..00000000
--- a/framework/Web/Javascripts/prototype/string.js
+++ /dev/null
@@ -1,134 +0,0 @@
-Object.extend(String.prototype, {
- gsub: function(pattern, replacement) {
- var result = '', source = this, match;
- replacement = arguments.callee.prepareReplacement(replacement);
-
- while (source.length > 0) {
- if (match = source.match(pattern)) {
- result += source.slice(0, match.index);
- result += (replacement(match) || '').toString();
- source = source.slice(match.index + match[0].length);
- } else {
- result += source, source = '';
- }
- }
- return result;
- },
-
- sub: function(pattern, replacement, count) {
- replacement = this.gsub.prepareReplacement(replacement);
- count = count === undefined ? 1 : count;
-
- return this.gsub(pattern, function(match) {
- if (--count < 0) return match[0];
- return replacement(match);
- });
- },
-
- scan: function(pattern, iterator) {
- this.gsub(pattern, iterator);
- return this;
- },
-
- truncate: function(length, truncation) {
- length = length || 30;
- truncation = truncation === undefined ? '...' : truncation;
- return this.length > length ?
- this.slice(0, length - truncation.length) + truncation : this;
- },
-
- strip: function() {
- return this.replace(/^\s+/, '').replace(/\s+$/, '');
- },
-
- stripTags: function() {
- return this.replace(/<\/?[^>]+>/gi, '');
- },
-
- stripScripts: function() {
- return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
- },
-
- extractScripts: function() {
- var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
- var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
- return (this.match(matchAll) || []).map(function(scriptTag) {
- return (scriptTag.match(matchOne) || ['', ''])[1];
- });
- },
-
- evalScripts: function() {
- return this.extractScripts().map(function(script) { return eval(script) });
- },
-
- escapeHTML: function() {
- var div = document.createElement('div');
- var text = document.createTextNode(this);
- div.appendChild(text);
- return div.innerHTML;
- },
-
- unescapeHTML: function() {
- var div = document.createElement('div');
- div.innerHTML = this.stripTags();
- return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
- },
-
- toQueryParams: function() {
- var pairs = this.match(/^\??(.*)$/)[1].split('&');
- return pairs.inject({}, function(params, pairString) {
- var pair = pairString.split('=');
- params[pair[0]] = pair[1];
- return params;
- });
- },
-
- toArray: function() {
- return this.split('');
- },
-
- camelize: function() {
- var oStringList = this.split('-');
- if (oStringList.length == 1) return oStringList[0];
-
- var camelizedString = this.indexOf('-') == 0
- ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
- : oStringList[0];
-
- for (var i = 1, len = oStringList.length; i < len; i++) {
- var s = oStringList[i];
- camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
- }
-
- return camelizedString;
- },
-
- inspect: function() {
- return "'" + this.replace(/\\/g, '\\\\').replace(/'/g, '\\\'') + "'";
- }
-});
-
-String.prototype.gsub.prepareReplacement = function(replacement) {
- if (typeof replacement == 'function') return replacement;
- var template = new Template(replacement);
- return function(match) { return template.evaluate(match) };
-}
-
-String.prototype.parseQuery = String.prototype.toQueryParams;
-
-var Template = Class.create();
-Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
-Template.prototype = {
- initialize: function(template, pattern) {
- this.template = template.toString();
- this.pattern = pattern || Template.Pattern;
- },
-
- evaluate: function(object) {
- return this.template.gsub(this.pattern, function(match) {
- var before = match[1];
- if (before == '\\') return match[2];
- return before + (object[match[3]] || '').toString();
- });
- }
-}
diff --git a/framework/Web/UI/WebControls/THtmlArea.php b/framework/Web/UI/WebControls/THtmlArea.php
index f70f8d25..91f0f033 100644
--- a/framework/Web/UI/WebControls/THtmlArea.php
+++ b/framework/Web/UI/WebControls/THtmlArea.php
@@ -85,6 +85,7 @@ class THtmlArea extends TTextBox
'ar' => 'ar',
'ca' => 'ca',
'cs' => 'cs',
+ 'cy' => 'cy', //available since 3.0.7
'da' => 'da',
'de' => 'de',
'el' => 'el',
@@ -118,7 +119,7 @@ class THtmlArea extends TTextBox
'vi' => 'vi',
'zh' => 'zh_cn_utf8',
'zh_CN' => 'zh_cn_utf8',
- 'zh_HK' => 'zh_tw_utf8',
+ //'zh_HK' => 'zh_tw_utf8', removed from 3.0.7
'zh_TW' => 'zh_tw_utf8'
);
diff --git a/framework/Web/UI/WebControls/TImageButton.php b/framework/Web/UI/WebControls/TImageButton.php
index 5ac0493c..f5c331c1 100644
--- a/framework/Web/UI/WebControls/TImageButton.php
+++ b/framework/Web/UI/WebControls/TImageButton.php
@@ -403,4 +403,4 @@ class TImageClickEventParameter extends TEventParameter
}
}
-?> \ No newline at end of file
+?>
diff --git a/tests/test_tools/selenium/core/SeleniumLog.html b/tests/test_tools/selenium/core/SeleniumLog.html
index dfa0080a..dffa184f 100644
--- a/tests/test_tools/selenium/core/SeleniumLog.html
+++ b/tests/test_tools/selenium/core/SeleniumLog.html
@@ -2,6 +2,7 @@
<head>
<title>Selenium Log Console</title>
+<link id="cssLink" rel="stylesheet" href="selenium.css" />
</head>
<body id="logging-console">
@@ -15,8 +16,6 @@ var logLevels = {
error: 3
};
-var logLevelThreshold = null;
-
function getThresholdLevel() {
var buttons = document.getElementById('logLevelChooser').level;
for (var i = 0; i < buttons.length; i++) {
@@ -27,10 +26,9 @@ function getThresholdLevel() {
}
function setThresholdLevel(logLevel) {
- logLevelThreshold = logLevel;
var buttons = document.getElementById('logLevelChooser').level;
for (var i = 0; i < buttons.length; i++) {
- if (buttons[i].value==logLevel) {
+ if (buttons[i].value==logLevel) {
buttons[i].checked = true;
}
else {
@@ -40,9 +38,7 @@ function setThresholdLevel(logLevel) {
}
function append(message, logLevel) {
- if (logLevelThreshold==null) {
- logLevelThreshold = getThresholdLevel();
- }
+ var logLevelThreshold = getThresholdLevel();
if (logLevels[logLevel] < logLevels[logLevelThreshold]) {
return;
}
@@ -64,9 +60,9 @@ function append(message, logLevel) {
value="error" /><label for="level-error">Error</label>
<input id="level-warn" type="radio" name="level"
value="warn" /><label for="level-warn">Warn</label>
- <input id="level-info" type="radio" name="level" checked="yes"
+ <input id="level-info" type="radio" name="level"
value="info" /><label for="level-info">Info</label>
- <input id="level-debug" type="radio" name="level"
+ <input id="level-debug" type="radio" name="level" checked="yes"
value="debug" /><label for="level-debug">Debug</label>
</form>
<h1>Selenium Log Console</h1>
diff --git a/tests/test_tools/selenium/core/TestRunner-splash.html b/tests/test_tools/selenium/core/TestRunner-splash.html
index 205bb8ef..1c32dd79 100644
--- a/tests/test_tools/selenium/core/TestRunner-splash.html
+++ b/tests/test_tools/selenium/core/TestRunner-splash.html
@@ -15,6 +15,7 @@ Copyright 2005 ThoughtWorks, Inc
-->
<html>
+<link rel="stylesheet" type="text/css" href="selenium.css" />
<body>
<table width="100%">
diff --git a/tests/test_tools/selenium/core/scripts/find_matching_child.js b/tests/test_tools/selenium/core/scripts/find_matching_child.js
index 197d1032..fbf35b75 100644
--- a/tests/test_tools/selenium/core/scripts/find_matching_child.js
+++ b/tests/test_tools/selenium/core/scripts/find_matching_child.js
@@ -15,8 +15,8 @@
*
*/
-Element.findMatchingChildren = function(element, selector) {
- var matches = $A([]);
+elementFindMatchingChildren = function(element, selector) {
+ var matches = [];
var childCount = element.childNodes.length;
for (var i=0; i<childCount; i++) {
@@ -24,7 +24,7 @@ Element.findMatchingChildren = function(element, selector) {
if (selector(child)) {
matches.push(child);
} else {
- childMatches = Element.findMatchingChildren(child, selector);
+ childMatches = elementFindMatchingChildren(child, selector);
matches.push(childMatches);
}
}
@@ -34,7 +34,7 @@ Element.findMatchingChildren = function(element, selector) {
ELEMENT_NODE_TYPE = 1;
-Element.findFirstMatchingChild = function(element, selector) {
+elementFindFirstMatchingChild = function(element, selector) {
var childCount = element.childNodes.length;
for (var i=0; i<childCount; i++) {
@@ -43,7 +43,7 @@ Element.findFirstMatchingChild = function(element, selector) {
if (selector(child)) {
return child;
}
- result = Element.findFirstMatchingChild(child, selector);
+ result = elementFindFirstMatchingChild(child, selector);
if (result) {
return result;
}
@@ -52,7 +52,7 @@ Element.findFirstMatchingChild = function(element, selector) {
return null;
}
-Element.findFirstMatchingParent = function(element, selector) {
+elementFindFirstMatchingParent = function(element, selector) {
var current = element.parentNode;
while (current != null) {
if (selector(current)) {
@@ -63,7 +63,7 @@ Element.findFirstMatchingParent = function(element, selector) {
return current;
}
-Element.findMatchingChildById = function(element, id) {
- return Element.findFirstMatchingChild(element, function(element){return element.id==id} );
+elementFindMatchingChildById = function(element, id) {
+ return elementFindFirstMatchingChild(element, function(element){return element.id==id} );
}
diff --git a/tests/test_tools/selenium/core/scripts/htmlutils.js b/tests/test_tools/selenium/core/scripts/htmlutils.js
index 4d78e1a6..a3cd3dd9 100644
--- a/tests/test_tools/selenium/core/scripts/htmlutils.js
+++ b/tests/test_tools/selenium/core/scripts/htmlutils.js
@@ -18,6 +18,93 @@
// This script contains a badly-organised collection of miscellaneous
// functions that really better homes.
+function classCreate() {
+ return function() {
+ this.initialize.apply(this, arguments);
+ }
+}
+
+function objectExtend(destination, source) {
+ for (var property in source) {
+ destination[property] = source[property];
+ }
+ return destination;
+}
+
+function $() {
+ var results = [], element;
+ for (var i = 0; i < arguments.length; i++) {
+ element = arguments[i];
+ if (typeof element == 'string')
+ element = document.getElementById(element);
+ results[results.length] = element;
+ }
+ return results.length < 2 ? results[0] : results;
+}
+
+function $A(iterable) {
+ if (!iterable) return [];
+ if (iterable.toArray) {
+ return iterable.toArray();
+ } else {
+ var results = [];
+ for (var i = 0; i < iterable.length; i++)
+ results.push(iterable[i]);
+ return results;
+ }
+}
+
+function fnBind() {
+ var args = $A(arguments), __method = args.shift(), object = args.shift();
+ var retval = function() {
+ return __method.apply(object, args.concat($A(arguments)));
+ }
+ retval.__method = __method;
+ return retval;
+}
+
+function fnBindAsEventListener(fn, object) {
+ var __method = fn;
+ return function(event) {
+ return __method.call(object, event || window.event);
+ }
+}
+
+function removeClassName(element, name) {
+ var re = new RegExp("\\b" + name + "\\b", "g");
+ element.className = element.className.replace(re, "");
+}
+
+function addClassName(element, name) {
+ element.className = element.className + ' ' + name;
+}
+
+function elementSetStyle(element, style) {
+ for (var name in style) {
+ var value = style[name];
+ if (value == null) value = "";
+ element.style[name] = value;
+ }
+}
+
+function elementGetStyle(element, style) {
+ var value = element.style[style];
+ if (!value) {
+ if (document.defaultView && document.defaultView.getComputedStyle) {
+ var css = document.defaultView.getComputedStyle(element, null);
+ value = css ? css.getPropertyValue(style) : null;
+ } else if (element.currentStyle) {
+ value = element.currentStyle[style];
+ }
+ }
+
+ /** DGF necessary?
+ if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
+ if (Element.getStyle(element, 'position') == 'static') value = 'auto'; */
+
+ return value == 'auto' ? null : value;
+ }
+
String.prototype.trim = function() {
var result = this.replace(/^\s+/g, "");
// strip leading
@@ -134,31 +221,49 @@ function xmlDecode(text) {
// Sets the text in this element
function setText(element, text) {
- if (element.textContent) {
+ if (element.textContent != null) {
element.textContent = text;
- } else if (element.innerText) {
+ } else if (element.innerText != null) {
element.innerText = text;
}
}
// Get the value of an <input> element
function getInputValue(inputElement) {
- if (inputElement.type.toUpperCase() == 'CHECKBOX' ||
- inputElement.type.toUpperCase() == 'RADIO')
- {
- return (inputElement.checked ? 'on' : 'off');
+ if (inputElement.type) {
+ if (inputElement.type.toUpperCase() == 'CHECKBOX' ||
+ inputElement.type.toUpperCase() == 'RADIO')
+ {
+ return (inputElement.checked ? 'on' : 'off');
+ }
+ }
+ if (inputElement.value == null) {
+ throw new SeleniumError("This element has no value; is it really a form field?");
}
return inputElement.value;
}
/* Fire an event in a browser-compatible manner */
-function triggerEvent(element, eventType, canBubble) {
+function triggerEvent(element, eventType, canBubble, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown) {
canBubble = (typeof(canBubble) == undefined) ? true : canBubble;
if (element.fireEvent) {
- element.fireEvent('on' + eventType);
+ var evt = createEventObject(element, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown);
+ element.fireEvent('on' + eventType, evt);
}
else {
var evt = document.createEvent('HTMLEvents');
+
+ try {
+ evt.shiftKey = shiftKeyDown;
+ evt.metaKey = metaKeyDown;
+ evt.altKey = altKeyDown;
+ evt.ctrlKey = controlKeyDown;
+ } catch (e) {
+ // On Firefox 1.0, you can only set these during initMouseEvent or initKeyEvent
+ // we'll have to ignore them here
+ LOG.exception(e);
+ }
+
evt.initEvent(eventType, canBubble, true);
element.dispatchEvent(evt);
}
@@ -179,14 +284,23 @@ function getKeyCodeFromKeySequence(keySequence) {
if (match != null) {
return match[0];
}
- throw SeleniumError("invalid keySequence");
+ throw new SeleniumError("invalid keySequence");
}
-function triggerKeyEvent(element, eventType, keySequence, canBubble) {
+function createEventObject(element, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown) {
+ var evt = element.ownerDocument.createEventObject();
+ evt.shiftKey = shiftKeyDown;
+ evt.metaKey = metaKeyDown;
+ evt.altKey = altKeyDown;
+ evt.ctrlKey = controlKeyDown;
+ return evt;
+}
+
+function triggerKeyEvent(element, eventType, keySequence, canBubble, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown) {
var keycode = getKeyCodeFromKeySequence(keySequence);
canBubble = (typeof(canBubble) == undefined) ? true : canBubble;
if (element.fireEvent) {
- keyEvent = element.ownerDocument.createEventObject();
+ var keyEvent = createEventObject(element, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown);
keyEvent.keyCode = keycode;
element.fireEvent('on' + eventType, keyEvent);
}
@@ -194,82 +308,24 @@ function triggerKeyEvent(element, eventType, keySequence, canBubble) {
var evt;
if (window.KeyEvent) {
evt = document.createEvent('KeyEvents');
- evt.initKeyEvent(eventType, true, true, window, false, false, false, false, keycode, keycode);
+ evt.initKeyEvent(eventType, true, true, window, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown, keycode, keycode);
} else {
evt = document.createEvent('UIEvents');
+
+ evt.shiftKey = shiftKeyDown;
+ evt.metaKey = metaKeyDown;
+ evt.altKey = altKeyDown;
+ evt.ctrlKey = controlKeyDown;
+
evt.initUIEvent(eventType, true, true, window, 1);
evt.keyCode = keycode;
+ evt.which = keycode;
}
element.dispatchEvent(evt);
}
}
-/* Fire a mouse event in a browser-compatible manner */
-function triggerMouseEvent(element, eventType, canBubble, clientX, clientY) {
- clientX = clientX ? clientX : 0;
- clientY = clientY ? clientY : 0;
-
- // TODO: set these attributes -- they don't seem to be needed by the initial test cases, but that could change...
- var screenX = 0;
- var screenY = 0;
-
- canBubble = (typeof(canBubble) == undefined) ? true : canBubble;
- if (element.fireEvent) {
- LOG.error("element has fireEvent");
- if (!screenX && !screenY && !clientX && !clientY) {
- element.fireEvent('on' + eventType);
- }
- else {
- var ieEvent = element.ownerDocument.createEventObject();
- ieEvent.detail = 0;
- ieEvent.screenX = screenX;
- ieEvent.screenY = screenY;
- ieEvent.clientX = clientX;
- ieEvent.clientY = clientY;
- ieEvent.ctrlKey = false;
- ieEvent.altKey = false;
- ieEvent.shiftKey = false;
- ieEvent.metaKey = false;
- ieEvent.button = 1;
- ieEvent.relatedTarget = null;
-
- // when we go this route, window.event is never set to contain the event we have just created.
- // ideally we could just slide it in as follows in the try-block below, but this normally
- // doesn't work. This is why I try to avoid this code path, which is only required if we need to
- // set attributes on the event (e.g., clientX).
- try {
- window.event = ieEvent;
- }
- catch(e) {
- // getting an "Object does not support this action or property" error. Save the event away
- // for future reference.
- // TODO: is there a way to update window.event?
-
- // work around for http://jira.openqa.org/browse/SEL-280 -- make the event available somewhere:
- selenium.browserbot.getCurrentWindow().selenium_event = ieEvent;
- }
- element.fireEvent('on' + eventType, ieEvent);
- }
- }
- else {
- LOG.error("element doesn't have fireEvent");
- var evt = document.createEvent('MouseEvents');
- if (evt.initMouseEvent)
- {
- LOG.error("element has initMouseEvent");
- //Safari
- evt.initMouseEvent(eventType, canBubble, true, document.defaultView, 1, screenX, screenY, clientX, clientY, false, false, false, false, 0, null)
- }
- else {
- LOG.error("element doesen't has initMouseEvent");
- // TODO we should be initialising other mouse-event related attributes here
- evt.initEvent(eventType, canBubble, true);
- }
- element.dispatchEvent(evt);
- }
-}
-
function removeLoadListener(element, command) {
LOG.info('Removing loadListenter for ' + element + ', ' + command);
if (window.removeEventListener)
@@ -280,10 +336,13 @@ function removeLoadListener(element, command) {
function addLoadListener(element, command) {
LOG.info('Adding loadListenter for ' + element + ', ' + command);
+ var augmentedCommand = function() {
+ command.call(this, element);
+ }
if (window.addEventListener && !browserVersion.isOpera)
- element.addEventListener("load", command, true);
+ element.addEventListener("load", augmentedCommand, true);
else if (window.attachEvent)
- element.attachEvent("onload", command);
+ element.attachEvent("onload", augmentedCommand);
}
/**
@@ -306,10 +365,160 @@ function getDocumentBase(doc) {
return "";
}
+function getTagName(element) {
+ var tagName;
+ if (element && element.tagName && element.tagName.toLowerCase) {
+ tagName = element.tagName.toLowerCase();
+ }
+ return tagName;
+}
+
+function absolutify(url, baseUrl) {
+ /** returns a relative url in its absolute form, given by baseUrl.
+ *
+ * This function is a little odd, because it can take baseUrls that
+ * aren't necessarily directories. It uses the same rules as the HTML
+ * &lt;base&gt; tag; if the baseUrl doesn't end with "/", we'll assume
+ * that it points to a file, and strip the filename off to find its
+ * base directory.
+ *
+ * So absolutify("foo", "http://x/bar") will return "http://x/foo" (stripping off bar),
+ * whereas absolutify("foo", "http://x/bar/") will return "http://x/bar/foo" (preserving bar).
+ * Naturally absolutify("foo", "http://x") will return "http://x/foo", appropriately.
+ *
+ * @param url the url to make absolute; if this url is already absolute, we'll just return that, unchanged
+ * @param baseUrl the baseUrl from which we'll absolutify, following the rules above.
+ * @return 'url' if it was already absolute, or the absolutized version of url if it was not absolute.
+ */
+
+ // DGF isn't there some library we could use for this?
+
+ if (/^\w+:/.test(url)) {
+ // it's already absolute
+ return url;
+ }
+
+ var loc;
+ try {
+ loc = parseUrl(baseUrl);
+ } catch (e) {
+ // is it an absolute windows file path? let's play the hero in that case
+ if (/^\w:\\/.test(baseUrl)) {
+ baseUrl = "file:///" + baseUrl.replace(/\\/g, "/");
+ loc = parseUrl(baseUrl);
+ } else {
+ throw new SeleniumError("baseUrl wasn't absolute: " + baseUrl);
+ }
+ }
+ loc.search = null;
+ loc.hash = null;
+
+ // if url begins with /, then that's the whole pathname
+ if (/^\//.test(url)) {
+ loc.pathname = url;
+ var result = reassembleLocation(loc);
+ return result;
+ }
+
+ // if pathname is null, then we'll just append "/" + the url
+ if (!loc.pathname) {
+ loc.pathname = "/" + url;
+ var result = reassembleLocation(loc);
+ return result;
+ }
+
+ // if pathname ends with /, just append url
+ if (/\/$/.test(loc.pathname)) {
+ loc.pathname += url;
+ var result = reassembleLocation(loc);
+ return result;
+ }
+
+ // if we're here, then the baseUrl has a pathname, but it doesn't end with /
+ // in that case, we replace everything after the final / with the relative url
+ loc.pathname = loc.pathname.replace(/[^\/\\]+$/, url);
+ var result = reassembleLocation(loc);
+ return result;
+
+}
+
+var URL_REGEX = /^((\w+):\/\/)(([^:]+):?([^@]+)?@)?([^\/\?:]*):?(\d+)?(\/?[^\?#]+)?\??([^#]+)?#?(.+)?/;
+
+function parseUrl(url) {
+ var fields = ['url', null, 'protocol', null, 'username', 'password', 'host', 'port', 'pathname', 'search', 'hash'];
+ var result = URL_REGEX.exec(url);
+ if (!result) {
+ throw new SeleniumError("Invalid URL: " + url);
+ }
+ var loc = new Object();
+ for (var i = 0; i < fields.length; i++) {
+ var field = fields[i];
+ if (field == null) {
+ continue;
+ }
+ loc[field] = result[i];
+ }
+ return loc;
+}
+
+function reassembleLocation(loc) {
+ if (!loc.protocol) {
+ throw new Error("Not a valid location object: " + o2s(loc));
+ }
+ var protocol = loc.protocol;
+ protocol = protocol.replace(/:$/, "");
+ var url = protocol + "://";
+ if (loc.username) {
+ url += loc.username;
+ if (loc.password) {
+ url += ":" + loc.password;
+ }
+ url += "@";
+ }
+ if (loc.host) {
+ url += loc.host;
+ }
+
+ if (loc.port) {
+ url += ":" + loc.port;
+ }
+
+ if (loc.pathname) {
+ url += loc.pathname;
+ }
+
+ if (loc.search) {
+ url += "?" + loc.search;
+ }
+ if (loc.hash) {
+ var hash = loc.hash;
+ hash = loc.hash.replace(/^#/, "");
+ url += "#" + hash;
+ }
+ return url;
+}
+
+function canonicalize(url) {
+ var tempLink = window.document.createElement("link");
+ tempLink.href = url; // this will canonicalize the href
+ return tempLink.href;
+}
+
+function extractExceptionMessage(ex) {
+ if (ex == null) return "null exception";
+ if (ex.message != null) return ex.message;
+ if (ex.toString && ex.toString() != null) return ex.toString();
+}
+
+
function describe(object, delimiter) {
var props = new Array();
for (var prop in object) {
- props.push(prop + " -> " + object[prop]);
+ try {
+ props.push(prop + " -> " + object[prop]);
+ } catch (e) {
+ props.push(prop + " -> [htmlutils: ack! couldn't read this property! (Permission Denied?)]");
+ }
}
return props.join(delimiter || '\n');
}
@@ -502,28 +711,27 @@ function SeleniumError(message) {
return error;
}
-var Effect = new Object();
-
-Object.extend(Effect, {
- highlight : function(element) {
- var highLightColor = "yellow";
- if (element.originalColor == undefined) { // avoid picking up highlight
- element.originalColor = Element.getStyle(element, "background-color");
- }
- Element.setStyle(element, {"background-color" : highLightColor});
- window.setTimeout(function() {
+function highlight(element) {
+ var highLightColor = "yellow";
+ if (element.originalColor == undefined) { // avoid picking up highlight
+ element.originalColor = elementGetStyle(element, "background-color");
+ }
+ elementSetStyle(element, {"backgroundColor" : highLightColor});
+ window.setTimeout(function() {
+ try {
//if element is orphan, probably page of it has already gone, so ignore
if (!element.parentNode) {
return;
}
- Element.setStyle(element, {"background-color" : element.originalColor});
- }, 200);
- }
-});
+ elementSetStyle(element, {"backgroundColor" : element.originalColor});
+ } catch (e) {} // DGF unhighlighting is very dangerous and low priority
+ }, 200);
+}
+
// for use from vs.2003 debugger
-function objToString(obj) {
+function o2s(obj) {
var s = "";
for (key in obj) {
var line = key + "->" + obj[key];
@@ -535,7 +743,7 @@ function objToString(obj) {
var seenReadyStateWarning = false;
-function openSeparateApplicationWindow(url) {
+function openSeparateApplicationWindow(url, suppressMozillaWarning) {
// resize the Selenium window itself
window.resizeTo(1200, 500);
window.moveTo(window.screenX, 0);
@@ -560,7 +768,7 @@ function openSeparateApplicationWindow(url) {
}
- if (window.document.readyState == null && !seenReadyStateWarning) {
+ if (!suppressMozillaWarning && window.document.readyState == null && !seenReadyStateWarning) {
alert("Beware! Mozilla bug 300992 means that we can't always reliably detect when a new page has loaded. Install the Selenium IDE extension or the readyState extension available from selenium.openqa.org to make page load detection more reliable.");
seenReadyStateWarning = true;
}
@@ -568,8 +776,8 @@ function openSeparateApplicationWindow(url) {
return appWindow;
}
-var URLConfiguration = Class.create();
-Object.extend(URLConfiguration.prototype, {
+var URLConfiguration = classCreate();
+objectExtend(URLConfiguration.prototype, {
initialize: function() {
},
_isQueryParameterTrue: function (name) {
@@ -615,6 +823,11 @@ Object.extend(URLConfiguration.prototype, {
isMultiWindowMode:function() {
return this._isQueryParameterTrue('multiWindow');
+ },
+
+ getBaseUrl:function() {
+ return this._getQueryParameter('baseUrl');
+
}
});
diff --git a/tests/test_tools/selenium/core/scripts/injection.html b/tests/test_tools/selenium/core/scripts/injection.html
index d41fbe69..a75c7211 100644
--- a/tests/test_tools/selenium/core/scripts/injection.html
+++ b/tests/test_tools/selenium/core/scripts/injection.html
@@ -1,21 +1,22 @@
<script language="JavaScript">
if (window["selenium_has_been_loaded_into_this_window"]==null)
{
-__SELENIUM_JS__
+
+ __SELENIUM_JS__
// Some background on the code below: broadly speaking, where we are relative to other windows
// when running in proxy injection mode depends on whether we are in a frame set file or not.
-//
+//
// In regular HTML files, the selenium JavaScript is injected into an iframe called "selenium"
// in order to reduce its impact on the JavaScript environment (through namespace pollution,
// etc.). So in regular HTML files, we need to look at the parent of the current window when we want
// a handle to, e.g., the application window.
-//
+//
// In frame set files, we can't use an iframe, so we put the JavaScript in the head element and share
// the window with the frame set. So in this case, we need to look at the current window, not the
-// parent when looking for, e.g., the application window. (TODO: Perhaps I should have just
+// parent when looking for, e.g., the application window. (TODO: Perhaps I should have just
// assigned a regular frame for selenium?)
-//
+//
BrowserBot.prototype.getContentWindow = function() {
if (window["seleniumInSameWindow"] != null) return window;
return window.parent;
@@ -38,7 +39,7 @@ LOG.openLogWindow = function(message, className) {
BrowserBot.prototype.relayToRC = function(name) {
var object = eval(name);
var s = 'state:' + serializeObject(name, object) + "\n";
- sendToRC(s);
+ sendToRC(s,"state=true");
}
BrowserBot.prototype.relayBotToRC = function(s) {
@@ -58,10 +59,16 @@ function seleniumOnLoad() {
runSeleniumTest();
}
+function seleniumOnUnload() {
+ sendToRC("OK"); // just in case some poor PI server thread is waiting for a response
+}
+
if (window.addEventListener) {
window.addEventListener("load", seleniumOnLoad, false); // firefox
+ window.addEventListener("unload", seleniumOnUnload, false); // firefox
} else if (window.attachEvent){
window.attachEvent("onload", seleniumOnLoad); // IE
+ window.attachEvent("onunload", seleniumOnUnload); // IE
}
else {
throw "causing a JavaScript error to tell the world that I did not arrange to be run on load";
diff --git a/tests/test_tools/selenium/core/scripts/selenium-api.js b/tests/test_tools/selenium/core/scripts/selenium-api.js
index e8e587f7..1646236f 100644
--- a/tests/test_tools/selenium/core/scripts/selenium-api.js
+++ b/tests/test_tools/selenium/core/scripts/selenium-api.js
@@ -15,157 +15,179 @@
*
*/
-// TODO: stop navigating this.page().document() ... it breaks encapsulation
+// TODO: stop navigating this.browserbot.document() ... it breaks encapsulation
var storedVars = new Object();
function Selenium(browserbot) {
- /**
- * Defines an object that runs Selenium commands.
- *
- * <h3><a name="locators"></a>Element Locators</h3>
- * <p>
- * Element Locators tell Selenium which HTML element a command refers to.
- * The format of a locator is:</p>
- * <blockquote>
- * <em>locatorType</em><strong>=</strong><em>argument</em>
- * </blockquote>
- *
- * <p>
- * We support the following strategies for locating elements:
- * </p>
- * <blockquote>
- * <dl>
- * <dt><strong>identifier</strong>=<em>id</em></dt>
- * <dd>Select the element with the specified &#64;id attribute. If no match is
- * found, select the first element whose &#64;name attribute is <em>id</em>.
- * (This is normally the default; see below.)</dd>
- * <dt><strong>id</strong>=<em>id</em></dt>
- * <dd>Select the element with the specified &#64;id attribute.</dd>
- *
- * <dt><strong>name</strong>=<em>name</em></dt>
- * <dd>Select the first element with the specified &#64;name attribute.</dd>
- * <dd><ul class="first last simple">
- * <li>username</li>
- * <li>name=username</li>
- * </ul>
- * </dd>
- * <dd>The name may optionally be followed by one or more <em>element-filters</em>, separated from the name by whitespace. If the <em>filterType</em> is not specified, <strong>value</strong> is assumed.</dd>
- *
- * <dd><ul class="first last simple">
- * <li>name=flavour value=chocolate</li>
- * </ul>
- * </dd>
- * <dt><strong>dom</strong>=<em>javascriptExpression</em></dt>
- *
- * <dd>
- *
- * <dd>Find an element using JavaScript traversal of the HTML Document Object
- * Model. DOM locators <em>must</em> begin with &quot;document.&quot;.
- * <ul class="first last simple">
- * <li>dom=document.forms['myForm'].myDropdown</li>
- * <li>dom=document.images[56]</li>
- * </ul>
- * </dd>
- *
- * </dd>
- *
- * <dt><strong>xpath</strong>=<em>xpathExpression</em></dt>
- * <dd>Locate an element using an XPath expression.
- * <ul class="first last simple">
- * <li>xpath=//img[&#64;alt='The image alt text']</li>
- * <li>xpath=//table[&#64;id='table1']//tr[4]/td[2]</li>
- *
- * </ul>
- * </dd>
- * <dt><strong>link</strong>=<em>textPattern</em></dt>
- * <dd>Select the link (anchor) element which contains text matching the
- * specified <em>pattern</em>.
- * <ul class="first last simple">
- * <li>link=The link text</li>
- * </ul>
- *
- * </dd>
- *
- * <dt><strong>css</strong>=<em>cssSelectorSyntax</em></dt>
- * <dd>Select the element using css selectors. Please refer to <a href="http://www.w3.org/TR/REC-CSS2/selector.html">CSS2 selectors</a>, <a href="http://www.w3.org/TR/2001/CR-css3-selectors-20011113/">CSS3 selectors</a> for more information. You can also check the TestCssLocators test in the selenium test suite for an example of usage, which is included in the downloaded selenium core package.
- * <ul class="first last simple">
- * <li>css=a[href="#id3"]</li>
- * <li>css=span#firstChild + span</li>
- * </ul>
- * </dd>
- * <dd>Currently the css selector locator supports all css1, css2 and css3 selectors except namespace in css3, some pseudo classes(:nth-of-type, :nth-last-of-type, :first-of-type, :last-of-type, :only-of-type, :visited, :hover, :active, :focus, :indeterminate) and pseudo elements(::first-line, ::first-letter, ::selection, ::before, ::after). </dd>
- * </dl>
- * </blockquote>
- * <p>
- * Without an explicit locator prefix, Selenium uses the following default
- * strategies:
- * </p>
- *
- * <ul class="simple">
- * <li><strong>dom</strong>, for locators starting with &quot;document.&quot;</li>
- * <li><strong>xpath</strong>, for locators starting with &quot;//&quot;</li>
- * <li><strong>identifier</strong>, otherwise</li>
- * </ul>
- *
- * <h3><a name="element-filters">Element Filters</a></h3>
- * <blockquote>
- * <p>Element filters can be used with a locator to refine a list of candidate elements. They are currently used only in the 'name' element-locator.</p>
- * <p>Filters look much like locators, ie.</p>
- * <blockquote>
- * <em>filterType</em><strong>=</strong><em>argument</em></blockquote>
- *
- * <p>Supported element-filters are:</p>
- * <p><strong>value=</strong><em>valuePattern</em></p>
- * <blockquote>
- * Matches elements based on their values. This is particularly useful for refining a list of similarly-named toggle-buttons.</blockquote>
- * <p><strong>index=</strong><em>index</em></p>
- * <blockquote>
- * Selects a single element based on its position in the list (offset from zero).</blockquote>
- * </blockquote>
- *
- * <h3><a name="patterns"></a>String-match Patterns</h3>
- *
- * <p>
- * Various Pattern syntaxes are available for matching string values:
- * </p>
- * <blockquote>
- * <dl>
- * <dt><strong>glob:</strong><em>pattern</em></dt>
- * <dd>Match a string against a "glob" (aka "wildmat") pattern. "Glob" is a
- * kind of limited regular-expression syntax typically used in command-line
- * shells. In a glob pattern, "*" represents any sequence of characters, and "?"
- * represents any single character. Glob patterns match against the entire
- * string.</dd>
- * <dt><strong>regexp:</strong><em>regexp</em></dt>
- * <dd>Match a string using a regular-expression. The full power of JavaScript
- * regular-expressions is available.</dd>
- * <dt><strong>exact:</strong><em>string</em></dt>
- *
- * <dd>Match a string exactly, verbatim, without any of that fancy wildcard
- * stuff.</dd>
- * </dl>
- * </blockquote>
- * <p>
- * If no pattern prefix is specified, Selenium assumes that it's a "glob"
- * pattern.
- * </p>
- */
+ /**
+ * Defines an object that runs Selenium commands.
+ *
+ * <h3><a name="locators"></a>Element Locators</h3>
+ * <p>
+ * Element Locators tell Selenium which HTML element a command refers to.
+ * The format of a locator is:</p>
+ * <blockquote>
+ * <em>locatorType</em><strong>=</strong><em>argument</em>
+ * </blockquote>
+ *
+ * <p>
+ * We support the following strategies for locating elements:
+ * </p>
+ *
+ * <ul>
+ * <li><strong>identifier</strong>=<em>id</em>:
+ * Select the element with the specified &#64;id attribute. If no match is
+ * found, select the first element whose &#64;name attribute is <em>id</em>.
+ * (This is normally the default; see below.)</li>
+ * <li><strong>id</strong>=<em>id</em>:
+ * Select the element with the specified &#64;id attribute.</li>
+ *
+ * <li><strong>name</strong>=<em>name</em>:
+ * Select the first element with the specified &#64;name attribute.
+ * <ul class="first last simple">
+ * <li>username</li>
+ * <li>name=username</li>
+ * </ul>
+ *
+ * <p>The name may optionally be followed by one or more <em>element-filters</em>, separated from the name by whitespace. If the <em>filterType</em> is not specified, <strong>value</strong> is assumed.</p>
+ *
+ * <ul class="first last simple">
+ * <li>name=flavour value=chocolate</li>
+ * </ul>
+ * </li>
+ * <li><strong>dom</strong>=<em>javascriptExpression</em>:
+ *
+ * Find an element by evaluating the specified string. This allows you to traverse the HTML Document Object
+ * Model using JavaScript. Note that you must not return a value in this string; simply make it the last expression in the block.
+ * <ul class="first last simple">
+ * <li>dom=document.forms['myForm'].myDropdown</li>
+ * <li>dom=document.images[56]</li>
+ * <li>dom=function foo() { return document.links[1]; }; foo();</li>
+ * </ul>
+ *
+ * </li>
+ *
+ * <li><strong>xpath</strong>=<em>xpathExpression</em>:
+ * Locate an element using an XPath expression.
+ * <ul class="first last simple">
+ * <li>xpath=//img[&#64;alt='The image alt text']</li>
+ * <li>xpath=//table[&#64;id='table1']//tr[4]/td[2]</li>
+ * <li>xpath=//a[contains(&#64;href,'#id1')]</li>
+ * <li>xpath=//a[contains(&#64;href,'#id1')]/&#64;class</li>
+ * <li>xpath=(//table[&#64;class='stylee'])//th[text()='theHeaderText']/../td</li>
+ * <li>xpath=//input[&#64;name='name2' and &#64;value='yes']</li>
+ * <li>xpath=//*[text()="right"]</li>
+ *
+ * </ul>
+ * </li>
+ * <li><strong>link</strong>=<em>textPattern</em>:
+ * Select the link (anchor) element which contains text matching the
+ * specified <em>pattern</em>.
+ * <ul class="first last simple">
+ * <li>link=The link text</li>
+ * </ul>
+ *
+ * </li>
+ *
+ * <li><strong>css</strong>=<em>cssSelectorSyntax</em>:
+ * Select the element using css selectors. Please refer to <a href="http://www.w3.org/TR/REC-CSS2/selector.html">CSS2 selectors</a>, <a href="http://www.w3.org/TR/2001/CR-css3-selectors-20011113/">CSS3 selectors</a> for more information. You can also check the TestCssLocators test in the selenium test suite for an example of usage, which is included in the downloaded selenium core package.
+ * <ul class="first last simple">
+ * <li>css=a[href="#id3"]</li>
+ * <li>css=span#firstChild + span</li>
+ * </ul>
+ * <p>Currently the css selector locator supports all css1, css2 and css3 selectors except namespace in css3, some pseudo classes(:nth-of-type, :nth-last-of-type, :first-of-type, :last-of-type, :only-of-type, :visited, :hover, :active, :focus, :indeterminate) and pseudo elements(::first-line, ::first-letter, ::selection, ::before, ::after). </p>
+ * </li>
+ * </ul>
+ *
+ * <p>
+ * Without an explicit locator prefix, Selenium uses the following default
+ * strategies:
+ * </p>
+ *
+ * <ul class="simple">
+ * <li><strong>dom</strong>, for locators starting with &quot;document.&quot;</li>
+ * <li><strong>xpath</strong>, for locators starting with &quot;//&quot;</li>
+ * <li><strong>identifier</strong>, otherwise</li>
+ * </ul>
+ *
+ * <h3><a name="element-filters">Element Filters</a></h3>
+ * <blockquote>
+ * <p>Element filters can be used with a locator to refine a list of candidate elements. They are currently used only in the 'name' element-locator.</p>
+ * <p>Filters look much like locators, ie.</p>
+ * <blockquote>
+ * <em>filterType</em><strong>=</strong><em>argument</em></blockquote>
+ *
+ * <p>Supported element-filters are:</p>
+ * <p><strong>value=</strong><em>valuePattern</em></p>
+ * <blockquote>
+ * Matches elements based on their values. This is particularly useful for refining a list of similarly-named toggle-buttons.</blockquote>
+ * <p><strong>index=</strong><em>index</em></p>
+ * <blockquote>
+ * Selects a single element based on its position in the list (offset from zero).</blockquote>
+ * </blockquote>
+ *
+ * <h3><a name="patterns"></a>String-match Patterns</h3>
+ *
+ * <p>
+ * Various Pattern syntaxes are available for matching string values:
+ * </p>
+ * <ul>
+ * <li><strong>glob:</strong><em>pattern</em>:
+ * Match a string against a "glob" (aka "wildmat") pattern. "Glob" is a
+ * kind of limited regular-expression syntax typically used in command-line
+ * shells. In a glob pattern, "*" represents any sequence of characters, and "?"
+ * represents any single character. Glob patterns match against the entire
+ * string.</li>
+ * <li><strong>regexp:</strong><em>regexp</em>:
+ * Match a string using a regular-expression. The full power of JavaScript
+ * regular-expressions is available.</li>
+ * <li><strong>exact:</strong><em>string</em>:
+ *
+ * Match a string exactly, verbatim, without any of that fancy wildcard
+ * stuff.</li>
+ * </ul>
+ * <p>
+ * If no pattern prefix is specified, Selenium assumes that it's a "glob"
+ * pattern.
+ * </p>
+ */
this.browserbot = browserbot;
this.optionLocatorFactory = new OptionLocatorFactory();
+ // DGF for backwards compatibility
this.page = function() {
- return browserbot.getCurrentPage();
+ return browserbot;
};
this.defaultTimeout = Selenium.DEFAULT_TIMEOUT;
+ this.mouseSpeed = 10;
}
Selenium.DEFAULT_TIMEOUT = 30 * 1000;
+Selenium.DEFAULT_MOUSE_SPEED = 10;
+
+Selenium.decorateFunctionWithTimeout = function(f, timeout) {
+ if (f == null) {
+ return null;
+ }
+ var timeoutValue = parseInt(timeout);
+ if (isNaN(timeoutValue)) {
+ throw new SeleniumError("Timeout is not a number: '" + timeout + "'");
+ }
+ var now = new Date().getTime();
+ var timeoutTime = now + timeoutValue;
+ return function() {
+ if (new Date().getTime() > timeoutTime) {
+ throw new SeleniumError("Timed out after " + timeoutValue + "ms");
+ }
+ return f();
+ };
+}
-Selenium.createForWindow = function(window) {
+Selenium.createForWindow = function(window, proxyInjectionMode) {
if (!window.location) {
throw "error: not a window!";
}
- return new Selenium(BrowserBot.createForWindow(window));
+ return new Selenium(BrowserBot.createForWindow(window, proxyInjectionMode));
};
Selenium.prototype.reset = function() {
@@ -176,7 +198,7 @@ Selenium.prototype.reset = function() {
};
Selenium.prototype.doClick = function(locator) {
- /**
+ /**
* Clicks on a link, button, checkbox or radio button. If the click action
* causes a new page to load (like a link usually does), call
* waitForPageToLoad.
@@ -184,43 +206,69 @@ Selenium.prototype.doClick = function(locator) {
* @param locator an element locator
*
*/
- var element = this.page().findElement(locator);
- this.page().clickElement(element);
+ var element = this.browserbot.findElement(locator);
+ this.browserbot.clickElement(element);
+};
+
+Selenium.prototype.doDoubleClick = function(locator) {
+ /**
+ * Double clicks on a link, button, checkbox or radio button. If the double click action
+ * causes a new page to load (like a link usually does), call
+ * waitForPageToLoad.
+ *
+ * @param locator an element locator
+ *
+ */
+ var element = this.browserbot.findElement(locator);
+ this.browserbot.doubleClickElement(element);
};
Selenium.prototype.doClickAt = function(locator, coordString) {
- /**
+ /**
* Clicks on a link, button, checkbox or radio button. If the click action
* causes a new page to load (like a link usually does), call
* waitForPageToLoad.
*
- * Beware of http://jira.openqa.org/browse/SEL-280, which will lead some event handlers to
- * get null event arguments. Read the bug for more details, including a workaround.
+ * @param locator an element locator
+ * @param coordString specifies the x,y position (i.e. - 10,20) of the mouse
+ * event relative to the element returned by the locator.
+ *
+ */
+ var element = this.browserbot.findElement(locator);
+ var clientXY = getClientXY(element, coordString)
+ this.browserbot.clickElement(element, clientXY[0], clientXY[1]);
+};
+
+Selenium.prototype.doDoubleClickAt = function(locator, coordString) {
+ /**
+ * Doubleclicks on a link, button, checkbox or radio button. If the action
+ * causes a new page to load (like a link usually does), call
+ * waitForPageToLoad.
*
* @param locator an element locator
* @param coordString specifies the x,y position (i.e. - 10,20) of the mouse
* event relative to the element returned by the locator.
*
*/
- var element = this.page().findElement(locator);
+ var element = this.browserbot.findElement(locator);
var clientXY = getClientXY(element, coordString)
- this.page().clickElement(element, clientXY[0], clientXY[1]);
+ this.browserbot.doubleClickElement(element, clientXY[0], clientXY[1]);
};
Selenium.prototype.doFireEvent = function(locator, eventName) {
- /**
+ /**
* Explicitly simulate an event, to trigger the corresponding &quot;on<em>event</em>&quot;
* handler.
*
* @param locator an <a href="#locators">element locator</a>
* @param eventName the event name, e.g. "focus" or "blur"
*/
- var element = this.page().findElement(locator);
+ var element = this.browserbot.findElement(locator);
triggerEvent(element, eventName, false);
};
Selenium.prototype.doKeyPress = function(locator, keySequence) {
- /**
+ /**
* Simulates a user pressing and releasing a key.
*
* @param locator an <a href="#locators">element locator</a>
@@ -228,12 +276,80 @@ Selenium.prototype.doKeyPress = function(locator, keySequence) {
* of the key to be pressed, normally the ASCII value of that key), or a single
* character. For example: "w", "\119".
*/
- var element = this.page().findElement(locator);
- triggerKeyEvent(element, 'keypress', keySequence, true);
+ var element = this.browserbot.findElement(locator);
+ triggerKeyEvent(element, 'keypress', keySequence, true,
+ this.browserbot.controlKeyDown,
+ this.browserbot.altKeyDown,
+ this.browserbot.shiftKeyDown,
+ this.browserbot.metaKeyDown);
+};
+
+Selenium.prototype.doShiftKeyDown = function() {
+ /**
+ * Press the shift key and hold it down until doShiftUp() is called or a new page is loaded.
+ *
+ */
+ this.browserbot.shiftKeyDown = true;
+};
+
+Selenium.prototype.doShiftKeyUp = function() {
+ /**
+ * Release the shift key.
+ *
+ */
+ this.browserbot.shiftKeyDown = false;
+};
+
+Selenium.prototype.doMetaKeyDown = function() {
+ /**
+ * Press the meta key and hold it down until doMetaUp() is called or a new page is loaded.
+ *
+ */
+ this.browserbot.metaKeyDown = true;
+};
+
+Selenium.prototype.doMetaKeyUp = function() {
+ /**
+ * Release the meta key.
+ *
+ */
+ this.browserbot.metaKeyDown = false;
+};
+
+Selenium.prototype.doAltKeyDown = function() {
+ /**
+ * Press the alt key and hold it down until doAltUp() is called or a new page is loaded.
+ *
+ */
+ this.browserbot.altKeyDown = true;
+};
+
+Selenium.prototype.doAltKeyUp = function() {
+ /**
+ * Release the alt key.
+ *
+ */
+ this.browserbot.altKeyDown = false;
+};
+
+Selenium.prototype.doControlKeyDown = function() {
+ /**
+ * Press the control key and hold it down until doControlUp() is called or a new page is loaded.
+ *
+ */
+ this.browserbot.controlKeyDown = true;
+};
+
+Selenium.prototype.doControlKeyUp = function() {
+ /**
+ * Release the control key.
+ *
+ */
+ this.browserbot.controlKeyDown = false;
};
Selenium.prototype.doKeyDown = function(locator, keySequence) {
- /**
+ /**
* Simulates a user pressing a key (without releasing it yet).
*
* @param locator an <a href="#locators">element locator</a>
@@ -241,12 +357,16 @@ Selenium.prototype.doKeyDown = function(locator, keySequence) {
* of the key to be pressed, normally the ASCII value of that key), or a single
* character. For example: "w", "\119".
*/
- var element = this.page().findElement(locator);
- triggerKeyEvent(element, 'keydown', keySequence, true);
+ var element = this.browserbot.findElement(locator);
+ triggerKeyEvent(element, 'keydown', keySequence, true,
+ this.browserbot.controlKeyDown,
+ this.browserbot.altKeyDown,
+ this.browserbot.shiftKeyDown,
+ this.browserbot.metaKeyDown);
};
Selenium.prototype.doKeyUp = function(locator, keySequence) {
- /**
+ /**
* Simulates a user releasing a key.
*
* @param locator an <a href="#locators">element locator</a>
@@ -254,8 +374,12 @@ Selenium.prototype.doKeyUp = function(locator, keySequence) {
* of the key to be pressed, normally the ASCII value of that key), or a single
* character. For example: "w", "\119".
*/
- var element = this.page().findElement(locator);
- triggerKeyEvent(element, 'keyup', keySequence, true);
+ var element = this.browserbot.findElement(locator);
+ triggerKeyEvent(element, 'keyup', keySequence, true,
+ this.browserbot.controlKeyDown,
+ this.browserbot.altKeyDown,
+ this.browserbot.shiftKeyDown,
+ this.browserbot.metaKeyDown);
};
function getClientXY(element, coordString) {
@@ -278,13 +402,13 @@ function getClientXY(element, coordString) {
}
Selenium.prototype.doMouseOver = function(locator) {
- /**
+ /**
* Simulates a user hovering a mouse over the specified element.
*
* @param locator an <a href="#locators">element locator</a>
*/
- var element = this.page().findElement(locator);
- triggerMouseEvent(element, 'mouseover', true);
+ var element = this.browserbot.findElement(locator);
+ this.browserbot.triggerMouseEvent(element, 'mouseover', true);
};
Selenium.prototype.doMouseOut = function(locator) {
@@ -293,100 +417,91 @@ Selenium.prototype.doMouseOut = function(locator) {
*
* @param locator an <a href="#locators">element locator</a>
*/
- var element = this.page().findElement(locator);
- triggerMouseEvent(element, 'mouseout', true);
+ var element = this.browserbot.findElement(locator);
+ this.browserbot.triggerMouseEvent(element, 'mouseout', true);
};
Selenium.prototype.doMouseDown = function(locator) {
- /**
+ /**
* Simulates a user pressing the mouse button (without releasing it yet) on
* the specified element.
*
* @param locator an <a href="#locators">element locator</a>
*/
- var element = this.page().findElement(locator);
- triggerMouseEvent(element, 'mousedown', true);
+ var element = this.browserbot.findElement(locator);
+ this.browserbot.triggerMouseEvent(element, 'mousedown', true);
};
Selenium.prototype.doMouseDownAt = function(locator, coordString) {
- /**
+ /**
* Simulates a user pressing the mouse button (without releasing it yet) on
* the specified element.
*
- * Beware of http://jira.openqa.org/browse/SEL-280, which will lead some event handlers to
- * get null event arguments. Read the bug for more details, including a workaround.
- *
* @param locator an <a href="#locators">element locator</a>
* @param coordString specifies the x,y position (i.e. - 10,20) of the mouse
* event relative to the element returned by the locator.
*/
- var element = this.page().findElement(locator);
+ var element = this.browserbot.findElement(locator);
var clientXY = getClientXY(element, coordString)
- triggerMouseEvent(element, 'mousedown', true, clientXY[0], clientXY[1]);
+ this.browserbot.triggerMouseEvent(element, 'mousedown', true, clientXY[0], clientXY[1]);
};
Selenium.prototype.doMouseUp = function(locator) {
- /**
+ /**
* Simulates a user pressing the mouse button (without releasing it yet) on
* the specified element.
*
* @param locator an <a href="#locators">element locator</a>
*/
- var element = this.page().findElement(locator);
- triggerMouseEvent(element, 'mouseup', true);
+ var element = this.browserbot.findElement(locator);
+ this.browserbot.triggerMouseEvent(element, 'mouseup', true);
};
Selenium.prototype.doMouseUpAt = function(locator, coordString) {
- /**
+ /**
* Simulates a user pressing the mouse button (without releasing it yet) on
* the specified element.
*
- * Beware of http://jira.openqa.org/browse/SEL-280, which will lead some event handlers to
- * get null event arguments. Read the bug for more details, including a workaround.
- *
* @param locator an <a href="#locators">element locator</a>
* @param coordString specifies the x,y position (i.e. - 10,20) of the mouse
* event relative to the element returned by the locator.
*/
- var element = this.page().findElement(locator);
+ var element = this.browserbot.findElement(locator);
var clientXY = getClientXY(element, coordString)
- triggerMouseEvent(element, 'mouseup', true, clientXY[0], clientXY[1]);
+ this.browserbot.triggerMouseEvent(element, 'mouseup', true, clientXY[0], clientXY[1]);
};
Selenium.prototype.doMouseMove = function(locator) {
- /**
+ /**
* Simulates a user pressing the mouse button (without releasing it yet) on
* the specified element.
*
* @param locator an <a href="#locators">element locator</a>
*/
- var element = this.page().findElement(locator);
- triggerMouseEvent(element, 'mousemove', true);
+ var element = this.browserbot.findElement(locator);
+ this.browserbot.triggerMouseEvent(element, 'mousemove', true);
};
Selenium.prototype.doMouseMoveAt = function(locator, coordString) {
- /**
+ /**
* Simulates a user pressing the mouse button (without releasing it yet) on
* the specified element.
*
- * Beware of http://jira.openqa.org/browse/SEL-280, which will lead some event handlers to
- * get null event arguments. Read the bug for more details, including a workaround.
- *
* @param locator an <a href="#locators">element locator</a>
* @param coordString specifies the x,y position (i.e. - 10,20) of the mouse
* event relative to the element returned by the locator.
*/
- var element = this.page().findElement(locator);
+ var element = this.browserbot.findElement(locator);
var clientXY = getClientXY(element, coordString)
- triggerMouseEvent(element, 'mousemove', true, clientXY[0], clientXY[1]);
+ this.browserbot.triggerMouseEvent(element, 'mousemove', true, clientXY[0], clientXY[1]);
};
Selenium.prototype.doType = function(locator, value) {
- /**
+ /**
* Sets the value of an input field, as though you typed it in.
*
* <p>Can also be used to set the value of combo boxes, check boxes, etc. In these cases,
@@ -395,13 +510,65 @@ Selenium.prototype.doType = function(locator, value) {
* @param locator an <a href="#locators">element locator</a>
* @param value the value to type
*/
- // TODO fail if it can't be typed into.
- var element = this.page().findElement(locator);
- this.page().replaceText(element, value);
+ if (this.browserbot.controlKeyDown || this.browserbot.altKeyDown || this.browserbot.metaKeyDown) {
+ throw new SeleniumError("type not supported immediately after call to controlKeyDown() or altKeyDown() or metaKeyDown()");
+ }
+ // TODO fail if it can't be typed into.
+ var element = this.browserbot.findElement(locator);
+ if (this.browserbot.shiftKeyDown) {
+ value = new String(value).toUpperCase();
+ }
+ this.browserbot.replaceText(element, value);
+};
+
+Selenium.prototype.doTypeKeys = function(locator, value) {
+ /**
+ * Simulates keystroke events on the specified element, as though you typed the value key-by-key.
+ *
+ * <p>This is a convenience method for calling keyDown, keyUp, keyPress for every character in the specified string;
+ * this is useful for dynamic UI widgets (like auto-completing combo boxes) that require explicit key events.</p>
+ *
+ * <p>Unlike the simple "type" command, which forces the specified value into the page directly, this command
+ * may or may not have any visible effect, even in cases where typing keys would normally have a visible effect.
+ * For example, if you use "typeKeys" on a form element, you may or may not see the results of what you typed in
+ * the field.</p>
+ * <p>In some cases, you may need to use the simple "type" command to set the value of the field and then the "typeKeys" command to
+ * send the keystroke events corresponding to what you just typed.</p>
+ *
+ * @param locator an <a href="#locators">element locator</a>
+ * @param value the value to type
+ */
+ var keys = new String(value).split("");
+ for (var i = 0; i < keys.length; i++) {
+ var c = keys[i];
+ this.doKeyDown(locator, c);
+ this.doKeyUp(locator, c);
+ this.doKeyPress(locator, c);
+ }
+};
+
+Selenium.prototype.doSetSpeed = function(value) {
+ /**
+ * Set execution speed (i.e., set the millisecond length of a delay which will follow each selenium operation). By default, there is no such delay, i.e.,
+ * the delay is 0 milliseconds.
+ *
+ * @param value the number of milliseconds to pause after operation
+ */
+ throw new SeleniumError("this operation is only implemented in selenium-rc, and should never result in a request making it across the wire");
+};
+
+Selenium.prototype.doGetSpeed = function() {
+ /**
+ * Get execution speed (i.e., get the millisecond length of the delay following each selenium operation). By default, there is no such delay, i.e.,
+ * the delay is 0 milliseconds.
+ *
+ * See also setSpeed.
+ */
+ throw new SeleniumError("this operation is only implemented in selenium-rc, and should never result in a request making it across the wire");
};
Selenium.prototype.findToggleButton = function(locator) {
- var element = this.page().findElement(locator);
+ var element = this.browserbot.findElement(locator);
if (element.checked == null) {
Assert.fail("Element " + locator + " is not a toggle-button.");
}
@@ -409,7 +576,7 @@ Selenium.prototype.findToggleButton = function(locator) {
}
Selenium.prototype.doCheck = function(locator) {
- /**
+ /**
* Check a toggle-button (checkbox/radio)
*
* @param locator an <a href="#locators">element locator</a>
@@ -418,7 +585,7 @@ Selenium.prototype.doCheck = function(locator) {
};
Selenium.prototype.doUncheck = function(locator) {
- /**
+ /**
* Uncheck a toggle-button (checkbox/radio)
*
* @param locator an <a href="#locators">element locator</a>
@@ -427,7 +594,7 @@ Selenium.prototype.doUncheck = function(locator) {
};
Selenium.prototype.doSelect = function(selectLocator, optionLocator) {
- /**
+ /**
* Select an option from a drop-down using an option locator.
*
* <p>
@@ -436,37 +603,37 @@ Selenium.prototype.doSelect = function(selectLocator, optionLocator) {
* that the selected option satisfies a specification). There are several
* forms of Select Option Locator.
* </p>
- * <dl>
- * <dt><strong>label</strong>=<em>labelPattern</em></dt>
- * <dd>matches options based on their labels, i.e. the visible text. (This
+ * <ul>
+ * <li><strong>label</strong>=<em>labelPattern</em>:
+ * matches options based on their labels, i.e. the visible text. (This
* is the default.)
* <ul class="first last simple">
* <li>label=regexp:^[Oo]ther</li>
* </ul>
- * </dd>
- * <dt><strong>value</strong>=<em>valuePattern</em></dt>
- * <dd>matches options based on their values.
+ * </li>
+ * <li><strong>value</strong>=<em>valuePattern</em>:
+ * matches options based on their values.
* <ul class="first last simple">
* <li>value=other</li>
* </ul>
*
*
- * </dd>
- * <dt><strong>id</strong>=<em>id</em></dt>
+ * </li>
+ * <li><strong>id</strong>=<em>id</em>:
*
- * <dd>matches options based on their ids.
+ * matches options based on their ids.
* <ul class="first last simple">
* <li>id=option1</li>
* </ul>
- * </dd>
- * <dt><strong>index</strong>=<em>index</em></dt>
- * <dd>matches an option based on its index (offset from zero).
+ * </li>
+ * <li><strong>index</strong>=<em>index</em>:
+ * matches an option based on its index (offset from zero).
* <ul class="first last simple">
*
* <li>index=2</li>
* </ul>
- * </dd>
- * </dl>
+ * </li>
+ * </ul>
* <p>
* If no option locator prefix is provided, the default behaviour is to match on <strong>label</strong>.
* </p>
@@ -475,15 +642,17 @@ Selenium.prototype.doSelect = function(selectLocator, optionLocator) {
* @param selectLocator an <a href="#locators">element locator</a> identifying a drop-down menu
* @param optionLocator an option locator (a label by default)
*/
- var element = this.page().findElement(selectLocator);
+ var element = this.browserbot.findElement(selectLocator);
if (!("options" in element)) {
throw new SeleniumError("Specified element is not a Select (has no options)");
}
var locator = this.optionLocatorFactory.fromLocatorString(optionLocator);
var option = locator.findOption(element);
- this.page().selectOption(element, option);
+ this.browserbot.selectOption(element, option);
};
+
+
Selenium.prototype.doAddSelection = function(locator, optionLocator) {
/**
* Add a selection to the set of selected options in a multi-select element using an option locator.
@@ -493,13 +662,13 @@ Selenium.prototype.doAddSelection = function(locator, optionLocator) {
* @param locator an <a href="#locators">element locator</a> identifying a multi-select box
* @param optionLocator an option locator (a label by default)
*/
- var element = this.page().findElement(locator);
+ var element = this.browserbot.findElement(locator);
if (!("options" in element)) {
throw new SeleniumError("Specified element is not a Select (has no options)");
}
var locator = this.optionLocatorFactory.fromLocatorString(optionLocator);
var option = locator.findOption(element);
- this.page().addSelection(element, option);
+ this.browserbot.addSelection(element, option);
};
Selenium.prototype.doRemoveSelection = function(locator, optionLocator) {
@@ -512,45 +681,39 @@ Selenium.prototype.doRemoveSelection = function(locator, optionLocator) {
* @param optionLocator an option locator (a label by default)
*/
- var element = this.page().findElement(locator);
+ var element = this.browserbot.findElement(locator);
if (!("options" in element)) {
throw new SeleniumError("Specified element is not a Select (has no options)");
}
var locator = this.optionLocatorFactory.fromLocatorString(optionLocator);
var option = locator.findOption(element);
- this.page().removeSelection(element, option);
+ this.browserbot.removeSelection(element, option);
};
+Selenium.prototype.doRemoveAllSelections = function(locator) {
+ /**
+ * Unselects all of the selected options in a multi-select element.
+ *
+ * @param locator an <a href="#locators">element locator</a> identifying a multi-select box
+ */
+ var element = this.browserbot.findElement(locator);
+ if (!("options" in element)) {
+ throw new SeleniumError("Specified element is not a Select (has no options)");
+ }
+ for (var i = 0; i < element.options.length; i++) {
+ this.browserbot.removeSelection(element, element.options[i]);
+ }
+}
+
Selenium.prototype.doSubmit = function(formLocator) {
- /**
+ /**
* Submit the specified form. This is particularly useful for forms without
* submit buttons, e.g. single-input "Search" forms.
*
* @param formLocator an <a href="#locators">element locator</a> for the form you want to submit
*/
- var form = this.page().findElement(formLocator);
- var actuallySubmit = true;
- if (form.onsubmit) {
- if (browserVersion.isHTA) {
- // run the code in the correct window so alerts are handled correctly even in HTA mode
- var win = this.browserbot.getCurrentWindow();
- var now = new Date().getTime();
- var marker = 'marker' + now;
- win[marker] = form;
- win.setTimeout("var actuallySubmit = "+marker+".onsubmit(); if (actuallySubmit) { "+marker+".submit(); };", 0);
- // pause for at least 20ms for this command to run
- testLoop.waitForCondition = function () {
- return new Date().getTime() > (now + 20);
- }
- } else {
- actuallySubmit = form.onsubmit();
- if (actuallySubmit) {
- form.submit();
- }
- }
- } else {
- form.submit();
- }
+ var form = this.browserbot.findElement(formLocator);
+ return this.browserbot.submit(form);
};
@@ -558,11 +721,11 @@ Selenium.prototype.makePageLoadCondition = function(timeout) {
if (timeout == null) {
timeout = this.defaultTimeout;
}
- return decorateFunctionWithTimeout(this._isNewPageLoaded.bind(this), timeout);
+ return Selenium.decorateFunctionWithTimeout(fnBind(this._isNewPageLoaded, this), timeout);
};
Selenium.prototype.doOpen = function(url) {
- /**
+ /**
* Opens an URL in the test frame. This accepts both relative and absolute
* URLs.
*
@@ -580,35 +743,70 @@ Selenium.prototype.doOpen = function(url) {
return this.makePageLoadCondition();
};
+Selenium.prototype.doOpenWindow = function(url, windowID) {
+ /**
+ * Opens a popup window (if a window with that ID isn't already open).
+ * After opening the window, you'll need to select it using the selectWindow
+ * command.
+ *
+ * <p>This command can also be a useful workaround for bug SEL-339. In some cases, Selenium will be unable to intercept a call to window.open (if the call occurs during or before the "onLoad" event, for example).
+ * In those cases, you can force Selenium to notice the open window's name by using the Selenium openWindow command, using
+ * an empty (blank) url, like this: openWindow("", "myFunnyWindow").</p>
+ *
+ * @param url the URL to open, which can be blank
+ * @param windowID the JavaScript window ID of the window to select
+ */
+ this.browserbot.openWindow(url, windowID);
+};
+
Selenium.prototype.doSelectWindow = function(windowID) {
- /**
+ /**
* Selects a popup window; once a popup window has been selected, all
- * commands go to that window. To select the main window again, use "null"
+ * commands go to that window. To select the main window again, use null
* as the target.
*
+ * <p>Selenium has several strategies for finding the window object referred to by the "windowID" parameter.</p>
+ *
+ * <p>1.) if windowID is null, then it is assumed the user is referring to the original window instantiated by the browser).</p>
+ * <p>2.) if the value of the "windowID" parameter is a JavaScript variable name in the current application window, then it is assumed
+ * that this variable contains the return value from a call to the JavaScript window.open() method.</p>
+ * <p>3.) Otherwise, selenium looks in a hash it maintains that maps string names to window objects. Each of these string
+ * names matches the second parameter "windowName" past to the JavaScript method window.open(url, windowName, windowFeatures, replaceFlag)
+ * (which selenium intercepts).</p>
+ *
+ * <p>If you're having trouble figuring out what is the name of a window that you want to manipulate, look at the selenium log messages
+ * which identify the names of windows created via window.open (and therefore intercepted by selenium). You will see messages
+ * like the following for each window as it is opened:</p>
+ *
+ * <p><code>debug: window.open call intercepted; window ID (which you can use with selectWindow()) is "myNewWindow"</code></p>
+ *
+ * <p>In some cases, Selenium will be unable to intercept a call to window.open (if the call occurs during or before the "onLoad" event, for example).
+ * (This is bug SEL-339.) In those cases, you can force Selenium to notice the open window's name by using the Selenium openWindow command, using
+ * an empty (blank) url, like this: openWindow("", "myFunnyWindow").</p>
+ *
* @param windowID the JavaScript window ID of the window to select
*/
this.browserbot.selectWindow(windowID);
};
Selenium.prototype.doSelectFrame = function(locator) {
- /**
- * Selects a frame within the current window. (You may invoke this command
- * multiple times to select nested frames.) To select the parent frame, use
- * "relative=parent" as a locator; to select the top frame, use "relative=top".
- *
- * <p>You may also use a DOM expression to identify the frame you want directly,
- * like this: <code>dom=frames["main"].frames["subframe"]</code></p>
- *
- * @param locator an <a href="#locators">element locator</a> identifying a frame or iframe
- */
+ /**
+ * Selects a frame within the current window. (You may invoke this command
+ * multiple times to select nested frames.) To select the parent frame, use
+ * "relative=parent" as a locator; to select the top frame, use "relative=top".
+ *
+ * <p>You may also use a DOM expression to identify the frame you want directly,
+ * like this: <code>dom=frames["main"].frames["subframe"]</code></p>
+ *
+ * @param locator an <a href="#locators">element locator</a> identifying a frame or iframe
+ */
this.browserbot.selectFrame(locator);
};
Selenium.prototype.getLogMessages = function() {
- /**
+ /**
* Return the contents of the log.
- *
+ *
* <p>This is a placeholder intended to make the code generator make this API
* available to clients. The selenium server will intercept this call, however,
* and return its recordkeeping of log messages since the last call to this API.
@@ -617,9 +815,9 @@ Selenium.prototype.getLogMessages = function() {
* <p>The reason I opted for a servercentric solution is to be able to support
* multiple frames served from different domains, which would break a
* centralized JavaScript logging mechanism under some conditions.</p>
- *
+ *
* @return string all log messages seen since the last call to this API
- */
+ */
return "getLogMessages should be implemented in the selenium server";
};
@@ -678,52 +876,68 @@ Selenium.prototype.getWhetherThisFrameMatchFrameExpression = function(currentFra
return false;
};
-Selenium.prototype.doWaitForPopUp = function(windowID, timeout) {
- /**
- * Waits for a popup window to appear and load up.
- *
- * @param windowID the JavaScript window ID of the window that will appear
- * @param timeout a timeout in milliseconds, after which the action will return with an error
- */
- if (isNaN(timeout)) {
- throw new SeleniumError("Timeout is not a number: " + timeout);
- }
+Selenium.prototype.getWhetherThisWindowMatchWindowExpression = function(currentWindowString, target) {
+ /**
+ * Determine whether currentWindowString plus target identify the window containing this running code.
+ *
+ * <p>This is useful in proxy injection mode, where this code runs in every
+ * browser frame and window, and sometimes the selenium server needs to identify
+ * the "current" window. In this case, when the test calls selectWindow, this
+ * routine is called for each window to figure out which one has been selected.
+ * The selected window will return true, while all others will return false.</p>
+ *
+ * @param currentWindowString starting window
+ * @param target new window (which might be relative to the current one, e.g., "_parent")
+ * @return boolean true if the new window is this code's window
+ */
+ if (window.opener!=null && window.opener[target]!=null && window.opener[target]==window) {
+ return true;
+ }
+ return false;
+};
+Selenium.prototype.doWaitForPopUp = function(windowID, timeout) {
+ /**
+ * Waits for a popup window to appear and load up.
+ *
+ * @param windowID the JavaScript window ID of the window that will appear
+ * @param timeout a timeout in milliseconds, after which the action will return with an error
+ */
var popupLoadedPredicate = function () {
var targetWindow = selenium.browserbot.getWindowByName(windowID, true);
if (!targetWindow) return false;
if (!targetWindow.location) return false;
if ("about:blank" == targetWindow.location) return false;
if (browserVersion.isKonqueror) {
- if ("/" == targetWindow.location.href) {
- // apparently Konqueror uses this as the temporary location, instead of about:blank
- return false;
- }
+ if ("/" == targetWindow.location.href) {
+ // apparently Konqueror uses this as the temporary location, instead of about:blank
+ return false;
+ }
}
if (browserVersion.isSafari) {
- if(targetWindow.location.href == selenium.browserbot.buttonWindow.location.href) {
- // Apparently Safari uses this as the temporary location, instead of about:blank
- // what a world!
- LOG.debug("DGF what a world!");
- return false;
- }
+ if(targetWindow.location.href == selenium.browserbot.buttonWindow.location.href) {
+ // Apparently Safari uses this as the temporary location, instead of about:blank
+ // what a world!
+ LOG.debug("DGF what a world!");
+ return false;
+ }
}
if (!targetWindow.document) return false;
if (!selenium.browserbot.getCurrentWindow().document.readyState) {
- // This is Firefox, with no readyState extension
- return true;
- }
+ // This is Firefox, with no readyState extension
+ return true;
+ }
if ('complete' != targetWindow.document.readyState) return false;
return true;
};
- return decorateFunctionWithTimeout(popupLoadedPredicate, timeout);
+ return Selenium.decorateFunctionWithTimeout(popupLoadedPredicate, timeout);
}
Selenium.prototype.doWaitForPopUp.dontCheckAlertsAndConfirms = true;
Selenium.prototype.doChooseCancelOnNextConfirmation = function() {
- /**
+ /**
* By default, Selenium's overridden window.confirm() function will
* return true, as if the user had manually clicked OK. After running
* this command, the next call to confirm() will return false, as if
@@ -735,7 +949,7 @@ Selenium.prototype.doChooseCancelOnNextConfirmation = function() {
Selenium.prototype.doAnswerOnNextPrompt = function(answer) {
- /**
+ /**
* Instructs Selenium to return the specified answer string in response to
* the next JavaScript prompt [window.prompt()].
*
@@ -750,7 +964,7 @@ Selenium.prototype.doGoBack = function() {
* Simulates the user clicking the "back" button on their browser.
*
*/
- this.page().goBack();
+ this.browserbot.goBack();
};
Selenium.prototype.doRefresh = function() {
@@ -758,7 +972,7 @@ Selenium.prototype.doRefresh = function() {
* Simulates the user clicking the "Refresh" button on their browser.
*
*/
- this.page().refresh();
+ this.browserbot.refresh();
};
Selenium.prototype.doClose = function() {
@@ -766,7 +980,7 @@ Selenium.prototype.doClose = function() {
* Simulates the user clicking the "close" button in the titlebar of a popup
* window or tab.
*/
- this.page().close();
+ this.browserbot.close();
};
Selenium.prototype.ensureNoUnhandledPopups = function() {
@@ -814,7 +1028,7 @@ Selenium.prototype.isConfirmationPresent = function() {
return this.browserbot.hasConfirmations();
};
Selenium.prototype.getAlert = function() {
- /**
+ /**
* Retrieves the message of a JavaScript alert generated during the previous action, or fail if there were no alerts.
*
* <p>Getting an alert has the same effect as manually clicking OK. If an
@@ -837,7 +1051,7 @@ Selenium.prototype.getAlert = function() {
Selenium.prototype.getAlert.dontCheckAlertsAndConfirms = true;
Selenium.prototype.getConfirmation = function() {
- /**
+ /**
* Retrieves the message of a JavaScript confirmation dialog generated during
* the previous action.
*
@@ -870,7 +1084,7 @@ Selenium.prototype.getConfirmation = function() {
Selenium.prototype.getConfirmation.dontCheckAlertsAndConfirms = true;
Selenium.prototype.getPrompt = function() {
- /**
+ /**
* Retrieves the message of a JavaScript question prompt dialog generated during
* the previous action.
*
@@ -893,28 +1107,28 @@ Selenium.prototype.getPrompt = function() {
};
Selenium.prototype.getLocation = function() {
- /** Gets the absolute URL of the current page.
+ /** Gets the absolute URL of the current page.
*
* @return string the absolute URL of the current page
*/
- return this.page().getCurrentWindow().location;
+ return this.browserbot.getCurrentWindow().location;
};
Selenium.prototype.getTitle = function() {
- /** Gets the title of the current page.
+ /** Gets the title of the current page.
*
* @return string the title of the current page
*/
- return this.page().getTitle();
+ return this.browserbot.getTitle();
};
Selenium.prototype.getBodyText = function() {
- /**
- * Gets the entire text of the page.
- * @return string the entire text of the page
- */
- return this.page().bodyText();
+ /**
+ * Gets the entire text of the page.
+ * @return string the entire text of the page
+ */
+ return this.browserbot.bodyText();
};
@@ -927,12 +1141,12 @@ Selenium.prototype.getValue = function(locator) {
* @param locator an <a href="#locators">element locator</a>
* @return string the element value, or "on/off" for checkbox/radio elements
*/
- var element = this.page().findElement(locator)
+ var element = this.browserbot.findElement(locator)
return getInputValue(element).trim();
}
Selenium.prototype.getText = function(locator) {
- /**
+ /**
* Gets the text of an element. This works for any element that contains
* text. This command uses either the textContent (Mozilla-like browsers) or
* the innerText (IE-like browsers) of the element, which is the rendered
@@ -941,12 +1155,22 @@ Selenium.prototype.getText = function(locator) {
* @param locator an <a href="#locators">element locator</a>
* @return string the text of the element
*/
- var element = this.page().findElement(locator);
+ var element = this.browserbot.findElement(locator);
return getText(element).trim();
};
+Selenium.prototype.doHighlight = function(locator) {
+ /**
+ * Briefly changes the backgroundColor of the specified element yellow. Useful for debugging.
+ *
+ * @param locator an <a href="#locators">element locator</a>
+ */
+ var element = this.browserbot.findElement(locator);
+ this.browserbot.highlight(element, true);
+};
+
Selenium.prototype.getEval = function(script) {
- /** Gets the result of evaluating the specified JavaScript snippet. The snippet may
+ /** Gets the result of evaluating the specified JavaScript snippet. The snippet may
* have multiple lines, but only the result of the last line will be returned.
*
* <p>Note that, by default, the snippet will run in the context of the "selenium"
@@ -956,28 +1180,28 @@ Selenium.prototype.getEval = function(script) {
* <p>If you need a reference to the window of your application, you can refer
* to <code>this.browserbot.getCurrentWindow()</code> and if you need to use
* a locator to refer to a single element in your application page, you can
- * use <code>this.page().findElement("foo")</code> where "foo" is your locator.</p>
+ * use <code>this.browserbot.findElement("foo")</code> where "foo" is your locator.</p>
*
* @param script the JavaScript snippet to run
* @return string the results of evaluating the snippet
*/
try {
- var result = eval(script);
- // Selenium RC doesn't allow returning null
- if (null == result) return "null";
- return result;
+ var result = eval(script);
+ // Selenium RC doesn't allow returning null
+ if (null == result) return "null";
+ return result;
} catch (e) {
- throw new SeleniumError("Threw an exception: " + e.message);
+ throw new SeleniumError("Threw an exception: " + e.message);
}
};
Selenium.prototype.isChecked = function(locator) {
- /**
+ /**
* Gets whether a toggle-button (checkbox/radio) is checked. Fails if the specified element doesn't exist or isn't a toggle-button.
* @param locator an <a href="#locators">element locator</a> pointing to a checkbox or radio button
* @return boolean true if the checkbox is checked, false otherwise
*/
- var element = this.page().findElement(locator);
+ var element = this.browserbot.findElement(locator);
if (element.checked == null) {
throw new SeleniumError("Element " + locator + " is not a toggle-button.");
}
@@ -985,7 +1209,7 @@ Selenium.prototype.isChecked = function(locator) {
};
Selenium.prototype.getTable = function(tableCellAddress) {
- /**
+ /**
* Gets the text from a cell of a table. The cellAddress syntax
* tableLocator.row.column, where row and column start at 0.
*
@@ -1006,7 +1230,7 @@ Selenium.prototype.getTable = function(tableCellAddress) {
row = pieces[2];
col = pieces[3];
- var table = this.page().findElement(tableName);
+ var table = this.browserbot.findElement(tableName);
if (row > table.rows.length) {
Assert.fail("Cannot access row " + row + " - table has " + table.rows.length + " rows");
}
@@ -1017,7 +1241,7 @@ Selenium.prototype.getTable = function(tableCellAddress) {
actualContent = getText(table.rows[row].cells[col]);
return actualContent.trim();
}
- return null;
+ return null;
};
Selenium.prototype.getSelectedLabels = function(selectLocator) {
@@ -1098,7 +1322,7 @@ Selenium.prototype.isSomethingSelected = function(selectLocator) {
* @param selectLocator an <a href="#locators">element locator</a> identifying a drop-down menu
* @return boolean true if some option has been selected, false otherwise
*/
- var element = this.page().findElement(selectLocator);
+ var element = this.browserbot.findElement(selectLocator);
if (!("options" in element)) {
throw new SeleniumError("Specified element is not a Select (has no options)");
}
@@ -1115,12 +1339,12 @@ Selenium.prototype.isSomethingSelected = function(selectLocator) {
}
Selenium.prototype.findSelectedOptionProperties = function(locator, property) {
- var element = this.page().findElement(locator);
+ var element = this.browserbot.findElement(locator);
if (!("options" in element)) {
throw new SeleniumError("Specified element is not a Select (has no options)");
}
- var selectedOptions = [];
+ var selectedOptions = [];
for (var i = 0; i < element.options.length; i++) {
if (element.options[i].selected)
@@ -1145,17 +1369,17 @@ Selenium.prototype.findSelectedOptionProperty = function(locator, property) {
}
Selenium.prototype.getSelectOptions = function(selectLocator) {
- /** Gets all option labels in the specified select drop-down.
+ /** Gets all option labels in the specified select drop-down.
*
* @param selectLocator an <a href="#locators">element locator</a> identifying a drop-down menu
* @return string[] an array of all option labels in the specified select drop-down
*/
- var element = this.page().findElement(selectLocator);
+ var element = this.browserbot.findElement(selectLocator);
var selectOptions = [];
for (var i = 0; i < element.options.length; i++) {
- var option = element.options[i].text.replace(/,/g, "\\,");
+ var option = element.options[i].text.replace(/,/g, "\\,");
selectOptions.push(option);
}
@@ -1164,49 +1388,49 @@ Selenium.prototype.getSelectOptions = function(selectLocator) {
Selenium.prototype.getAttribute = function(attributeLocator) {
- /**
+ /**
* Gets the value of an element attribute.
*
- * Beware of http://jira.openqa.org/browse/SEL-280, which will lead some event handlers to
- * get null event arguments. Read the bug for more details, including a workaround.
- *
* @param attributeLocator an element locator followed by an @ sign and then the name of the attribute, e.g. "foo@bar"
* @return string the value of the specified attribute
*/
- var result = this.page().findAttribute(attributeLocator);
+ var result = this.browserbot.findAttribute(attributeLocator);
if (result == null) {
- throw new SeleniumError("Could not find element attribute: " + attributeLocator);
- }
+ throw new SeleniumError("Could not find element attribute: " + attributeLocator);
+ }
return result;
};
Selenium.prototype.isTextPresent = function(pattern) {
- /**
+ /**
* Verifies that the specified text pattern appears somewhere on the rendered page shown to the user.
* @param pattern a <a href="#patterns">pattern</a> to match with the text of the page
* @return boolean true if the pattern matches the text, false otherwise
*/
- var allText = this.page().bodyText();
+ var allText = this.browserbot.bodyText();
var patternMatcher = new PatternMatcher(pattern);
if (patternMatcher.strategy == PatternMatcher.strategies.glob) {
- patternMatcher.matcher = new PatternMatcher.strategies.globContains(pattern);
+ if (pattern.indexOf("glob:")==0) {
+ pattern = pattern.substring("glob:".length); // strip off "glob:"
+ }
+ patternMatcher.matcher = new PatternMatcher.strategies.globContains(pattern);
}
else if (patternMatcher.strategy == PatternMatcher.strategies.exact) {
- pattern = pattern.substring("exact:".length); // strip off "exact:"
- return allText.indexOf(pattern) != -1;
+ pattern = pattern.substring("exact:".length); // strip off "exact:"
+ return allText.indexOf(pattern) != -1;
}
return patternMatcher.matches(allText);
};
Selenium.prototype.isElementPresent = function(locator) {
- /**
+ /**
* Verifies that the specified element is somewhere on the page.
* @param locator an <a href="#locators">element locator</a>
* @return boolean true if the element is present, false otherwise
*/
try {
- this.page().findElement(locator);
+ this.browserbot.findElement(locator);
} catch (e) {
return false;
}
@@ -1214,7 +1438,7 @@ Selenium.prototype.isElementPresent = function(locator) {
};
Selenium.prototype.isVisible = function(locator) {
- /**
+ /**
* Determines if the specified element is visible. An
* element can be rendered invisible by setting the CSS "visibility"
* property to "hidden", or the "display" property to "none", either for the
@@ -1225,7 +1449,7 @@ Selenium.prototype.isVisible = function(locator) {
* @return boolean true if the specified element is visible, false otherwise
*/
var element;
- element = this.page().findElement(locator);
+ element = this.browserbot.findElement(locator);
var visibility = this.findEffectiveStyleProperty(element, "visibility");
var _isDisplayed = this._isDisplayed(element);
return (visibility != "hidden" && _isDisplayed);
@@ -1269,14 +1493,14 @@ Selenium.prototype.findEffectiveStyle = function(element) {
};
Selenium.prototype.isEditable = function(locator) {
- /**
+ /**
* Determines whether the specified input element is editable, ie hasn't been disabled.
* This method will fail if the specified element isn't an input element.
*
* @param locator an <a href="#locators">element locator</a>
* @return boolean true if the input element is editable, false otherwise
*/
- var element = this.page().findElement(locator);
+ var element = this.browserbot.findElement(locator);
if (element.value == undefined) {
Assert.fail("Element " + locator + " is not an input.");
}
@@ -1284,134 +1508,184 @@ Selenium.prototype.isEditable = function(locator) {
};
Selenium.prototype.getAllButtons = function() {
- /** Returns the IDs of all buttons on the page.
+ /** Returns the IDs of all buttons on the page.
*
* <p>If a given button has no ID, it will appear as "" in this array.</p>
*
* @return string[] the IDs of all buttons on the page
*/
- return this.page().getAllButtons();
+ return this.browserbot.getAllButtons();
};
Selenium.prototype.getAllLinks = function() {
- /** Returns the IDs of all links on the page.
+ /** Returns the IDs of all links on the page.
*
* <p>If a given link has no ID, it will appear as "" in this array.</p>
*
* @return string[] the IDs of all links on the page
*/
- return this.page().getAllLinks();
+ return this.browserbot.getAllLinks();
};
Selenium.prototype.getAllFields = function() {
- /** Returns the IDs of all input fields on the page.
+ /** Returns the IDs of all input fields on the page.
*
* <p>If a given field has no ID, it will appear as "" in this array.</p>
*
* @return string[] the IDs of all field on the page
*/
- return this.page().getAllFields();
-};
-
-Selenium.prototype._getTestAppParentOfAllWindows = function() {
- /** Returns the IDs of all input fields on the page.
- *
- * <p>If a given field has no ID, it will appear as "" in this array.</p>
- *
- * @return string[] the IDs of all field on the page
- */
- if (this.browserbot.getCurrentWindow().opener!=null) {
- return this.browserbot.getCurrentWindow().opener;
- }
- if (this.browserbot.buttonWindow!=null) {
- return this.browserbot.buttonWindow;
- }
- return top; // apparently we are in proxy injection mode
+ return this.browserbot.getAllFields();
};
Selenium.prototype.getAttributeFromAllWindows = function(attributeName) {
- /** Returns every instance of some attribute from all known windows.
- *
- * @param attributeName name of an attribute on the windows
- * @return string[] the set of values of this attribute from all known windows.
- */
- var attributes = new Array();
- var testAppParentOfAllWindows = this._getTestAppParentOfAllWindows();
- attributes.push(eval("testAppParentOfAllWindows." + attributeName));
- var selenium = testAppParentOfAllWindows.selenium==null ? testAppParentOfAllWindows.parent.selenium : testAppParentOfAllWindows.selenium;
- for (windowName in selenium.browserbot.openedWindows)
- {
- attributes.push(eval("selenium.browserbot.openedWindows[windowName]." + attributeName));
- }
- return attributes;
+ /** Returns every instance of some attribute from all known windows.
+ *
+ * @param attributeName name of an attribute on the windows
+ * @return string[] the set of values of this attribute from all known windows.
+ */
+ var attributes = new Array();
+
+ var win = selenium.browserbot.topWindow;
+
+ // DGF normally you should use []s instead of eval "win."+attributeName
+ // but in this case, attributeName may contain dots (e.g. document.title)
+ // in that case, we have no choice but to use eval...
+ attributes.push(eval("win."+attributeName));
+ for (var windowName in this.browserbot.openedWindows)
+ {
+ try {
+ win = selenium.browserbot.openedWindows[windowName];
+ attributes.push(eval("win."+attributeName));
+ } catch (e) {} // DGF If we miss one... meh. It's probably closed or inaccessible anyway.
+ }
+ return attributes;
};
Selenium.prototype.findWindow = function(soughtAfterWindowPropertyValue) {
- var testAppParentOfAllWindows = this._getTestAppParentOfAllWindows();
var targetPropertyName = "name";
if (soughtAfterWindowPropertyValue.match("^title=")) {
- targetPropertyName = "document.title";
+ targetPropertyName = "document.title";
soughtAfterWindowPropertyValue = soughtAfterWindowPropertyValue.replace(/^title=/, "");
}
else {
- // matching "name":
- // If we are not in proxy injection mode, then the top-level test window will be named myiframe.
+ // matching "name":
+ // If we are not in proxy injection mode, then the top-level test window will be named myiframe.
// But as far as the interface goes, we are expected to match a blank string to this window, if
// we are searching with respect to the widow name.
// So make a special case so that this logic will work:
if (PatternMatcher.matches(soughtAfterWindowPropertyValue, "")) {
- return this.browserbot.getCurrentWindow();
+ return this.browserbot.getCurrentWindow();
}
}
- if (PatternMatcher.matches(soughtAfterWindowPropertyValue, eval("testAppParentOfAllWindows." + targetPropertyName))) {
- return testAppParentOfAllWindows;
+ // DGF normally you should use []s instead of eval "win."+attributeName
+ // but in this case, attributeName may contain dots (e.g. document.title)
+ // in that case, we have no choice but to use eval...
+ if (PatternMatcher.matches(soughtAfterWindowPropertyValue, eval("this.browserbot.topWindow." + targetPropertyName))) {
+ return this.browserbot.topWindow;
}
for (windowName in selenium.browserbot.openedWindows) {
- var openedWindow = selenium.browserbot.openedWindows[windowName];
- if (PatternMatcher.matches(soughtAfterWindowPropertyValue, eval("openedWindow." + targetPropertyName))) {
- return openedWindow;
+ var openedWindow = selenium.browserbot.openedWindows[windowName];
+ if (PatternMatcher.matches(soughtAfterWindowPropertyValue, eval("openedWindow." + targetPropertyName))) {
+ return openedWindow;
}
}
throw new SeleniumError("could not find window with property " + targetPropertyName + " matching " + soughtAfterWindowPropertyValue);
};
Selenium.prototype.doDragdrop = function(locator, movementsString) {
- /** Drags an element a certain distance and then drops it
- * Beware of http://jira.openqa.org/browse/SEL-280, which will lead some event handlers to
- * get null event arguments. Read the bug for more details, including a workaround.
+/** deprecated - use dragAndDrop instead
*
- * @param movementsString offset in pixels from the current location to which the element should be moved, e.g., "+70,-300"
* @param locator an element locator
+ * @param movementsString offset in pixels from the current location to which the element should be moved, e.g., "+70,-300"
*/
- var element = this.page().findElement(locator);
- var clientStartXY = getClientXY(element)
- var clientStartX = clientStartXY[0];
- var clientStartY = clientStartXY[1];
-
- var movements = movementsString.split(/,/);
- var movementX = Number(movements[0]);
- var movementY = Number(movements[1]);
+ this.doDragAndDrop(locator, movementsString);
+};
- var clientFinishX = ((clientStartX + movementX) < 0) ? 0 : (clientStartX + movementX);
- var clientFinishY = ((clientStartY + movementY) < 0) ? 0 : (clientStartY + movementY);
+Selenium.prototype.doSetMouseSpeed = function(pixels) {
+ /** Configure the number of pixels between "mousemove" events during dragAndDrop commands (default=10).
+ * <p>Setting this value to 0 means that we'll send a "mousemove" event to every single pixel
+ * in between the start location and the end location; that can be very slow, and may
+ * cause some browsers to force the JavaScript to timeout.</p>
+ *
+ * <p>If the mouse speed is greater than the distance between the two dragged objects, we'll
+ * just send one "mousemove" at the start location and then one final one at the end location.</p>
+ * @param pixels the number of pixels between "mousemove" events
+ */
+ this.mouseSpeed = pixels;
+}
+
+Selenium.prototype.getMouseSpeed = function() {
+ /** Returns the number of pixels between "mousemove" events during dragAndDrop commands (default=10).
+ *
+ * @return number the number of pixels between "mousemove" events during dragAndDrop commands (default=10)
+ */
+ this.mouseSpeed = pixels;
+}
- var movementXincrement = (movementX > 0) ? 1 : -1;
- var movementYincrement = (movementY > 0) ? 1 : -1;
- triggerMouseEvent(element, 'mousedown', true, clientStartX, clientStartY);
- var clientX = clientStartX;
- var clientY = clientStartY;
- while ((clientX != clientFinishX) || (clientY != clientFinishY)) {
- if (clientX != clientFinishX) {
- clientX += movementXincrement;
- }
- if (clientY != clientFinishY) {
- clientY += movementYincrement;
- }
- triggerMouseEvent(element, 'mousemove', true, clientX, clientY);
+Selenium.prototype.doDragAndDrop = function(locator, movementsString) {
+ /** Drags an element a certain distance and then drops it
+ * @param locator an element locator
+ * @param movementsString offset in pixels from the current location to which the element should be moved, e.g., "+70,-300"
+ */
+ var element = this.browserbot.findElement(locator);
+ var clientStartXY = getClientXY(element)
+ var clientStartX = clientStartXY[0];
+ var clientStartY = clientStartXY[1];
+
+ var movements = movementsString.split(/,/);
+ var movementX = Number(movements[0]);
+ var movementY = Number(movements[1]);
+
+ var clientFinishX = ((clientStartX + movementX) < 0) ? 0 : (clientStartX + movementX);
+ var clientFinishY = ((clientStartY + movementY) < 0) ? 0 : (clientStartY + movementY);
+
+ var mouseSpeed = this.mouseSpeed;
+ var move = function(current, dest) {
+ if (current == dest) return current;
+ if (Math.abs(current - dest) < mouseSpeed) return dest;
+ return (current < dest) ? current + mouseSpeed : current - mouseSpeed;
+ }
+
+ this.browserbot.triggerMouseEvent(element, 'mousedown', true, clientStartX, clientStartY);
+ this.browserbot.triggerMouseEvent(element, 'mousemove', true, clientStartX, clientStartY);
+ var clientX = clientStartX;
+ var clientY = clientStartY;
+
+ while ((clientX != clientFinishX) || (clientY != clientFinishY)) {
+ clientX = move(clientX, clientFinishX);
+ clientY = move(clientY, clientFinishY);
+ this.browserbot.triggerMouseEvent(element, 'mousemove', true, clientX, clientY);
}
- triggerMouseEvent(element, 'mouseup', true, clientFinishX, clientFinishY);
+
+ this.browserbot.triggerMouseEvent(element, 'mousemove', true, clientFinishX, clientFinishY);
+ this.browserbot.triggerMouseEvent(element, 'mouseup', true, clientFinishX, clientFinishY);
+};
+
+Selenium.prototype.doDragAndDropToObject = function(locatorOfObjectToBeDragged, locatorOfDragDestinationObject) {
+/** Drags an element and drops it on another element
+ *
+ * @param locatorOfObjectToBeDragged an element to be dragged
+ * @param locatorOfDragDestinationObject an element whose location (i.e., whose center-most pixel) will be the point where locatorOfObjectToBeDragged is dropped
+ */
+ var startX = this.getElementPositionLeft(locatorOfObjectToBeDragged);
+ var startY = this.getElementPositionTop(locatorOfObjectToBeDragged);
+
+ var destinationLeftX = this.getElementPositionLeft(locatorOfDragDestinationObject);
+ var destinationTopY = this.getElementPositionTop(locatorOfDragDestinationObject);
+ var destinationWidth = this.getElementWidth(locatorOfDragDestinationObject);
+ var destinationHeight = this.getElementHeight(locatorOfDragDestinationObject);
+
+ var endX = Math.round(destinationLeftX + (destinationWidth / 2));
+ var endY = Math.round(destinationTopY + (destinationHeight / 2));
+
+ var deltaX = endX - startX;
+ var deltaY = endY - startY;
+
+ var movementsString = "" + deltaX + "," + deltaY;
+
+ this.doDragAndDrop(locatorOfObjectToBeDragged, movementsString);
};
Selenium.prototype.doWindowFocus = function(windowName) {
@@ -1430,7 +1704,7 @@ Selenium.prototype.doWindowMaximize = function(windowName) {
*/
var window = this.findWindow(windowName);
if (window!=null && window.screen) {
- window.moveTo(0,0);
+ window.moveTo(0,0);
window.outerHeight = screen.availHeight;
window.outerWidth = screen.availWidth;
}
@@ -1461,32 +1735,32 @@ Selenium.prototype.getAllWindowTitles = function() {
};
Selenium.prototype.getHtmlSource = function() {
- /** Returns the entire HTML source between the opening and
+ /** Returns the entire HTML source between the opening and
* closing "html" tags.
*
* @return string the entire HTML source
*/
- return this.page().document().getElementsByTagName("html")[0].innerHTML;
+ return this.browserbot.getDocument().getElementsByTagName("html")[0].innerHTML;
};
Selenium.prototype.doSetCursorPosition = function(locator, position) {
- /**
+ /**
* Moves the text cursor to the specified position in the given input element or textarea.
* This method will fail if the specified element isn't an input element or textarea.
*
* @param locator an <a href="#locators">element locator</a> pointing to an input element or textarea
* @param position the numerical position of the cursor in the field; position should be 0 to move the position to the beginning of the field. You can also set the cursor to -1 to move it to the end of the field.
*/
- var element = this.page().findElement(locator);
+ var element = this.browserbot.findElement(locator);
if (element.value == undefined) {
Assert.fail("Element " + locator + " is not an input.");
}
if (position == -1) {
- position = element.value.length;
+ position = element.value.length;
}
if( element.setSelectionRange && !browserVersion.isOpera) {
- element.focus();
+ element.focus();
element.setSelectionRange(/*start*/position,/*end*/position);
}
else if( element.createTextRange ) {
@@ -1507,7 +1781,7 @@ Selenium.prototype.getElementIndex = function(locator) {
* @param locator an <a href="#locators">element locator</a> pointing to an element
* @return number of relative index of the element to its parent (starting from 0)
*/
- var element = this.page().findElement(locator);
+ var element = this.browserbot.findElement(locator);
var previousSibling;
var index = 0;
while ((previousSibling = element.previousSibling) != null) {
@@ -1528,8 +1802,8 @@ Selenium.prototype.isOrdered = function(locator1, locator2) {
* @param locator2 an <a href="#locators">element locator</a> pointing to the second element
* @return boolean true if two elements are ordered and have same parent, false otherwise
*/
- var element1 = this.page().findElement(locator1);
- var element2 = this.page().findElement(locator2);
+ var element1 = this.browserbot.findElement(locator1);
+ var element2 = this.browserbot.findElement(locator2);
if (element1 === element2) return false;
var previousSibling;
@@ -1553,48 +1827,48 @@ Selenium.prototype.getElementPositionLeft = function(locator) {
* @param locator an <a href="#locators">element locator</a> pointing to an element OR an element itself
* @return number of pixels from the edge of the frame.
*/
- var element;
+ var element;
if ("string"==typeof locator) {
- element = this.page().findElement(locator);
+ element = this.browserbot.findElement(locator);
}
else {
- element = locator;
+ element = locator;
+ }
+ var x = element.offsetLeft;
+ var elementParent = element.offsetParent;
+
+ while (elementParent != null)
+ {
+ if(document.all)
+ {
+ if( (elementParent.tagName != "TABLE") && (elementParent.tagName != "BODY") )
+ {
+ x += elementParent.clientLeft;
+ }
+ }
+ else // Netscape/DOM
+ {
+ if(elementParent.tagName == "TABLE")
+ {
+ var parentBorder = parseInt(elementParent.border);
+ if(isNaN(parentBorder))
+ {
+ var parentFrame = elementParent.getAttribute('frame');
+ if(parentFrame != null)
+ {
+ x += 1;
+ }
+ }
+ else if(parentBorder > 0)
+ {
+ x += parentBorder;
+ }
+ }
}
- var x = element.offsetLeft;
- var elementParent = element.offsetParent;
-
- while (elementParent != null)
- {
- if(document.all)
- {
- if( (elementParent.tagName != "TABLE") && (elementParent.tagName != "BODY") )
- {
- x += elementParent.clientLeft;
- }
- }
- else // Netscape/DOM
- {
- if(elementParent.tagName == "TABLE")
- {
- var parentBorder = parseInt(elementParent.border);
- if(isNaN(parentBorder))
- {
- var parentFrame = elementParent.getAttribute('frame');
- if(parentFrame != null)
- {
- x += 1;
- }
- }
- else if(parentBorder > 0)
- {
- x += parentBorder;
- }
- }
- }
- x += elementParent.offsetLeft;
- elementParent = elementParent.offsetParent;
- }
- return x;
+ x += elementParent.offsetLeft;
+ elementParent = elementParent.offsetParent;
+ }
+ return x;
};
Selenium.prototype.getElementPositionTop = function(locator) {
@@ -1604,61 +1878,61 @@ Selenium.prototype.getElementPositionTop = function(locator) {
* @param locator an <a href="#locators">element locator</a> pointing to an element OR an element itself
* @return number of pixels from the edge of the frame.
*/
- var element;
+ var element;
if ("string"==typeof locator) {
- element = this.page().findElement(locator);
+ element = this.browserbot.findElement(locator);
}
else {
- element = locator;
+ element = locator;
}
- var y = 0;
-
- while (element != null)
- {
- if(document.all)
- {
- if( (element.tagName != "TABLE") && (element.tagName != "BODY") )
- {
- y += element.clientTop;
- }
- }
- else // Netscape/DOM
- {
- if(element.tagName == "TABLE")
- {
- var parentBorder = parseInt(element.border);
- if(isNaN(parentBorder))
- {
- var parentFrame = element.getAttribute('frame');
- if(parentFrame != null)
- {
- y += 1;
- }
- }
- else if(parentBorder > 0)
- {
- y += parentBorder;
- }
- }
- }
- y += element.offsetTop;
-
- // Netscape can get confused in some cases, such that the height of the parent is smaller
- // than that of the element (which it shouldn't really be). If this is the case, we need to
- // exclude this element, since it will result in too large a 'top' return value.
- if (element.offsetParent && element.offsetParent.offsetHeight && element.offsetParent.offsetHeight < element.offsetHeight)
- {
- // skip the parent that's too small
- element = element.offsetParent.offsetParent;
- }
- else
- {
- // Next up...
- element = element.offsetParent;
- }
- }
- return y;
+ var y = 0;
+
+ while (element != null)
+ {
+ if(document.all)
+ {
+ if( (element.tagName != "TABLE") && (element.tagName != "BODY") )
+ {
+ y += element.clientTop;
+ }
+ }
+ else // Netscape/DOM
+ {
+ if(element.tagName == "TABLE")
+ {
+ var parentBorder = parseInt(element.border);
+ if(isNaN(parentBorder))
+ {
+ var parentFrame = element.getAttribute('frame');
+ if(parentFrame != null)
+ {
+ y += 1;
+ }
+ }
+ else if(parentBorder > 0)
+ {
+ y += parentBorder;
+ }
+ }
+ }
+ y += element.offsetTop;
+
+ // Netscape can get confused in some cases, such that the height of the parent is smaller
+ // than that of the element (which it shouldn't really be). If this is the case, we need to
+ // exclude this element, since it will result in too large a 'top' return value.
+ if (element.offsetParent && element.offsetParent.offsetHeight && element.offsetParent.offsetHeight < element.offsetHeight)
+ {
+ // skip the parent that's too small
+ element = element.offsetParent.offsetParent;
+ }
+ else
+ {
+ // Next up...
+ element = element.offsetParent;
+ }
+ }
+ return y;
};
Selenium.prototype.getElementWidth = function(locator) {
@@ -1668,7 +1942,7 @@ Selenium.prototype.getElementWidth = function(locator) {
* @param locator an <a href="#locators">element locator</a> pointing to an element
* @return number width of an element in pixels
*/
- var element = this.page().findElement(locator);
+ var element = this.browserbot.findElement(locator);
return element.offsetWidth;
};
@@ -1679,12 +1953,12 @@ Selenium.prototype.getElementHeight = function(locator) {
* @param locator an <a href="#locators">element locator</a> pointing to an element
* @return number height of an element in pixels
*/
- var element = this.page().findElement(locator);
+ var element = this.browserbot.findElement(locator);
return element.offsetHeight;
};
Selenium.prototype.getCursorPosition = function(locator) {
- /**
+ /**
* Retrieves the text cursor position in the given input element or textarea; beware, this may not work perfectly on all browsers.
*
* <p>Specifically, if the cursor/selection has been cleared by JavaScript, this command will tend to
@@ -1694,37 +1968,37 @@ Selenium.prototype.getCursorPosition = function(locator) {
* @param locator an <a href="#locators">element locator</a> pointing to an input element or textarea
* @return number the numerical position of the cursor in the field
*/
- var element = this.page().findElement(locator);
- var doc = this.page().getDocument();
- var win = this.browserbot.getCurrentWindow();
- if( doc.selection && !browserVersion.isOpera){
- var selectRange = doc.selection.createRange().duplicate();
- var elementRange = element.createTextRange();
- selectRange.move("character",0);
- elementRange.move("character",0);
- var inRange1 = selectRange.inRange(elementRange);
- var inRange2 = elementRange.inRange(selectRange);
- try {
- elementRange.setEndPoint("EndToEnd", selectRange);
- } catch (e) {
- Assert.fail("There is no cursor on this page!");
- }
- var answer = String(elementRange.text).replace(/\r/g,"").length;
- return answer;
- } else {
- if (typeof(element.selectionStart) != "undefined") {
- if (win.getSelection && typeof(win.getSelection().rangeCount) != undefined && win.getSelection().rangeCount == 0) {
- Assert.fail("There is no cursor on this page!");
- }
- return element.selectionStart;
- }
- }
- throw new Error("Couldn't detect cursor position on this browser!");
+ var element = this.browserbot.findElement(locator);
+ var doc = this.browserbot.getDocument();
+ var win = this.browserbot.getCurrentWindow();
+ if( doc.selection && !browserVersion.isOpera){
+ try {
+ var selectRange = doc.selection.createRange().duplicate();
+ var elementRange = element.createTextRange();
+ selectRange.move("character",0);
+ elementRange.move("character",0);
+ var inRange1 = selectRange.inRange(elementRange);
+ var inRange2 = elementRange.inRange(selectRange);
+ elementRange.setEndPoint("EndToEnd", selectRange);
+ } catch (e) {
+ Assert.fail("There is no cursor on this page!");
+ }
+ var answer = String(elementRange.text).replace(/\r/g,"").length;
+ return answer;
+ } else {
+ if (typeof(element.selectionStart) != "undefined") {
+ if (win.getSelection && typeof(win.getSelection().rangeCount) != undefined && win.getSelection().rangeCount == 0) {
+ Assert.fail("There is no cursor on this page!");
+ }
+ return element.selectionStart;
+ }
+ }
+ throw new Error("Couldn't detect cursor position on this browser!");
}
Selenium.prototype.doSetContext = function(context, logLevelThreshold) {
- /**
+ /**
* Writes a message to the status bar and adds a note to the browser-side
* log.
*
@@ -1739,26 +2013,26 @@ Selenium.prototype.doSetContext = function(context, logLevelThreshold) {
* @param logLevelThreshold one of "debug", "info", "warn", "error", sets the threshold for browser-side logging
*/
if (logLevelThreshold==null || logLevelThreshold=="") {
- return this.page().setContext(context);
+ return this.browserbot.setContext(context);
}
- return this.page().setContext(context, logLevelThreshold);
+ return this.browserbot.setContext(context, logLevelThreshold);
};
Selenium.prototype.getExpression = function(expression) {
- /**
- * Returns the specified expression.
- *
- * <p>This is useful because of JavaScript preprocessing.
- * It is used to generate commands like assertExpression and waitForExpression.</p>
- *
- * @param expression the value to return
- * @return string the value passed in
- */
- return expression;
+ /**
+ * Returns the specified expression.
+ *
+ * <p>This is useful because of JavaScript preprocessing.
+ * It is used to generate commands like assertExpression and waitForExpression.</p>
+ *
+ * @param expression the value to return
+ * @return string the value passed in
+ */
+ return expression;
}
Selenium.prototype.doWaitForCondition = function(script, timeout) {
- /**
+ /**
* Runs the specified JavaScript snippet repeatedly until it evaluates to "true".
* The snippet may have multiple lines, but only the result of the last line
* will be considered.
@@ -1770,10 +2044,7 @@ Selenium.prototype.doWaitForCondition = function(script, timeout) {
* @param script the JavaScript snippet to run
* @param timeout a timeout in milliseconds, after which this command will return with an error
*/
- if (isNaN(timeout)) {
- throw new SeleniumError("Timeout is not a number: " + timeout);
- }
- return decorateFunctionWithTimeout(function () {
+ return Selenium.decorateFunctionWithTimeout(function () {
return eval(script);
}, timeout);
};
@@ -1781,32 +2052,32 @@ Selenium.prototype.doWaitForCondition = function(script, timeout) {
Selenium.prototype.doWaitForCondition.dontCheckAlertsAndConfirms = true;
Selenium.prototype.doSetTimeout = function(timeout) {
- /**
- * Specifies the amount of time that Selenium will wait for actions to complete.
- *
- * <p>Actions that require waiting include "open" and the "waitFor*" actions.</p>
- * The default timeout is 30 seconds.
- * @param timeout a timeout in milliseconds, after which the action will return with an error
- */
- this.defaultTimeout = parseInt(timeout);
+ /**
+ * Specifies the amount of time that Selenium will wait for actions to complete.
+ *
+ * <p>Actions that require waiting include "open" and the "waitFor*" actions.</p>
+ * The default timeout is 30 seconds.
+ * @param timeout a timeout in milliseconds, after which the action will return with an error
+ */
+ if (!timeout) {
+ timeout = Selenium.DEFAULT_TIMEOUT;
+ }
+ this.defaultTimeout = timeout;
}
Selenium.prototype.doWaitForPageToLoad = function(timeout) {
- /**
- * Waits for a new page to load.
- *
- * <p>You can use this command instead of the "AndWait" suffixes, "clickAndWait", "selectAndWait", "typeAndWait" etc.
- * (which are only available in the JS API).</p>
- *
- * <p>Selenium constantly keeps track of new pages loading, and sets a "newPageLoaded"
- * flag when it first notices a page load. Running any other Selenium command after
- * turns the flag to false. Hence, if you want to wait for a page to load, you must
- * wait immediately after a Selenium command that caused a page-load.</p>
- * @param timeout a timeout in milliseconds, after which this command will return with an error
- */
- if (isNaN(timeout)) {
- throw new SeleniumError("Timeout is not a number: " + timeout);
- }
+ /**
+ * Waits for a new page to load.
+ *
+ * <p>You can use this command instead of the "AndWait" suffixes, "clickAndWait", "selectAndWait", "typeAndWait" etc.
+ * (which are only available in the JS API).</p>
+ *
+ * <p>Selenium constantly keeps track of new pages loading, and sets a "newPageLoaded"
+ * flag when it first notices a page load. Running any other Selenium command after
+ * turns the flag to false. Hence, if you want to wait for a page to load, you must
+ * wait immediately after a Selenium command that caused a page-load.</p>
+ * @param timeout a timeout in milliseconds, after which this command will return with an error
+ */
// in pi-mode, the test and the harness share the window; thus if we are executing this code, then we have loaded
if (window["proxyInjectionMode"] == null || !window["proxyInjectionMode"]) {
return this.makePageLoadCondition(timeout);
@@ -1862,7 +2133,7 @@ Selenium.prototype.getCookie = function() {
*
* @return string all cookies of the current page under test
*/
- var doc = this.page().document();
+ var doc = this.browserbot.getDocument();
return doc.cookie;
};
@@ -1888,9 +2159,17 @@ Selenium.prototype.doCreateCookie = function(nameValuePair, optionsString) {
}
results = /path=([^\s,]+)[,]?/.exec(optionsString);
if (results) {
- cookie += "; path=" + results[1];
+ var path = results[1];
+ if (browserVersion.khtml) {
+ // Safari and conquerer don't like paths with / at the end
+ if ("/" != path) {
+ path = path.replace(/\/$/, "");
+ }
+ }
+ cookie += "; path=" + path;
}
- this.page().document().cookie = cookie;
+ LOG.debug("Setting cookie to: " + cookie);
+ this.browserbot.getDocument().cookie = cookie;
}
Selenium.prototype.doDeleteCookie = function(name,path) {
@@ -1901,8 +2180,17 @@ Selenium.prototype.doDeleteCookie = function(name,path) {
* @param path the path property of the cookie to be deleted
*/
// set the expire time of the cookie to be deleted to one minute before now.
+ path = path.trim();
+ if (browserVersion.khtml) {
+ // Safari and conquerer don't like paths with / at the end
+ if ("/" != path) {
+ path = path.replace(/\/$/, "");
+ }
+ }
var expireDateInMilliseconds = (new Date()).getTime() + (-1 * 1000);
- this.page().document().cookie = name.trim() + "=deleted; path=" + path.trim() + "; expires=" + new Date(expireDateInMilliseconds).toGMTString();
+ var cookie = name.trim() + "=deleted; path=" + path + "; expires=" + new Date(expireDateInMilliseconds).toGMTString();
+ LOG.debug("Setting cookie to: " + cookie);
+ this.browserbot.getDocument().cookie = cookie;
}
@@ -2015,7 +2303,7 @@ OptionLocatorFactory.prototype.OptionLocatorByIndex = function(index) {
};
this.assertSelected = function(element) {
- Assert.equals(this.index, element.selectedIndex);
+ Assert.equals(this.index, element.selectedIndex);
};
};
diff --git a/tests/test_tools/selenium/core/scripts/selenium-browserbot.js b/tests/test_tools/selenium/core/scripts/selenium-browserbot.js
index 22df0fdb..633289e2 100644
--- a/tests/test_tools/selenium/core/scripts/selenium-browserbot.js
+++ b/tests/test_tools/selenium/core/scripts/selenium-browserbot.js
@@ -26,19 +26,24 @@
// The window to which the commands will be sent. For example, to click on a
// popup window, first select that window, and then do a normal click command.
-
var BrowserBot = function(topLevelApplicationWindow) {
this.topWindow = topLevelApplicationWindow;
+ this.topFrame = this.topWindow;
+ this.baseUrl=window.location.href;
// the buttonWindow is the Selenium window
// it contains the Run/Pause buttons... this should *not* be the AUT window
- // todo: Here the buttonWindow is not Selenium window. It will be set to Selenium window in pollForLoad.
- // Change this!!!
- this.buttonWindow = this.topWindow;
- // not sure what this is used for
- this.currentPage = null;
+ this.buttonWindow = window;
this.currentWindow = this.topWindow;
this.currentWindowName = null;
+
+ // We need to know this in advance, in case the frame closes unexpectedly
+ this.isSubFrameSelected = false;
+
+ this.altKeyDown = false;
+ this.controlKeyDown = false;
+ this.shiftKeyDown = false;
+ this.metaKeyDown = false;
this.modalDialogTest = null;
this.recordedAlerts = new Array();
@@ -49,35 +54,56 @@ var BrowserBot = function(topLevelApplicationWindow) {
this.nextPromptResult = '';
this.newPageLoaded = false;
this.pageLoadError = null;
+
+ this.shouldHighlightLocatedElement = false;
this.uniqueId = new Date().getTime();
this.pollingForLoad = new Object();
+ this.permDeniedCount = new Object();
this.windowPollers = new Array();
+ // DGF for backwards compatibility
+ this.browserbot = this;
var self = this;
- this.recordPageLoad = function() {
+
+ objectExtend(this, PageBot.prototype);
+ this._registerAllLocatorFunctions();
+
+ this.recordPageLoad = function(elementOrWindow) {
LOG.debug("Page load detected");
try {
- LOG.debug("Page load location=" + self.getCurrentWindow(true).location);
+ if (elementOrWindow.location && elementOrWindow.location.href) {
+ LOG.debug("Page load location=" + elementOrWindow.location.href);
+ } else if (elementOrWindow.contentWindow && elementOrWindow.contentWindow.location && elementOrWindow.contentWindow.location.href) {
+ LOG.debug("Page load location=" + elementOrWindow.contentWindow.location.href);
+ } else {
+ LOG.debug("Page load location unknown, current window location=" + this.getCurrentWindow(true).location);
+ }
} catch (e) {
+ LOG.error("Caught an exception attempting to log location; this should get noticed soon!");
+ LOG.exception(e);
self.pageLoadError = e;
return;
}
- self.currentPage = null;
self.newPageLoaded = true;
};
this.isNewPageLoaded = function() {
if (this.pageLoadError) {
+ LOG.error("isNewPageLoaded found an old pageLoadError");
var e = this.pageLoadError;
this.pageLoadError = null;
throw e;
}
return self.newPageLoaded;
};
+
};
-BrowserBot.createForWindow = function(window) {
+// DGF PageBot exists for backwards compatibility with old user-extensions
+var PageBot = function(){};
+
+BrowserBot.createForWindow = function(window, proxyInjectionMode) {
var browserbot;
LOG.debug('createForWindow');
LOG.debug("browserName: " + browserVersion.name);
@@ -98,8 +124,9 @@ BrowserBot.createForWindow = function(window) {
// Use mozilla by default
browserbot = new MozillaBrowserBot(window);
}
- browserbot.getCurrentWindow();
- // todo: why?
+ // getCurrentWindow has the side effect of modifying it to handle page loads etc
+ browserbot.proxyInjectionMode = proxyInjectionMode;
+ browserbot.getCurrentWindow(); // for modifyWindow side effect. This is not a transparent style
return browserbot;
};
@@ -156,6 +183,74 @@ BrowserBot.prototype.getNextPrompt = function() {
return t;
};
+/* Fire a mouse event in a browser-compatible manner */
+
+BrowserBot.prototype.triggerMouseEvent = function(element, eventType, canBubble, clientX, clientY) {
+ clientX = clientX ? clientX : 0;
+ clientY = clientY ? clientY : 0;
+
+ LOG.warn("triggerMouseEvent assumes setting screenX and screenY to 0 is ok");
+ var screenX = 0;
+ var screenY = 0;
+
+ canBubble = (typeof(canBubble) == undefined) ? true : canBubble;
+ if (element.fireEvent) {
+ LOG.info("element has fireEvent");
+ var evt = createEventObject(element, this.controlKeyDown, this.altKeyDown, this.shiftKeyDown, this.metaKeyDown);
+ evt.detail = 0;
+ evt.button = 1;
+ evt.relatedTarget = null;
+ if (!screenX && !screenY && !clientX && !clientY && !this.controlKeyDown && !this.altKeyDown && !this.shiftKeyDown && !this.metaKeyDown) {
+ element.fireEvent('on' + eventType);
+ }
+ else {
+ evt.screenX = screenX;
+ evt.screenY = screenY;
+ evt.clientX = clientX;
+ evt.clientY = clientY;
+
+ // when we go this route, window.event is never set to contain the event we have just created.
+ // ideally we could just slide it in as follows in the try-block below, but this normally
+ // doesn't work. This is why I try to avoid this code path, which is only required if we need to
+ // set attributes on the event (e.g., clientX).
+ try {
+ window.event = evt;
+ }
+ catch(e) {
+ // getting an "Object does not support this action or property" error. Save the event away
+ // for future reference.
+ // TODO: is there a way to update window.event?
+
+ // work around for http://jira.openqa.org/browse/SEL-280 -- make the event available somewhere:
+ selenium.browserbot.getCurrentWindow().selenium_event = evt;
+ }
+ element.fireEvent('on' + eventType, evt);
+ }
+ }
+ else {
+ LOG.info("element doesn't have fireEvent");
+ var evt = document.createEvent('MouseEvents');
+ if (evt.initMouseEvent)
+ {
+ LOG.info("element has initMouseEvent");
+ //Safari
+ evt.initMouseEvent(eventType, canBubble, true, document.defaultView, 1, screenX, screenY, clientX, clientY,
+ this.controlKeyDown, this.altKeyDown, this.shiftKeyDown, this.metaKeyDown, 0, null);
+ }
+ else {
+ LOG.warn("element doesn't have initMouseEvent; firing an event which should -- but doesn't -- have other mouse-event related attributes here, as well as controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown");
+ evt.initEvent(eventType, canBubble, true);
+
+ evt.shiftKey = this.shiftKeyDown;
+ evt.metaKey = this.metaKeyDown;
+ evt.altKey = this.altKeyDown;
+ evt.ctrlKey = this.controlKeyDown;
+
+ }
+ element.dispatchEvent(evt);
+ }
+}
+
BrowserBot.prototype._windowClosed = function(win) {
var c = win.closed;
if (c == null) return true;
@@ -163,25 +258,35 @@ BrowserBot.prototype._windowClosed = function(win) {
};
BrowserBot.prototype._modifyWindow = function(win) {
+ // In proxyInjectionMode, have to suppress LOG calls in _modifyWindow to avoid an infinite loop
if (this._windowClosed(win)) {
- LOG.error("modifyWindow: Window was closed!");
+ if (!this.proxyInjectionMode) {
+ LOG.error("modifyWindow: Window was closed!");
+ }
return null;
}
- LOG.debug('modifyWindow ' + this.uniqueId + ":" + win[this.uniqueId]);
+ if (!this.proxyInjectionMode) {
+ LOG.debug('modifyWindow ' + this.uniqueId + ":" + win[this.uniqueId]);
+ }
if (!win[this.uniqueId]) {
win[this.uniqueId] = true;
this.modifyWindowToRecordPopUpDialogs(win, this);
- this.currentPage = PageBot.createForWindow(this);
- this.newPageLoaded = false;
}
- this.modifySeparateTestWindowToDetectPageLoads(win);
+ // In proxyInjection mode, we have our own mechanism for detecting page loads
+ if (!this.proxyInjectionMode) {
+ this.modifySeparateTestWindowToDetectPageLoads(win);
+ }
+ if (win.frames && win.frames.length && win.frames.length > 0) {
+ for (var i = 0; i < win.frames.length; i++) {
+ try {
+ this._modifyWindow(win.frames[i]);
+ } catch (e) {} // we're just trying to be opportunistic; don't worry if this doesn't work out
+ }
+ }
return win;
};
BrowserBot.prototype.selectWindow = function(target) {
- // we've moved to a new page - clear the current one
- this.currentPage = null;
-
if (target && target != "null") {
this._selectWindowByName(target);
} else {
@@ -192,67 +297,117 @@ BrowserBot.prototype.selectWindow = function(target) {
BrowserBot.prototype._selectTopWindow = function() {
this.currentWindowName = null;
this.currentWindow = this.topWindow;
+ this.topFrame = this.topWindow;
+ this.isSubFrameSelected = false;
}
BrowserBot.prototype._selectWindowByName = function(target) {
this.currentWindow = this.getWindowByName(target, false);
+ this.topFrame = this.currentWindow;
this.currentWindowName = target;
+ this.isSubFrameSelected = false;
}
BrowserBot.prototype.selectFrame = function(target) {
if (target == "relative=up") {
this.currentWindow = this.getCurrentWindow().parent;
+ this.isSubFrameSelected = (this._getFrameElement(this.currentWindow) != null);
} else if (target == "relative=top") {
- this.currentWindow = this.topWindow;
+ this.currentWindow = this.topFrame;
+ this.isSubFrameSelected = false;
} else {
- var frame = this.getCurrentPage().findElement(target);
+ var frame = this.findElement(target);
if (frame == null) {
throw new SeleniumError("Not found: " + target);
}
// now, did they give us a frame or a frame ELEMENT?
+ var match = false;
if (frame.contentWindow) {
// this must be a frame element
- this.currentWindow = frame.contentWindow;
- } else if (frame.document) {
+ if (browserVersion.isHTA) {
+ // stupid HTA bug; can't get in the front door
+ target = frame.contentWindow.name;
+ } else {
+ this.currentWindow = frame.contentWindow;
+ this.isSubFrameSelected = true;
+ match = true;
+ }
+ } else if (frame.document && frame.location) {
// must be an actual window frame
this.currentWindow = frame;
- } else {
- // neither
- throw new SeleniumError("Not a frame: " + target);
+ this.isSubFrameSelected = true;
+ match = true;
+ }
+
+ if (!match) {
+ // neither, let's loop through the frame names
+ var win = this.getCurrentWindow();
+
+ if (win && win.frames && win.frames.length) {
+ for (var i = 0; i < win.frames.length; i++) {
+ if (win.frames[i].name == target) {
+ this.currentWindow = win.frames[i];
+ this.isSubFrameSelected = true;
+ match = true;
+ break;
+ }
+ }
+ }
+ if (!match) {
+ throw new SeleniumError("Not a frame: " + target);
+ }
}
}
- this.currentPage = null;
+ // modifies the window
+ this.getCurrentWindow();
};
BrowserBot.prototype.openLocation = function(target) {
// We're moving to a new page - clear the current one
var win = this.getCurrentWindow();
LOG.debug("openLocation newPageLoaded = false");
- this.currentPage = null;
this.newPageLoaded = false;
this.setOpenLocation(win, target);
};
+BrowserBot.prototype.openWindow = function(url, windowID) {
+ if (url != "") {
+ url = absolutify(url, this.baseUrl);
+ }
+ if (browserVersion.isHTA) {
+ // in HTA mode, calling .open on the window interprets the url relative to that window
+ // we need to absolute-ize the URL to make it consistent
+ var child = this.getCurrentWindow().open(url, windowID);
+ selenium.browserbot.openedWindows[windowID] = child;
+ } else {
+ this.getCurrentWindow().open(url, windowID);
+ }
+};
+
BrowserBot.prototype.setIFrameLocation = function(iframe, location) {
iframe.src = location;
};
BrowserBot.prototype.setOpenLocation = function(win, loc) {
-
- // is there a Permission Denied risk here? setting a timeout breaks Firefox
- //win.setTimeout(function() { win.location.href = loc; }, 0);
- win.location.href = loc;
+ loc = absolutify(loc, this.baseUrl);
+ if (browserVersion.isHTA) {
+ var oldHref = win.location.href;
+ win.location.href = loc;
+ var marker = null;
+ try {
+ marker = this.isPollingForLoad(win);
+ if (marker && win.location[marker]) {
+ win.location[marker] = false;
+ }
+ } catch (e) {} // DGF don't know why, but this often fails
+ } else {
+ win.location.href = loc;
+ }
};
BrowserBot.prototype.getCurrentPage = function() {
- if (this.currentPage == null) {
- var testWindow = this.getCurrentWindow();
- this.currentPage = PageBot.createForWindow(this);
- this.newPageLoaded = false;
- }
-
- return this.currentPage;
+ return this;
};
BrowserBot.prototype.modifyWindowToRecordPopUpDialogs = function(windowToModify, browserBot) {
@@ -283,11 +438,45 @@ BrowserBot.prototype.modifyWindowToRecordPopUpDialogs = function(windowToModify,
// Keep a reference to all popup windows by name
// note that in IE the "windowName" argument must be a valid javascript identifier, it seems.
var originalOpen = windowToModify.open;
- windowToModify.open = function(url, windowName, windowFeatures, replaceFlag) {
- var openedWindow = originalOpen(url, windowName, windowFeatures, replaceFlag);
+ var originalOpenReference;
+ if (browserVersion.isHTA) {
+ originalOpenReference = 'selenium_originalOpen' + new Date().getTime();
+ windowToModify[originalOpenReference] = windowToModify.open;
+ }
+
+ var isHTA = browserVersion.isHTA;
+
+ var newOpen = function(url, windowName, windowFeatures, replaceFlag) {
+ var myOriginalOpen = originalOpen;
+ if (isHTA) {
+ myOriginalOpen = this[originalOpenReference];
+ }
+ var openedWindow = myOriginalOpen(url, windowName, windowFeatures, replaceFlag);
+ LOG.debug("window.open call intercepted; window ID (which you can use with selectWindow()) is \"" + windowName + "\"");
+ if (windowName!=null) {
+ openedWindow["seleniumWindowName"] = windowName;
+ }
selenium.browserbot.openedWindows[windowName] = openedWindow;
return openedWindow;
};
+
+ if (browserVersion.isHTA) {
+ originalOpenReference = 'selenium_originalOpen' + new Date().getTime();
+ newOpenReference = 'selenium_newOpen' + new Date().getTime();
+ var setOriginalRef = "this['" + originalOpenReference + "'] = this.open;";
+
+ if (windowToModify.eval) {
+ windowToModify.eval(setOriginalRef);
+ windowToModify.open = newOpen;
+ } else {
+ // DGF why can't I eval here? Seems like I'm querying the window at a bad time, maybe?
+ setOriginalRef += "this.open = this['" + newOpenReference + "'];";
+ windowToModify[newOpenReference] = newOpen;
+ windowToModify.setTimeout(setOriginalRef, 0);
+ }
+ } else {
+ windowToModify.open = newOpen;
+ }
};
/**
@@ -311,27 +500,51 @@ BrowserBot.prototype.modifySeparateTestWindowToDetectPageLoads = function(window
}
var marker = 'selenium' + new Date().getTime();
- LOG.debug("Starting pollForLoad (" + marker + "): " + windowObject.document.location);
+ LOG.debug("Starting pollForLoad (" + marker + "): " + windowObject.location);
this.pollingForLoad[marker] = true;
// if this is a frame, add a load listener, otherwise, attach a poller
- if (this._getFrameElement(windowObject)) {
+ var frameElement = this._getFrameElement(windowObject);
+ // DGF HTA mode can't attach load listeners to subframes (yuk!)
+ var htaSubFrame = this._isHTASubFrame(windowObject);
+ if (frameElement && !htaSubFrame) {
LOG.debug("modifySeparateTestWindowToDetectPageLoads: this window is a frame; attaching a load listener");
- addLoadListener(windowObject.frameElement, this.recordPageLoad);
- windowObject.frameElement[marker] = true;
- windowObject.frameElement[this.uniqueId] = marker;
+ addLoadListener(frameElement, this.recordPageLoad);
+ frameElement[marker] = true;
+ frameElement[this.uniqueId] = marker;
} else {
- windowObject.document.location[marker] = true;
+ windowObject.location[marker] = true;
windowObject[this.uniqueId] = marker;
this.pollForLoad(this.recordPageLoad, windowObject, windowObject.document, windowObject.location, windowObject.location.href, marker);
}
};
+BrowserBot.prototype._isHTASubFrame = function(win) {
+ if (!browserVersion.isHTA) return false;
+ // DGF this is wrong! what if "win" isn't the selected window?
+ return this.isSubFrameSelected;
+}
+
BrowserBot.prototype._getFrameElement = function(win) {
var frameElement = null;
+ var caught;
try {
frameElement = win.frameElement;
} catch (e) {
- } // on IE, checking frameElement on a pop-up results in a "No such interface supported" exception
+ caught = true;
+ }
+ if (caught) {
+ // on IE, checking frameElement in a pop-up results in a "No such interface supported" exception
+ // but it might have a frame element anyway!
+ var parentContainsIdenticallyNamedFrame = false;
+ try {
+ parentContainsIdenticallyNamedFrame = win.parent.frames[win.name];
+ } catch (e) {} // this may fail if access is denied to the parent; in that case, assume it's not a pop-up
+
+ if (parentContainsIdenticallyNamedFrame) {
+ // it can't be a coincidence that the parent has a frame with the same name as myself!
+ return BrowserBot.prototype.locateElementByName(win.name, win.parent.document, win.parent);
+ }
+ }
return frameElement;
}
@@ -342,18 +555,12 @@ BrowserBot.prototype._getFrameElement = function(win) {
*/
BrowserBot.prototype.pollForLoad = function(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker) {
LOG.debug("pollForLoad original (" + marker + "): " + originalHref);
-
try {
if (this._windowClosed(windowObject)) {
LOG.debug("pollForLoad WINDOW CLOSED (" + marker + ")");
delete this.pollingForLoad[marker];
return;
}
- // todo: Change this!!!
- // under multi-window layout, buttonWindow should be TestRunner window
- // but only after the _windowClosed checking, we can ensure that this.topWindow exists
- // then we can assign the TestRunner window to buttonWindow
- this.buttonWindow = windowObject.opener;
var isSamePage = this._isSamePage(windowObject, originalDocument, originalLocation, originalHref, marker);
var rs = this.getReadyState(windowObject, windowObject.document);
@@ -369,13 +576,18 @@ BrowserBot.prototype.pollForLoad = function(loadFunction, windowObject, original
this.modifySeparateTestWindowToDetectPageLoads(windowObject);
}
newMarker = this.isPollingForLoad(windowObject);
+ var currentlySelectedWindow;
+ var currentlySelectedWindowMarker;
+ currentlySelectedWindow =this.getCurrentWindow(true);
+ currentlySelectedWindowMarker = currentlySelectedWindow[this.uniqueId];
+
LOG.debug("pollForLoad (" + marker + ") restarting " + newMarker);
if (/(TestRunner-splash|Blank)\.html\?start=true$/.test(currentHref)) {
LOG.debug("pollForLoad Oh, it's just the starting page. Never mind!");
- } else if (this.currentWindow[this.uniqueId] == newMarker) {
- loadFunction();
+ } else if (currentlySelectedWindowMarker == newMarker) {
+ loadFunction(currentlySelectedWindow);
} else {
- LOG.debug("pollForLoad page load detected in non-current window; ignoring");
+ LOG.debug("pollForLoad page load detected in non-current window; ignoring (currentlySelected="+currentlySelectedWindowMarker+", detection in "+newMarker+")");
}
return;
}
@@ -396,6 +608,19 @@ BrowserBot.prototype._isSamePage = function(windowObject, originalDocument, orig
var sameDoc = this._isSameDocument(originalDocument, currentDocument);
var sameLoc = (originalLocation === currentLocation);
+
+ // hash marks don't meant the page has loaded, so we need to strip them off if they exist...
+ var currentHash = currentHref.indexOf('#');
+ if (currentHash > 0) {
+ currentHref = currentHref.substring(0, currentHash);
+ }
+ var originalHash = originalHref.indexOf('#');
+ if (originalHash > 0) {
+ originalHref = originalHref.substring(0, originalHash);
+ }
+ LOG.debug("_isSamePage: currentHref: " + currentHref);
+ LOG.debug("_isSamePage: originalHref: " + originalHref);
+
var sameHref = (originalHref === currentHref);
var markedLoc = currentLocation[marker];
@@ -403,6 +628,13 @@ BrowserBot.prototype._isSamePage = function(windowObject, originalDocument, orig
// the mark disappears too early on these browsers
markedLoc = true;
}
+
+ // since this is some _very_ important logic, especially for PI and multiWindow mode, we should log all these out
+ LOG.debug("_isSamePage: sameDoc: " + sameDoc);
+ LOG.debug("_isSamePage: sameLoc: " + sameLoc);
+ LOG.debug("_isSamePage: sameHref: " + sameHref);
+ LOG.debug("_isSamePage: markedLoc: " + markedLoc);
+
return sameDoc && sameLoc && sameHref && markedLoc
};
@@ -489,17 +721,21 @@ BrowserBot.prototype.reschedulePoller = function(loadFunction, windowObject, ori
};
BrowserBot.prototype.runScheduledPollers = function() {
+ LOG.debug("runScheduledPollers");
var oldPollers = this.windowPollers;
this.windowPollers = new Array();
for (var i = 0; i < oldPollers.length; i++) {
oldPollers[i].call();
}
+ LOG.debug("runScheduledPollers DONE");
};
BrowserBot.prototype.isPollingForLoad = function(win) {
var marker;
- if (this._getFrameElement(win)) {
- marker = win.frameElement[this.uniqueId];
+ var frameElement = this._getFrameElement(win);
+ var htaSubFrame = this._isHTASubFrame(win);
+ if (frameElement && !htaSubFrame) {
+ marker = frameElement[this.uniqueId];
} else {
marker = win[this.uniqueId];
}
@@ -521,9 +757,32 @@ BrowserBot.prototype.getWindowByName = function(windowName, doNotModify) {
if (!targetWindow) {
targetWindow = this.topWindow[windowName];
}
+ if (!targetWindow && windowName == "_blank") {
+ for (var winName in this.openedWindows) {
+ // _blank can match selenium_blank*, if it looks like it's OK (valid href, not closed)
+ if (/^selenium_blank/.test(winName)) {
+ targetWindow = this.openedWindows[winName];
+ var ok;
+ try {
+ if (!this._windowClosed(targetWindow)) {
+ ok = targetWindow.location.href;
+ }
+ } catch (e) {}
+ if (ok) break;
+ }
+ }
+ }
if (!targetWindow) {
throw new SeleniumError("Window does not exist");
}
+ if (browserVersion.isHTA) {
+ try {
+ targetWindow.location.href;
+ } catch (e) {
+ targetWindow = window.open("", targetWindow.name);
+ this.openedWindows[targetWindow.name] = targetWindow;
+ }
+ }
if (!doNotModify) {
this._modifyWindow(targetWindow);
}
@@ -534,210 +793,62 @@ BrowserBot.prototype.getCurrentWindow = function(doNotModify) {
var testWindow = this.currentWindow;
if (!doNotModify) {
this._modifyWindow(testWindow);
+ if (!this.proxyInjectionMode) {
+ // In proxy injection mode, have to avoid logging during getCurrentWindow to avoid an infinite loop
+ LOG.debug("getCurrentWindow newPageLoaded = false");
+ }
+ this.newPageLoaded = false;
}
+ testWindow = this._handleClosedSubFrame(testWindow, doNotModify);
return testWindow;
};
-function MozillaBrowserBot(frame) {
- BrowserBot.call(this, frame);
-}
-MozillaBrowserBot.prototype = new BrowserBot;
-
-function KonquerorBrowserBot(frame) {
- BrowserBot.call(this, frame);
-}
-KonquerorBrowserBot.prototype = new BrowserBot;
-
-KonquerorBrowserBot.prototype.setIFrameLocation = function(iframe, location) {
- // Window doesn't fire onload event when setting src to the current value,
- // so we set it to blank first.
- iframe.src = "about:blank";
- iframe.src = location;
-};
-
-KonquerorBrowserBot.prototype.setOpenLocation = function(win, loc) {
- // Window doesn't fire onload event when setting src to the current value,
- // so we set it to blank first.
- win.location.href = "about:blank";
- win.location.href = loc;
- // force the current polling thread to detect a page load
- var marker = this.isPollingForLoad(win);
- if (marker) {
- delete win.location[marker];
- }
-};
-
-KonquerorBrowserBot.prototype._isSameDocument = function(originalDocument, currentDocument) {
- // under Konqueror, there may be this case:
- // originalDocument and currentDocument are different objects
- // while their location are same.
- if (originalDocument) {
- return originalDocument.location == currentDocument.location
- } else {
- return originalDocument === currentDocument;
- }
-};
-
-function SafariBrowserBot(frame) {
- BrowserBot.call(this, frame);
-}
-SafariBrowserBot.prototype = new BrowserBot;
-
-SafariBrowserBot.prototype.setIFrameLocation = KonquerorBrowserBot.prototype.setIFrameLocation;
-SafariBrowserBot.prototype.setOpenLocation = KonquerorBrowserBot.prototype.setOpenLocation;
-
-
-function OperaBrowserBot(frame) {
- BrowserBot.call(this, frame);
-}
-OperaBrowserBot.prototype = new BrowserBot;
-OperaBrowserBot.prototype.setIFrameLocation = function(iframe, location) {
- if (iframe.src == location) {
- iframe.src = location + '?reload';
- } else {
- iframe.src = location;
+BrowserBot.prototype._handleClosedSubFrame = function(testWindow, doNotModify) {
+ if (this.proxyInjectionMode) {
+ return testWindow;
}
-}
-
-function IEBrowserBot(frame) {
- BrowserBot.call(this, frame);
-}
-IEBrowserBot.prototype = new BrowserBot;
-
-IEBrowserBot.prototype.modifyWindowToRecordPopUpDialogs = function(windowToModify, browserBot) {
- BrowserBot.prototype.modifyWindowToRecordPopUpDialogs(windowToModify, browserBot);
-
- // we will call the previous version of this method from within our own interception
- oldShowModalDialog = windowToModify.showModalDialog;
-
- windowToModify.showModalDialog = function(url, args, features) {
- // Get relative directory to where TestRunner.html lives
- // A risky assumption is that the user's TestRunner is named TestRunner.html
- var doc_location = document.location.toString();
- var end_of_base_ref = doc_location.indexOf('TestRunner.html');
- var base_ref = doc_location.substring(0, end_of_base_ref);
-
- var fullURL = base_ref + "TestRunner.html?singletest=" + escape(browserBot.modalDialogTest) + "&autoURL=" + escape(url) + "&runInterval=" + runOptions.runInterval;
- browserBot.modalDialogTest = null;
-
- var returnValue = oldShowModalDialog(fullURL, args, features);
- return returnValue;
- };
-};
-
-IEBrowserBot.prototype.modifySeparateTestWindowToDetectPageLoads = function(windowObject) {
- this.pageUnloading = false;
- this.permDeniedCount = 0;
- var self = this;
- var pageUnloadDetector = function() {
- self.pageUnloading = true;
- };
- windowObject.attachEvent("onbeforeunload", pageUnloadDetector);
- BrowserBot.prototype.modifySeparateTestWindowToDetectPageLoads.call(this, windowObject);
-};
-
-IEBrowserBot.prototype.pollForLoad = function(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker) {
- BrowserBot.prototype.pollForLoad.call(this, loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
- if (this.pageLoadError) {
- if (this.pageUnloading) {
- var self = this;
- LOG.warn("pollForLoad UNLOADING (" + marker + "): caught exception while firing events on unloading page: " + this.pageLoadError.message);
- this.reschedulePoller(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
- this.pageLoadError = null;
- return;
- } else if (((this.pageLoadError.message == "Permission denied") || (/^Access is denied/.test(this.pageLoadError.message)))
- && this.permDeniedCount++ < 4) {
- var self = this;
- LOG.warn("pollForLoad (" + marker + "): " + this.pageLoadError.message + " (" + this.permDeniedCount + "), waiting to see if it goes away");
- this.reschedulePoller(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
- this.pageLoadError = null;
- return;
- }
- //handy for debugging!
- //throw this.pageLoadError;
- }
-};
-
-IEBrowserBot.prototype._windowClosed = function(win) {
- try {
- var c = win.closed;
- // frame windows claim to be non-closed when their parents are closed
- // but you can't access their document objects in that case
- if (!c) {
- try {
- win.document;
- } catch (de) {
- if (de.message == "Permission denied") {
- // the window is probably unloading, which means it's probably not closed yet
- return false;
- }
- else if (/^Access is denied/.test(de.message)) {
- // rare variation on "Permission denied"?
- LOG.debug("IEBrowserBot.windowClosed: got " + de.message + " (this.pageUnloading=" + this.pageUnloading + "); assuming window is unloading, probably not closed yet");
- return false;
- } else {
- // this is probably one of those frame window situations
- LOG.debug("IEBrowserBot.windowClosed: couldn't read win.document, assume closed: " + de.message + " (this.pageUnloading=" + this.pageUnloading + ")");
- return true;
+
+ if (this.isSubFrameSelected) {
+ var missing = true;
+ if (testWindow.parent && testWindow.parent.frames && testWindow.parent.frames.length) {
+ for (var i = 0; i < testWindow.parent.frames.length; i++) {
+ if (testWindow.parent.frames[i] == testWindow) {
+ missing = false;
+ break;
}
}
}
- if (c == null) {
- LOG.debug("IEBrowserBot.windowClosed: win.closed was null, assuming closed");
- return true;
- }
- return c;
- } catch (e) {
- // Got an exception trying to read win.closed; we'll have to take a guess!
- if (browserVersion.isHTA) {
- if (e.message == "Permission denied") {
- // the window is probably unloading, which means it's probably not closed yet
- return false;
- } else {
- // there's a good chance that we've lost contact with the window object if it is closed
- return true;
- }
- } else {
- // the window is probably unloading, which means it's probably not closed yet
- return false;
+ if (missing) {
+ LOG.warn("Current subframe appears to have closed; selecting top frame");
+ this.selectFrame("relative=top");
+ return this.getCurrentWindow(doNotModify);
}
+ } else if (this._windowClosed(testWindow)) {
+ var closedError = new SeleniumError("Current window or frame is closed!");
+ closedError.windowClosed = true;
+ throw closedError;
}
+ return testWindow;
};
-SafariBrowserBot.prototype.modifyWindowToRecordPopUpDialogs = function(windowToModify, browserBot) {
- BrowserBot.prototype.modifyWindowToRecordPopUpDialogs(windowToModify, browserBot);
-
- var originalOpen = windowToModify.open;
- /*
- * Safari seems to be broken, so that when we manually trigger the onclick method
- * of a button/href, any window.open calls aren't resolved relative to the app location.
- * So here we replace the open() method with one that does resolve the url correctly.
- */
- windowToModify.open = function(url, windowName, windowFeatures, replaceFlag) {
-
- if (url.startsWith("http://") || url.startsWith("https://") || url.startsWith("/")) {
- return originalOpen(url, windowName, windowFeatures, replaceFlag);
- }
-
- // Reduce the current path to the directory
- var currentPath = windowToModify.location.pathname || "/";
- currentPath = currentPath.replace(/\/[^\/]*$/, "/");
-
- // Remove any leading "./" from the new url.
- url = url.replace(/^\.\//, "");
+BrowserBot.prototype.highlight = function (element, force) {
+ if (force || this.shouldHighlightLocatedElement) {
+ try {
+ highlight(element);
+ } catch (e) {} // DGF element highlighting is low-priority and possibly dangerous
+ }
+ return element;
+}
- newUrl = currentPath + url;
+BrowserBot.prototype.setShouldHighlightElement = function (shouldHighlight) {
+ this.shouldHighlightLocatedElement = shouldHighlight;
+}
- return originalOpen(newUrl, windowName, windowFeatures, replaceFlag);
- };
-};
+/*****************************************************************/
+/* BROWSER-SPECIFIC FUNCTIONS ONLY AFTER THIS LINE */
-var PageBot = function(browserbot) {
- this.browserbot = browserbot;
- this._registerAllLocatorFunctions();
-};
-PageBot.prototype._registerAllLocatorFunctions = function() {
+BrowserBot.prototype._registerAllLocatorFunctions = function() {
// TODO - don't do this in the constructor - only needed once ever
this.locationStrategies = {};
for (var functionName in this) {
@@ -779,15 +890,11 @@ PageBot.prototype._registerAllLocatorFunctions = function() {
};
}
-PageBot.prototype.getDocument = function() {
+BrowserBot.prototype.getDocument = function() {
return this.getCurrentWindow().document;
}
-PageBot.prototype.getCurrentWindow = function() {
- return this.browserbot.getCurrentWindow();
-}
-
-PageBot.prototype.getTitle = function() {
+BrowserBot.prototype.getTitle = function() {
var t = this.getDocument().title;
if (typeof(t) == "string") {
t = t.trim();
@@ -795,55 +902,30 @@ PageBot.prototype.getTitle = function() {
return t;
}
-// todo: this is a bad name ... we're not passing a window in
-PageBot.createForWindow = function(browserbot) {
- if (browserVersion.isIE) {
- return new IEPageBot(browserbot);
- }
- else if (browserVersion.isKonqueror) {
- return new KonquerorPageBot(browserbot);
- }
- else if (browserVersion.isSafari) {
- return new SafariPageBot(browserbot);
- }
- else if (browserVersion.isOpera) {
- return new OperaPageBot(browserbot);
+/*
+ * Finds an element recursively in frames and nested frames
+ * in the specified document, using various lookup protocols
+ */
+BrowserBot.prototype.findElementRecursive = function(locatorType, locatorString, inDocument, inWindow) {
+
+ var element = this.findElementBy(locatorType, locatorString, inDocument, inWindow);
+ if (element != null) {
+ return element;
}
- else {
- // Use mozilla by default
- return new MozillaPageBot(browserbot);
+
+ for (var i = 0; i < inWindow.frames.length; i++) {
+ element = this.findElementRecursive(locatorType, locatorString, inWindow.frames[i].document, inWindow.frames[i]);
+
+ if (element != null) {
+ return element;
+ }
}
};
-var MozillaPageBot = function(browserbot) {
- PageBot.call(this, browserbot);
-};
-MozillaPageBot.prototype = new PageBot();
-
-var KonquerorPageBot = function(browserbot) {
- PageBot.call(this, browserbot);
-};
-KonquerorPageBot.prototype = new PageBot();
-
-var SafariPageBot = function(browserbot) {
- PageBot.call(this, browserbot);
-};
-SafariPageBot.prototype = new PageBot();
-
-var IEPageBot = function(browserbot) {
- PageBot.call(this, browserbot);
-};
-IEPageBot.prototype = new PageBot();
-
-var OperaPageBot = function(browserbot) {
- PageBot.call(this, browserbot);
-};
-OperaPageBot.prototype = new PageBot();
-
/*
* Finds an element on the current page, using various lookup protocols
*/
-PageBot.prototype.findElement = function(locator) {
+BrowserBot.prototype.findElement = function(locator) {
var locatorType = 'implicit';
var locatorString = locator;
@@ -853,57 +935,31 @@ PageBot.prototype.findElement = function(locator) {
locatorType = result[1].toLowerCase();
locatorString = result[2];
}
-
- var element = this.findElementBy(locatorType, locatorString, this.getDocument(), this.getCurrentWindow());
+
+ var element = this.findElementRecursive(locatorType, locatorString, this.getDocument(), this.getCurrentWindow())
+
if (element != null) {
- return this.highlight(element);
- }
- for (var i = 0; i < this.getCurrentWindow().frames.length; i++) {
- element = this.findElementBy(locatorType, locatorString, this.getCurrentWindow().frames[i].document, this.getCurrentWindow().frames[i]);
- if (element != null) {
- return this.highlight(element);
- }
+ return this.browserbot.highlight(element);
}
// Element was not found by any locator function.
throw new SeleniumError("Element " + locator + " not found");
};
-PageBot.prototype.highlight = function (element) {
- if (shouldHighlightLocatedElement) {
- Effect.highlight(element);
- }
- return element;
-}
-
-// as a static variable.
-var shouldHighlightLocatedElement = false;
-
-PageBot.prototype.setHighlightElement = function (shouldHighlight) {
- shouldHighlightLocatedElement = shouldHighlight;
-}
-
/**
* In non-IE browsers, getElementById() does not search by name. Instead, we
* we search separately by id and name.
*/
-PageBot.prototype.locateElementByIdentifier = function(identifier, inDocument, inWindow) {
- return PageBot.prototype.locateElementById(identifier, inDocument, inWindow)
- || PageBot.prototype.locateElementByName(identifier, inDocument, inWindow)
+BrowserBot.prototype.locateElementByIdentifier = function(identifier, inDocument, inWindow) {
+ return BrowserBot.prototype.locateElementById(identifier, inDocument, inWindow)
+ || BrowserBot.prototype.locateElementByName(identifier, inDocument, inWindow)
|| null;
};
/**
- * In IE, getElementById() also searches by name - this is an optimisation for IE.
- */
-IEPageBot.prototype.locateElementByIdentifer = function(identifier, inDocument, inWindow) {
- return inDocument.getElementById(identifier);
-};
-
-/**
* Find the element with id - can't rely on getElementById, coz it returns by name as well in IE..
*/
-PageBot.prototype.locateElementById = function(identifier, inDocument, inWindow) {
+BrowserBot.prototype.locateElementById = function(identifier, inDocument, inWindow) {
var element = inDocument.getElementById(identifier);
if (element && element.id === identifier) {
return element;
@@ -917,7 +973,7 @@ PageBot.prototype.locateElementById = function(identifier, inDocument, inWindow)
* Find an element by name, refined by (optional) element-filter
* expressions.
*/
-PageBot.prototype.locateElementByName = function(locator, document, inWindow) {
+BrowserBot.prototype.locateElementByName = function(locator, document, inWindow) {
var elements = document.getElementsByTagName("*");
var filters = locator.split(' ');
@@ -937,15 +993,12 @@ PageBot.prototype.locateElementByName = function(locator, document, inWindow) {
/**
* Finds an element using by evaluating the specfied string.
*/
-PageBot.prototype.locateElementByDomTraversal = function(domTraversal, inDocument, inWindow) {
+BrowserBot.prototype.locateElementByDomTraversal = function(domTraversal, document, window) {
+ var browserbot = this.browserbot;
var element = null;
try {
- if (browserVersion.isOpera) {
- element = inWindow.eval(domTraversal);
- } else {
- element = eval("inWindow." + domTraversal);
- }
+ element = eval(domTraversal);
} catch (e) {
e.isSeleniumError = true;
throw e;
@@ -957,13 +1010,13 @@ PageBot.prototype.locateElementByDomTraversal = function(domTraversal, inDocumen
return element;
};
-PageBot.prototype.locateElementByDomTraversal.prefix = "dom";
+BrowserBot.prototype.locateElementByDomTraversal.prefix = "dom";
/**
* Finds an element identified by the xpath expression. Expressions _must_
* begin with "//".
*/
-PageBot.prototype.locateElementByXPath = function(xpath, inDocument, inWindow) {
+BrowserBot.prototype.locateElementByXPath = function(xpath, inDocument, inWindow) {
// Trim any trailing "/": not valid xpath, and remains from attribute
// locator.
@@ -982,12 +1035,21 @@ PageBot.prototype.locateElementByXPath = function(xpath, inDocument, inWindow) {
// Handle //tag[@attr='value']
var match = xpath.match(/^\/\/(\w+|\*)\[@(\w+)=('([^\']+)'|"([^\"]+)")\]$/);
if (match) {
- return this._findElementByTagNameAndAttributeValue(
+ // We don't return the value without checking if it is null first.
+ // This is beacuse in some rare cases, this shortcut actually WONT work
+ // but that the full XPath WILL. A known case, for example, is in IE
+ // when the attribute is onclick/onblur/onsubmit/etc. Due to a bug in IE
+ // this shortcut won't work because the actual function is returned
+ // by getAttribute() rather than the text of the attribute.
+ var val = this._findElementByTagNameAndAttributeValue(
inDocument,
match[1].toUpperCase(),
match[2].toLowerCase(),
match[3].slice(1, -1)
);
+ if (val) {
+ return val;
+ }
}
// Handle //tag[text()='value']
@@ -1003,7 +1065,7 @@ PageBot.prototype.locateElementByXPath = function(xpath, inDocument, inWindow) {
return this._findElementUsingFullXPath(xpath, inDocument);
};
-PageBot.prototype._findElementByTagNameAndAttributeValue = function(
+BrowserBot.prototype._findElementByTagNameAndAttributeValue = function(
inDocument, tagName, attributeName, attributeValue
) {
if (browserVersion.isIE && attributeName == "class") {
@@ -1019,7 +1081,7 @@ PageBot.prototype._findElementByTagNameAndAttributeValue = function(
return null;
};
-PageBot.prototype._findElementByTagNameAndText = function(
+BrowserBot.prototype._findElementByTagNameAndText = function(
inDocument, tagName, text
) {
var elements = inDocument.getElementsByTagName(tagName);
@@ -1031,7 +1093,7 @@ PageBot.prototype._findElementByTagNameAndText = function(
return null;
};
-PageBot.prototype._namespaceResolver = function(prefix) {
+BrowserBot.prototype._namespaceResolver = function(prefix) {
if (prefix == 'html' || prefix == 'xhtml' || prefix == 'x') {
return 'http://www.w3.org/1999/xhtml';
} else if (prefix == 'mathml') {
@@ -1041,7 +1103,7 @@ PageBot.prototype._namespaceResolver = function(prefix) {
}
}
-PageBot.prototype._findElementUsingFullXPath = function(xpath, inDocument, inWindow) {
+BrowserBot.prototype._findElementUsingFullXPath = function(xpath, inDocument, inWindow) {
// HUGE hack - remove namespace from xpath for IE
if (browserVersion.isIE) {
xpath = xpath.replace(/x:/g, '')
@@ -1066,7 +1128,7 @@ PageBot.prototype._findElementUsingFullXPath = function(xpath, inDocument, inWin
* Finds a link element with text matching the expression supplied. Expressions must
* begin with "link:".
*/
-PageBot.prototype.locateElementByLinkText = function(linkText, inDocument, inWindow) {
+BrowserBot.prototype.locateElementByLinkText = function(linkText, inDocument, inWindow) {
var links = inDocument.getElementsByTagName('a');
for (var i = 0; i < links.length; i++) {
var element = links[i];
@@ -1076,13 +1138,13 @@ PageBot.prototype.locateElementByLinkText = function(linkText, inDocument, inWin
}
return null;
};
-PageBot.prototype.locateElementByLinkText.prefix = "link";
+BrowserBot.prototype.locateElementByLinkText.prefix = "link";
/**
* Returns an attribute based on an attribute locator. This is made up of an element locator
* suffixed with @attribute-name.
*/
-PageBot.prototype.findAttribute = function(locator) {
+BrowserBot.prototype.findAttribute = function(locator) {
// Split into locator + attributeName
var attributePos = locator.lastIndexOf("@");
var elementLocator = locator.slice(0, attributePos);
@@ -1105,7 +1167,7 @@ PageBot.prototype.findAttribute = function(locator) {
/*
* Select the specified option and trigger the relevant events of the element.
*/
-PageBot.prototype.selectOption = function(element, optionToSelect) {
+BrowserBot.prototype.selectOption = function(element, optionToSelect) {
triggerEvent(element, 'focus', false);
var changed = false;
for (var i = 0; i < element.options.length; i++) {
@@ -1128,7 +1190,7 @@ PageBot.prototype.selectOption = function(element, optionToSelect) {
/*
* Select the specified option and trigger the relevant events of the element.
*/
-PageBot.prototype.addSelection = function(element, option) {
+BrowserBot.prototype.addSelection = function(element, option) {
this.checkMultiselect(element);
triggerEvent(element, 'focus', false);
if (!option.selected) {
@@ -1140,7 +1202,7 @@ PageBot.prototype.addSelection = function(element, option) {
/*
* Select the specified option and trigger the relevant events of the element.
*/
-PageBot.prototype.removeSelection = function(element, option) {
+BrowserBot.prototype.removeSelection = function(element, option) {
this.checkMultiselect(element);
triggerEvent(element, 'focus', false);
if (option.selected) {
@@ -1149,7 +1211,7 @@ PageBot.prototype.removeSelection = function(element, option) {
}
};
-PageBot.prototype.checkMultiselect = function(element) {
+BrowserBot.prototype.checkMultiselect = function(element) {
if (!element.multiple)
{
throw new SeleniumError("Not a multi-select");
@@ -1157,7 +1219,7 @@ PageBot.prototype.checkMultiselect = function(element) {
};
-PageBot.prototype.replaceText = function(element, stringValue) {
+BrowserBot.prototype.replaceText = function(element, stringValue) {
triggerEvent(element, 'focus', false);
triggerEvent(element, 'select', true);
var maxLengthAttr = element.getAttribute("maxLength");
@@ -1170,42 +1232,83 @@ PageBot.prototype.replaceText = function(element, stringValue) {
LOG.warn("AFTER")
}
}
- element.value = actualValue;
+
+ if (getTagName(element) == "body") {
+ if (element.ownerDocument && element.ownerDocument.designMode) {
+ var designMode = new String(element.ownerDocument.designMode).toLowerCase();
+ if (designMode = "on") {
+ // this must be a rich text control!
+ element.innerHTML = actualValue;
+ }
+ }
+ } else {
+ element.value = actualValue;
+ }
// DGF this used to be skipped in chrome URLs, but no longer. Is xpcnativewrappers to blame?
- triggerEvent(element, 'change', true);
+ try {
+ triggerEvent(element, 'change', true);
+ } catch (e) {}
};
-MozillaPageBot.prototype.clickElement = function(element, clientX, clientY) {
-
- triggerEvent(element, 'focus', false);
-
- // Add an event listener that detects if the default action has been prevented.
- // (This is caused by a javascript onclick handler returning false)
- var preventDefault = false;
-
- element.addEventListener("click", function(evt) {
- preventDefault = evt.getPreventDefault();
- }, false);
-
- // Trigger the click event.
- triggerMouseEvent(element, 'click', true, clientX, clientY);
-
- // Perform the link action if preventDefault was set.
- // In chrome URL, the link action is already executed by triggerMouseEvent.
- if (!browserVersion.isChrome && !preventDefault) {
- var targetWindow = this.browserbot._getTargetWindow(element);
- if (element.href) {
- targetWindow.location.href = element.href;
+BrowserBot.prototype.submit = function(formElement) {
+ var actuallySubmit = true;
+ this._modifyElementTarget(formElement);
+ if (formElement.onsubmit) {
+ if (browserVersion.isHTA) {
+ // run the code in the correct window so alerts are handled correctly even in HTA mode
+ var win = this.browserbot.getCurrentWindow();
+ var now = new Date().getTime();
+ var marker = 'marker' + now;
+ win[marker] = formElement;
+ win.setTimeout("var actuallySubmit = "+marker+".onsubmit();" +
+ "if (actuallySubmit) { " +
+ marker+".submit(); " +
+ "if ("+marker+".target && !/^_/.test("+marker+".target)) {"+
+ "window.open('', "+marker+".target);"+
+ "}"+
+ "};"+
+ marker+"=null", 0);
+ // pause for up to 2s while this command runs
+ var terminationCondition = function () {
+ return !win[marker];
+ }
+ return Selenium.decorateFunctionWithTimeout(terminationCondition, 2000);
} else {
- this.browserbot._handleClickingImagesInsideLinks(targetWindow, element);
+ actuallySubmit = formElement.onsubmit();
+ if (actuallySubmit) {
+ formElement.submit();
+ if (formElement.target && !/^_/.test(formElement.target)) {
+ this.browserbot.openWindow('', formElement.target);
+ }
+ }
}
+ } else {
+ formElement.submit();
}
+}
- if (this._windowClosed()) {
- return;
+BrowserBot.prototype.clickElement = function(element, clientX, clientY) {
+ this._fireEventOnElement("click", element, clientX, clientY);
+};
+
+BrowserBot.prototype.doubleClickElement = function(element, clientX, clientY) {
+ this._fireEventOnElement("dblclick", element, clientX, clientY);
+};
+
+BrowserBot.prototype._modifyElementTarget = function(element) {
+ if (element.target) {
+ if (element.target == "_blank" || /^selenium_blank/.test(element.target) ) {
+ var tagName = getTagName(element);
+ if (tagName == "a" || tagName == "form") {
+ var newTarget = "selenium_blank" + Math.round(100000 * Math.random());
+ LOG.warn("Link has target '_blank', which is not supported in Selenium! Randomizing target to be: " + newTarget);
+ this.browserbot.openWindow('', newTarget);
+ element.target = newTarget;
+ }
+ }
}
+}
-};
BrowserBot.prototype._handleClickingImagesInsideLinks = function(targetWindow, element) {
if (element.parentNode && element.parentNode.href) {
@@ -1214,126 +1317,38 @@ BrowserBot.prototype._handleClickingImagesInsideLinks = function(targetWindow, e
}
BrowserBot.prototype._getTargetWindow = function(element) {
- var targetWindow = this.getCurrentWindow();
+ var targetWindow = element.ownerDocument.defaultView;
if (element.target) {
- var frame = this._getFrameFromGlobal(element.target);
- targetWindow = frame.contentWindow;
+ targetWindow = this._getFrameFromGlobal(element.target);
}
return targetWindow;
}
BrowserBot.prototype._getFrameFromGlobal = function(target) {
- pagebot = PageBot.createForWindow(this);
- return pagebot.findElementBy("implicit", target, this.topWindow.document, this.topWindow);
+
+ if (target == "_top") {
+ return this.topFrame;
+ } else if (target == "_parent") {
+ return this.getCurrentWindow().parent;
+ } else if (target == "_blank") {
+ // TODO should this set cleverer window defaults?
+ return this.getCurrentWindow().open('', '_blank');
+ }
+ var frameElement = this.findElementBy("implicit", target, this.topFrame.document, this.topFrame);
+ if (frameElement) {
+ return frameElement.contentWindow;
+ }
+ var win = this.getWindowByName(target);
+ if (win) return win;
+ return this.getCurrentWindow().open('', target);
}
-OperaPageBot.prototype.clickElement = function(element, clientX, clientY) {
-
- triggerEvent(element, 'focus', false);
-
- // Trigger the click event.
- triggerMouseEvent(element, 'click', true, clientX, clientY);
-
- if (this._windowClosed()) {
- return;
- }
-
-};
-
-
-KonquerorPageBot.prototype.clickElement = function(element, clientX, clientY) {
-
- triggerEvent(element, 'focus', false);
-
- if (element.click) {
- element.click();
- }
- else {
- triggerMouseEvent(element, 'click', true, clientX, clientY);
- }
-
- if (this._windowClosed()) {
- return;
- }
-
-};
-
-SafariPageBot.prototype.clickElement = function(element, clientX, clientY) {
- triggerEvent(element, 'focus', false);
- var wasChecked = element.checked;
- // For form element it is simple.
- if (element.click) {
- element.click();
- }
- // For links and other elements, event emulation is required.
- else {
- var targetWindow = this.browserbot._getTargetWindow(element);
- // todo: what if the target anchor is on another page?
- if (element.href && element.href.indexOf("#") != -1) {
- var b = targetWindow.document.getElementById(element.href.split("#")[1]);
- targetWindow.document.body.scrollTop = b.offsetTop;
- } else {
- triggerMouseEvent(element, 'click', true, clientX, clientY);
- }
-
- }
-
-};
-
-IEPageBot.prototype.clickElement = function(element, clientX, clientY) {
-
- triggerEvent(element, 'focus', false);
-
- var wasChecked = element.checked;
-
- // Set a flag that records if the page will unload - this isn't always accurate, because
- // <a href="javascript:alert('foo'):"> triggers the onbeforeunload event, even thought the page won't unload
- var pageUnloading = false;
- var pageUnloadDetector = function() {
- pageUnloading = true;
- };
- this.getCurrentWindow().attachEvent("onbeforeunload", pageUnloadDetector);
- element.click();
-
-
- // If the page is going to unload - still attempt to fire any subsequent events.
- // However, we can't guarantee that the page won't unload half way through, so we need to handle exceptions.
- try {
- this.getCurrentWindow().detachEvent("onbeforeunload", pageUnloadDetector);
-
- if (this._windowClosed()) {
- return;
- }
-
- // Onchange event is not triggered automatically in IE.
- if (isDefined(element.checked) && wasChecked != element.checked) {
- triggerEvent(element, 'change', true);
- }
-
- }
- catch (e) {
- // If the page is unloading, we may get a "Permission denied" or "Unspecified error".
- // Just ignore it, because the document may have unloaded.
- if (pageUnloading) {
- LOG.logHook = function() {
- };
- LOG.warn("Caught exception when firing events on unloading page: " + e.message);
- return;
- }
- throw e;
- }
-};
-
-PageBot.prototype._windowClosed = function(element) {
- return selenium.browserbot._windowClosed(this.getCurrentWindow());
-};
-
-PageBot.prototype.bodyText = function() {
+BrowserBot.prototype.bodyText = function() {
return getText(this.getDocument().body);
};
-PageBot.prototype.getAllButtons = function() {
+BrowserBot.prototype.getAllButtons = function() {
var elements = this.getDocument().getElementsByTagName('input');
var result = '';
@@ -1349,7 +1364,7 @@ PageBot.prototype.getAllButtons = function() {
};
-PageBot.prototype.getAllFields = function() {
+BrowserBot.prototype.getAllFields = function() {
var elements = this.getDocument().getElementsByTagName('input');
var result = '';
@@ -1364,7 +1379,7 @@ PageBot.prototype.getAllFields = function() {
return result;
};
-PageBot.prototype.getAllLinks = function() {
+BrowserBot.prototype.getAllLinks = function() {
var elements = this.getDocument().getElementsByTagName('a');
var result = '';
@@ -1377,7 +1392,8 @@ PageBot.prototype.getAllLinks = function() {
return result;
};
-PageBot.prototype.setContext = function(strContext, logLevel) {
+BrowserBot.prototype.setContext = function(strContext, logLevel) {
+
//set the current test title
var ctx = document.getElementById("context");
if (ctx != null) {
@@ -1392,31 +1408,31 @@ function isDefined(value) {
return typeof(value) != undefined;
}
-PageBot.prototype.goBack = function() {
+BrowserBot.prototype.goBack = function() {
this.getCurrentWindow().history.back();
};
-PageBot.prototype.goForward = function() {
+BrowserBot.prototype.goForward = function() {
this.getCurrentWindow().history.forward();
};
-PageBot.prototype.close = function() {
- if (browserVersion.isChrome || browserVersion.isSafari) {
+BrowserBot.prototype.close = function() {
+ if (browserVersion.isChrome || browserVersion.isSafari || browserVersion.isOpera) {
this.getCurrentWindow().close();
} else {
this.getCurrentWindow().eval("window.close();");
}
};
-PageBot.prototype.refresh = function() {
+BrowserBot.prototype.refresh = function() {
this.getCurrentWindow().location.reload(true);
};
/**
* Refine a list of elements using a filter.
*/
-PageBot.prototype.selectElementsBy = function(filterType, filter, elements) {
- var filterFunction = PageBot.filterFunctions[filterType];
+BrowserBot.prototype.selectElementsBy = function(filterType, filter, elements) {
+ var filterFunction = BrowserBot.filterFunctions[filterType];
if (! filterFunction) {
throw new SeleniumError("Unrecognised element-filter type: '" + filterType + "'");
}
@@ -1424,9 +1440,9 @@ PageBot.prototype.selectElementsBy = function(filterType, filter, elements) {
return filterFunction(filter, elements);
};
-PageBot.filterFunctions = {};
+BrowserBot.filterFunctions = {};
-PageBot.filterFunctions.name = function(name, elements) {
+BrowserBot.filterFunctions.name = function(name, elements) {
var selectedElements = [];
for (var i = 0; i < elements.length; i++) {
if (elements[i].name === name) {
@@ -1436,7 +1452,7 @@ PageBot.filterFunctions.name = function(name, elements) {
return selectedElements;
};
-PageBot.filterFunctions.value = function(value, elements) {
+BrowserBot.filterFunctions.value = function(value, elements) {
var selectedElements = [];
for (var i = 0; i < elements.length; i++) {
if (elements[i].value === value) {
@@ -1446,7 +1462,7 @@ PageBot.filterFunctions.value = function(value, elements) {
return selectedElements;
};
-PageBot.filterFunctions.index = function(index, elements) {
+BrowserBot.filterFunctions.index = function(index, elements) {
index = Number(index);
if (isNaN(index) || index < 0) {
throw new SeleniumError("Illegal Index: " + index);
@@ -1457,7 +1473,7 @@ PageBot.filterFunctions.index = function(index, elements) {
return [elements[index]];
};
-PageBot.prototype.selectElements = function(filterExpr, elements, defaultFilterType) {
+BrowserBot.prototype.selectElements = function(filterExpr, elements, defaultFilterType) {
var filterType = (defaultFilterType || 'value');
@@ -1474,8 +1490,8 @@ PageBot.prototype.selectElements = function(filterExpr, elements, defaultFilterT
/**
* Find an element by class
*/
-PageBot.prototype.locateElementByClass = function(locator, document) {
- return Element.findFirstMatchingChild(document,
+BrowserBot.prototype.locateElementByClass = function(locator, document) {
+ return elementFindFirstMatchingChild(document,
function(element) {
return element.className == locator
}
@@ -1485,8 +1501,8 @@ PageBot.prototype.locateElementByClass = function(locator, document) {
/**
* Find an element by alt
*/
-PageBot.prototype.locateElementByAlt = function(locator, document) {
- return Element.findFirstMatchingChild(document,
+BrowserBot.prototype.locateElementByAlt = function(locator, document) {
+ return elementFindFirstMatchingChild(document,
function(element) {
return element.alt == locator
}
@@ -1496,9 +1512,435 @@ PageBot.prototype.locateElementByAlt = function(locator, document) {
/**
* Find an element by css selector
*/
-PageBot.prototype.locateElementByCss = function(locator, document) {
+BrowserBot.prototype.locateElementByCss = function(locator, document) {
var elements = cssQuery(locator, document);
if (elements.length != 0)
return elements[0];
return null;
}
+
+
+/*****************************************************************/
+/* BROWSER-SPECIFIC FUNCTIONS ONLY AFTER THIS LINE */
+
+function MozillaBrowserBot(frame) {
+ BrowserBot.call(this, frame);
+}
+objectExtend(MozillaBrowserBot.prototype, BrowserBot.prototype);
+
+function KonquerorBrowserBot(frame) {
+ BrowserBot.call(this, frame);
+}
+objectExtend(KonquerorBrowserBot.prototype, BrowserBot.prototype);
+
+KonquerorBrowserBot.prototype.setIFrameLocation = function(iframe, location) {
+ // Window doesn't fire onload event when setting src to the current value,
+ // so we set it to blank first.
+ iframe.src = "about:blank";
+ iframe.src = location;
+};
+
+KonquerorBrowserBot.prototype.setOpenLocation = function(win, loc) {
+ // Window doesn't fire onload event when setting src to the current value,
+ // so we just refresh in that case instead.
+ loc = absolutify(loc, this.baseUrl);
+ loc = canonicalize(loc);
+ var startLoc = parseUrl(win.location.href);
+ startLoc.hash = null;
+ var startUrl = reassembleLocation(startLoc);
+ LOG.debug("startUrl="+startUrl);
+ LOG.debug("win.location.href="+win.location.href);
+ LOG.debug("loc="+loc);
+ if (startUrl == loc) {
+ LOG.debug("opening exact same location");
+ this.refresh();
+ } else {
+ LOG.debug("locations differ");
+ win.location.href = loc;
+ }
+ // force the current polling thread to detect a page load
+ var marker = this.isPollingForLoad(win);
+ if (marker) {
+ delete win.location[marker];
+ }
+};
+
+KonquerorBrowserBot.prototype._isSameDocument = function(originalDocument, currentDocument) {
+ // under Konqueror, there may be this case:
+ // originalDocument and currentDocument are different objects
+ // while their location are same.
+ if (originalDocument) {
+ return originalDocument.location == currentDocument.location
+ } else {
+ return originalDocument === currentDocument;
+ }
+};
+
+function SafariBrowserBot(frame) {
+ BrowserBot.call(this, frame);
+}
+objectExtend(SafariBrowserBot.prototype, BrowserBot.prototype);
+
+SafariBrowserBot.prototype.setIFrameLocation = KonquerorBrowserBot.prototype.setIFrameLocation;
+SafariBrowserBot.prototype.setOpenLocation = KonquerorBrowserBot.prototype.setOpenLocation;
+
+
+function OperaBrowserBot(frame) {
+ BrowserBot.call(this, frame);
+}
+objectExtend(OperaBrowserBot.prototype, BrowserBot.prototype);
+OperaBrowserBot.prototype.setIFrameLocation = function(iframe, location) {
+ if (iframe.src == location) {
+ iframe.src = location + '?reload';
+ } else {
+ iframe.src = location;
+ }
+}
+
+function IEBrowserBot(frame) {
+ BrowserBot.call(this, frame);
+}
+objectExtend(IEBrowserBot.prototype, BrowserBot.prototype);
+
+IEBrowserBot.prototype._handleClosedSubFrame = function(testWindow, doNotModify) {
+ if (this.proxyInjectionMode) {
+ return testWindow;
+ }
+
+ try {
+ testWindow.location.href;
+ this.permDenied = 0;
+ } catch (e) {
+ this.permDenied++;
+ }
+ if (this._windowClosed(testWindow) || this.permDenied > 4) {
+ if (this.isSubFrameSelected) {
+ LOG.warn("Current subframe appears to have closed; selecting top frame");
+ this.selectFrame("relative=top");
+ return this.getCurrentWindow(doNotModify);
+ } else {
+ var closedError = new SeleniumError("Current window or frame is closed!");
+ closedError.windowClosed = true;
+ throw closedError;
+ }
+ }
+ return testWindow;
+};
+
+IEBrowserBot.prototype.modifyWindowToRecordPopUpDialogs = function(windowToModify, browserBot) {
+ BrowserBot.prototype.modifyWindowToRecordPopUpDialogs(windowToModify, browserBot);
+
+ // we will call the previous version of this method from within our own interception
+ oldShowModalDialog = windowToModify.showModalDialog;
+
+ windowToModify.showModalDialog = function(url, args, features) {
+ // Get relative directory to where TestRunner.html lives
+ // A risky assumption is that the user's TestRunner is named TestRunner.html
+ var doc_location = document.location.toString();
+ var end_of_base_ref = doc_location.indexOf('TestRunner.html');
+ var base_ref = doc_location.substring(0, end_of_base_ref);
+
+ var fullURL = base_ref + "TestRunner.html?singletest=" + escape(browserBot.modalDialogTest) + "&autoURL=" + escape(url) + "&runInterval=" + runOptions.runInterval;
+ browserBot.modalDialogTest = null;
+
+ var returnValue = oldShowModalDialog(fullURL, args, features);
+ return returnValue;
+ };
+};
+
+IEBrowserBot.prototype.modifySeparateTestWindowToDetectPageLoads = function(windowObject) {
+ this.pageUnloading = false;
+ var self = this;
+ var pageUnloadDetector = function() {
+ self.pageUnloading = true;
+ };
+ windowObject.attachEvent("onbeforeunload", pageUnloadDetector);
+ BrowserBot.prototype.modifySeparateTestWindowToDetectPageLoads.call(this, windowObject);
+};
+
+IEBrowserBot.prototype.pollForLoad = function(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker) {
+ LOG.debug("IEBrowserBot.pollForLoad: " + marker);
+ if (!this.permDeniedCount[marker]) this.permDeniedCount[marker] = 0;
+ BrowserBot.prototype.pollForLoad.call(this, loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
+ if (this.pageLoadError) {
+ if (this.pageUnloading) {
+ var self = this;
+ LOG.warn("pollForLoad UNLOADING (" + marker + "): caught exception while firing events on unloading page: " + this.pageLoadError.message);
+ this.reschedulePoller(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
+ this.pageLoadError = null;
+ return;
+ } else if (((this.pageLoadError.message == "Permission denied") || (/^Access is denied/.test(this.pageLoadError.message)))
+ && this.permDeniedCount[marker]++ < 8) {
+ if (this.permDeniedCount[marker] > 4) {
+ var canAccessThisWindow;
+ var canAccessCurrentlySelectedWindow;
+ try {
+ windowObject.location.href;
+ canAccessThisWindow = true;
+ } catch (e) {}
+ try {
+ this.getCurrentWindow(true).location.href;
+ canAccessCurrentlySelectedWindow = true;
+ } catch (e) {}
+ if (canAccessCurrentlySelectedWindow & !canAccessThisWindow) {
+ LOG.warn("pollForLoad (" + marker + ") ABORTING: " + this.pageLoadError.message + " (" + this.permDeniedCount[marker] + "), but the currently selected window is fine");
+ // returning without rescheduling
+ this.pageLoadError = null;
+ return;
+ }
+ }
+
+ var self = this;
+ LOG.warn("pollForLoad (" + marker + "): " + this.pageLoadError.message + " (" + this.permDeniedCount[marker] + "), waiting to see if it goes away");
+ this.reschedulePoller(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
+ this.pageLoadError = null;
+ return;
+ }
+ //handy for debugging!
+ //throw this.pageLoadError;
+ }
+};
+
+IEBrowserBot.prototype._windowClosed = function(win) {
+ try {
+ var c = win.closed;
+ // frame windows claim to be non-closed when their parents are closed
+ // but you can't access their document objects in that case
+ if (!c) {
+ try {
+ win.document;
+ } catch (de) {
+ if (de.message == "Permission denied") {
+ // the window is probably unloading, which means it's probably not closed yet
+ return false;
+ }
+ else if (/^Access is denied/.test(de.message)) {
+ // rare variation on "Permission denied"?
+ LOG.debug("IEBrowserBot.windowClosed: got " + de.message + " (this.pageUnloading=" + this.pageUnloading + "); assuming window is unloading, probably not closed yet");
+ return false;
+ } else {
+ // this is probably one of those frame window situations
+ LOG.debug("IEBrowserBot.windowClosed: couldn't read win.document, assume closed: " + de.message + " (this.pageUnloading=" + this.pageUnloading + ")");
+ return true;
+ }
+ }
+ }
+ if (c == null) {
+ LOG.debug("IEBrowserBot.windowClosed: win.closed was null, assuming closed");
+ return true;
+ }
+ return c;
+ } catch (e) {
+ LOG.debug("IEBrowserBot._windowClosed: Got an exception trying to read win.closed; we'll have to take a guess!");
+
+ if (browserVersion.isHTA) {
+ if (e.message == "Permission denied") {
+ // the window is probably unloading, which means it's not closed yet
+ return false;
+ } else {
+ // there's a good chance that we've lost contact with the window object if it is closed
+ return true;
+ }
+ } else {
+ // the window is probably unloading, which means it's not closed yet
+ return false;
+ }
+ }
+};
+
+/**
+ * In IE, getElementById() also searches by name - this is an optimisation for IE.
+ */
+IEBrowserBot.prototype.locateElementByIdentifer = function(identifier, inDocument, inWindow) {
+ return inDocument.getElementById(identifier);
+};
+
+SafariBrowserBot.prototype.modifyWindowToRecordPopUpDialogs = function(windowToModify, browserBot) {
+ BrowserBot.prototype.modifyWindowToRecordPopUpDialogs(windowToModify, browserBot);
+
+ var originalOpen = windowToModify.open;
+ /*
+ * Safari seems to be broken, so that when we manually trigger the onclick method
+ * of a button/href, any window.open calls aren't resolved relative to the app location.
+ * So here we replace the open() method with one that does resolve the url correctly.
+ */
+ windowToModify.open = function(url, windowName, windowFeatures, replaceFlag) {
+
+ if (url.startsWith("http://") || url.startsWith("https://") || url.startsWith("/")) {
+ return originalOpen(url, windowName, windowFeatures, replaceFlag);
+ }
+
+ // Reduce the current path to the directory
+ var currentPath = windowToModify.location.pathname || "/";
+ currentPath = currentPath.replace(/\/[^\/]*$/, "/");
+
+ // Remove any leading "./" from the new url.
+ url = url.replace(/^\.\//, "");
+
+ newUrl = currentPath + url;
+
+ var openedWindow = originalOpen(newUrl, windowName, windowFeatures, replaceFlag);
+ LOG.debug("window.open call intercepted; window ID (which you can use with selectWindow()) is \"" + windowName + "\"");
+ if (windowName!=null) {
+ openedWindow["seleniumWindowName"] = windowName;
+ }
+ return openedWindow;
+ };
+};
+
+MozillaBrowserBot.prototype._fireEventOnElement = function(eventType, element, clientX, clientY) {
+ var win = this.getCurrentWindow();
+ triggerEvent(element, 'focus', false);
+
+ // Add an event listener that detects if the default action has been prevented.
+ // (This is caused by a javascript onclick handler returning false)
+ // we capture the whole event, rather than the getPreventDefault() state at the time,
+ // because we need to let the entire event bubbling and capturing to go through
+ // before making a decision on whether we should force the href
+ var savedEvent = null;
+
+ element.addEventListener(eventType, function(evt) {
+ savedEvent = evt;
+ }, false);
+
+ this._modifyElementTarget(element);
+
+ // Trigger the event.
+ this.browserbot.triggerMouseEvent(element, eventType, true, clientX, clientY);
+
+ if (this._windowClosed(win)) {
+ return;
+ }
+
+ // Perform the link action if preventDefault was set.
+ // In chrome URL, the link action is already executed by triggerMouseEvent.
+ if (!browserVersion.isChrome && savedEvent != null && !savedEvent.getPreventDefault()) {
+ var targetWindow = this.browserbot._getTargetWindow(element);
+ if (element.href) {
+ targetWindow.location.href = element.href;
+ } else {
+ this.browserbot._handleClickingImagesInsideLinks(targetWindow, element);
+ }
+ }
+
+};
+
+
+OperaBrowserBot.prototype._fireEventOnElement = function(eventType, element, clientX, clientY) {
+ var win = this.getCurrentWindow();
+ triggerEvent(element, 'focus', false);
+
+ this._modifyElementTarget(element);
+
+ // Trigger the click event.
+ this.browserbot.triggerMouseEvent(element, eventType, true, clientX, clientY);
+
+ if (this._windowClosed(win)) {
+ return;
+ }
+
+};
+
+
+KonquerorBrowserBot.prototype._fireEventOnElement = function(eventType, element, clientX, clientY) {
+ var win = this.getCurrentWindow();
+ triggerEvent(element, 'focus', false);
+
+ this._modifyElementTarget(element);
+
+ if (element[eventType]) {
+ element[eventType]();
+ }
+ else {
+ this.browserbot.triggerMouseEvent(element, eventType, true, clientX, clientY);
+ }
+
+ if (this._windowClosed(win)) {
+ return;
+ }
+
+};
+
+SafariBrowserBot.prototype._fireEventOnElement = function(eventType, element, clientX, clientY) {
+ triggerEvent(element, 'focus', false);
+ var wasChecked = element.checked;
+
+ this._modifyElementTarget(element);
+
+ // For form element it is simple.
+ if (element[eventType]) {
+ element[eventType]();
+ }
+ // For links and other elements, event emulation is required.
+ else {
+ var targetWindow = this.browserbot._getTargetWindow(element);
+ // todo: deal with anchors?
+ this.browserbot.triggerMouseEvent(element, eventType, true, clientX, clientY);
+
+ }
+
+};
+
+SafariBrowserBot.prototype.refresh = function() {
+ var win = this.getCurrentWindow();
+ if (win.location.hash) {
+ // DGF Safari refuses to refresh when there's a hash symbol in the URL
+ win.location.hash = "";
+ var actuallyReload = function() {
+ win.location.reload(true);
+ }
+ window.setTimeout(actuallyReload, 1);
+ } else {
+ win.location.reload(true);
+ }
+};
+
+IEBrowserBot.prototype._fireEventOnElement = function(eventType, element, clientX, clientY) {
+ var win = this.getCurrentWindow();
+ triggerEvent(element, 'focus', false);
+
+ var wasChecked = element.checked;
+
+ // Set a flag that records if the page will unload - this isn't always accurate, because
+ // <a href="javascript:alert('foo'):"> triggers the onbeforeunload event, even thought the page won't unload
+ var pageUnloading = false;
+ var pageUnloadDetector = function() {
+ pageUnloading = true;
+ };
+ win.attachEvent("onbeforeunload", pageUnloadDetector);
+ this._modifyElementTarget(element);
+ if (element[eventType]) {
+ element[eventType]();
+ }
+ else {
+ this.browserbot.triggerMouseEvent(element, eventType, true, clientX, clientY);
+ }
+
+
+ // If the page is going to unload - still attempt to fire any subsequent events.
+ // However, we can't guarantee that the page won't unload half way through, so we need to handle exceptions.
+ try {
+ win.detachEvent("onbeforeunload", pageUnloadDetector);
+
+ if (this._windowClosed(win)) {
+ return;
+ }
+
+ // Onchange event is not triggered automatically in IE.
+ if (isDefined(element.checked) && wasChecked != element.checked) {
+ triggerEvent(element, 'change', true);
+ }
+
+ }
+ catch (e) {
+ // If the page is unloading, we may get a "Permission denied" or "Unspecified error".
+ // Just ignore it, because the document may have unloaded.
+ if (pageUnloading) {
+ LOG.logHook = function() {
+ };
+ LOG.warn("Caught exception when firing events on unloading page: " + e.message);
+ return;
+ }
+ throw e;
+ }
+};
diff --git a/tests/test_tools/selenium/core/scripts/selenium-browserdetect.js b/tests/test_tools/selenium/core/scripts/selenium-browserdetect.js
index d97e5a58..a9607371 100644
--- a/tests/test_tools/selenium/core/scripts/selenium-browserdetect.js
+++ b/tests/test_tools/selenium/core/scripts/selenium-browserdetect.js
@@ -30,32 +30,66 @@ var BrowserVersion = function() {
return;
}
+ var _getQueryParameter = function(searchKey) {
+ var str = location.search.substr(1);
+ if (str == null) return null;
+ var clauses = str.split('&');
+ for (var i = 0; i < clauses.length; i++) {
+ var keyValuePair = clauses[i].split('=', 2);
+ var key = unescape(keyValuePair[0]);
+ if (key == searchKey) {
+ return unescape(keyValuePair[1]);
+ }
+ }
+ return null;
+ };
+
var self = this;
var checkChrome = function() {
var loc = window.document.location.href;
try {
loc = window.top.document.location.href;
+ if (/^chrome:\/\//.test(loc)) {
+ self.isChrome = true;
+ } else {
+ self.isChrome = false;
+ }
} catch (e) {
// can't see the top (that means we might be chrome, but it's impossible to be sure)
self.isChromeDetectable = "no, top location couldn't be read in this window";
+ if (_getQueryParameter('thisIsChrome')) {
+ self.isChrome = true;
+ } else {
+ self.isChrome = false;
+ }
}
- if (/^chrome:\/\//.test(loc)) {
- self.isChrome = true;
- } else {
- self.isChrome = false;
- }
+
}
+
+
if (this.name == "Microsoft Internet Explorer") {
this.browser = BrowserVersion.IE;
this.isIE = true;
- if (window.top.SeleniumHTARunner && window.top.document.location.pathname.match(/.hta$/i)) {
- this.isHTA = true;
+ try {
+ if (window.top.SeleniumHTARunner && window.top.document.location.pathname.match(/.hta$/i)) {
+ this.isHTA = true;
+ }
+ } catch (e) {
+ this.isHTADetectable = "no, top location couldn't be read in this window";
+ if (_getQueryParameter('thisIsHTA')) {
+ self.isHTA = true;
+ } else {
+ self.isHTA = false;
+ }
}
if ("0" == navigator.appMinorVersion) {
this.preSV1 = true;
+ if (navigator.appVersion.match(/MSIE 6.0/)) {
+ this.appearsToBeBrokenInitialIE6 = true;
+ }
}
return;
}
diff --git a/tests/test_tools/selenium/core/scripts/selenium-commandhandlers.js b/tests/test_tools/selenium/core/scripts/selenium-commandhandlers.js
index c11a80ad..a23e9335 100644
--- a/tests/test_tools/selenium/core/scripts/selenium-commandhandlers.js
+++ b/tests/test_tools/selenium/core/scripts/selenium-commandhandlers.js
@@ -12,141 +12,165 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
-*
*/
-function CommandHandlerFactory() {
-
- var self = this;
-
- this.handlers = {};
-
- this.registerAction = function(name, action, wait, dontCheckAlertsAndConfirms) {
- var handler = new ActionHandler(action, wait, dontCheckAlertsAndConfirms);
- this.handlers[name] = handler;
- };
-
- this.registerAccessor = function(name, accessor) {
- var handler = new AccessorHandler(accessor);
- this.handlers[name] = handler;
- };
-
- this.registerAssert = function(name, assertion, haltOnFailure) {
- var handler = new AssertHandler(assertion, haltOnFailure);
- this.handlers[name] = handler;
- };
-
- this.getCommandHandler = function(name) {
- return this.handlers[name] || null; // todo: why null, and not undefined?
- };
-
- // Methods of the form getFoo(target) result in commands:
- // getFoo, assertFoo, verifyFoo, assertNotFoo, verifyNotFoo
- // storeFoo, waitForFoo, and waitForNotFoo.
- var _registerAllAccessors = function(commandObject) {
- for (var functionName in commandObject) {
- var matchForGetter = /^get([A-Z].+)$/.exec(functionName);
- if (matchForGetter != null) {
- var accessor = commandObject[functionName];
- var baseName = matchForGetter[1];
- self.registerAccessor(functionName, accessor);
- self.registerAssertionsBasedOnAccessor(accessor, baseName);
- self.registerStoreCommandBasedOnAccessor(accessor, baseName);
- self.registerWaitForCommandsBasedOnAccessor(accessor, baseName);
- }
- var matchForIs = /^is([A-Z].+)$/.exec(functionName);
- if (matchForIs != null) {
- var accessor = commandObject[functionName];
- var baseName = matchForIs[1];
- var predicate = self.createPredicateFromBooleanAccessor(accessor);
- self.registerAccessor(functionName, accessor);
- self.registerAssertionsBasedOnAccessor(accessor, baseName, predicate);
- self.registerStoreCommandBasedOnAccessor(accessor, baseName);
- self.registerWaitForCommandsBasedOnAccessor(accessor, baseName, predicate);
+
+// A naming convention used in this file:
+//
+//
+// - a "seleniumApi" is an instance of the Selenium object, defined in selenium-api.js.
+//
+// - a "Method" is an unbound function whose target must be supplied when it's called, ie.
+// it should be invoked using Function.call() or Function.apply()
+//
+// - a "Block" is a function that has been bound to a target object, so can be called invoked directly
+// (or with a null target)
+//
+// - "CommandHandler" is effectively an abstract base for
+// various handlers including ActionHandler, AccessorHandler and AssertHandler.
+// Subclasses need to implement an execute(seleniumApi, command) function,
+// where seleniumApi is the Selenium object, and command a SeleniumCommand object.
+//
+// - Handlers will return a "result" object (ActionResult, AccessorResult, AssertResult).
+// ActionResults may contain a .terminationCondition function which is run by
+// -executionloop.js after the command is run; we'll run it over and over again
+// until it returns true or the .terminationCondition throws an exception.
+// AccessorResults will contain the results of running getter (e.g. getTitle returns
+// the title as a string).
+
+var CommandHandlerFactory = classCreate();
+objectExtend(CommandHandlerFactory.prototype, {
+
+ initialize: function() {
+ this.handlers = {};
+ },
+
+ registerAction: function(name, actionBlock, wait, dontCheckAlertsAndConfirms) {
+ this.handlers[name] = new ActionHandler(actionBlock, wait, dontCheckAlertsAndConfirms);
+ },
+
+ registerAccessor: function(name, accessBlock) {
+ this.handlers[name] = new AccessorHandler(accessBlock);
+ },
+
+ registerAssert: function(name, assertBlock, haltOnFailure) {
+ this.handlers[name] = new AssertHandler(assertBlock, haltOnFailure);
+ },
+
+ getCommandHandler: function(name) {
+ return this.handlers[name];
+ },
+
+ _registerAllAccessors: function(seleniumApi) {
+ // Methods of the form getFoo(target) result in commands:
+ // getFoo, assertFoo, verifyFoo, assertNotFoo, verifyNotFoo
+ // storeFoo, waitForFoo, and waitForNotFoo.
+ for (var functionName in seleniumApi) {
+ var match = /^(get|is)([A-Z].+)$/.exec(functionName);
+ if (match) {
+ var accessMethod = seleniumApi[functionName];
+ var accessBlock = fnBind(accessMethod, seleniumApi);
+ var baseName = match[2];
+ var isBoolean = (match[1] == "is");
+ var requiresTarget = (accessMethod.length == 1);
+
+ this.registerAccessor(functionName, accessBlock);
+ this._registerStoreCommandForAccessor(baseName, accessBlock, requiresTarget);
+
+ var predicateBlock = this._predicateForAccessor(accessBlock, requiresTarget, isBoolean);
+ this._registerAssertionsForPredicate(baseName, predicateBlock);
+ this._registerWaitForCommandsForPredicate(seleniumApi, baseName, predicateBlock);
}
}
- };
-
- var _registerAllActions = function(commandObject) {
- for (var functionName in commandObject) {
- var result = /^do([A-Z].+)$/.exec(functionName);
- if (result != null) {
- var actionName = result[1].lcfirst();
-
- // Register the action without the wait flag.
- var action = commandObject[functionName];
- self.registerAction(actionName, action, false, action.dontCheckAlertsAndConfirms);
-
- // Register actionName + "AndWait" with the wait flag;
- var waitActionName = actionName + "AndWait";
- self.registerAction(waitActionName, action, true, action.dontCheckAlertsAndConfirms);
+ },
+
+ _registerAllActions: function(seleniumApi) {
+ for (var functionName in seleniumApi) {
+ var match = /^do([A-Z].+)$/.exec(functionName);
+ if (match) {
+ var actionName = match[1].lcfirst();
+ var actionMethod = seleniumApi[functionName];
+ var dontCheckPopups = actionMethod.dontCheckAlertsAndConfirms;
+ var actionBlock = fnBind(actionMethod, seleniumApi);
+ this.registerAction(actionName, actionBlock, false, dontCheckPopups);
+ this.registerAction(actionName + "AndWait", actionBlock, true, dontCheckPopups);
}
}
- };
+ },
- var _registerAllAsserts = function(commandObject) {
- for (var functionName in commandObject) {
- var result = /^assert([A-Z].+)$/.exec(functionName);
- if (result != null) {
- var assert = commandObject[functionName];
+ _registerAllAsserts: function(seleniumApi) {
+ for (var functionName in seleniumApi) {
+ var match = /^assert([A-Z].+)$/.exec(functionName);
+ if (match) {
+ var assertBlock = fnBind(seleniumApi[functionName], seleniumApi);
// Register the assert with the "assert" prefix, and halt on failure.
var assertName = functionName;
- self.registerAssert(assertName, assert, true);
+ this.registerAssert(assertName, assertBlock, true);
// Register the assert with the "verify" prefix, and do not halt on failure.
- var verifyName = "verify" + result[1];
- self.registerAssert(verifyName, assert, false);
+ var verifyName = "verify" + match[1];
+ this.registerAssert(verifyName, assertBlock, false);
}
}
- };
-
- this.registerAll = function(commandObject) {
- _registerAllAccessors(commandObject);
- _registerAllActions(commandObject);
- _registerAllAsserts(commandObject);
- };
-
- // Given an accessor function getBlah(target),
- // return a "predicate" equivalient to isBlah(target, value) that
- // is true when the value returned by the accessor matches the specified value.
- this.createPredicateFromSingleArgAccessor = function(accessor) {
+ },
+
+ registerAll: function(seleniumApi) {
+ this._registerAllAccessors(seleniumApi);
+ this._registerAllActions(seleniumApi);
+ this._registerAllAsserts(seleniumApi);
+ },
+
+ _predicateForAccessor: function(accessBlock, requiresTarget, isBoolean) {
+ if (isBoolean) {
+ return this._predicateForBooleanAccessor(accessBlock);
+ }
+ if (requiresTarget) {
+ return this._predicateForSingleArgAccessor(accessBlock);
+ }
+ return this._predicateForNoArgAccessor(accessBlock);
+ },
+
+ _predicateForSingleArgAccessor: function(accessBlock) {
+ // Given an accessor function getBlah(target),
+ // return a "predicate" equivalient to isBlah(target, value) that
+ // is true when the value returned by the accessor matches the specified value.
return function(target, value) {
- var accessorResult = accessor.call(this, target);
+ var accessorResult = accessBlock(target);
if (PatternMatcher.matches(value, accessorResult)) {
return new PredicateResult(true, "Actual value '" + accessorResult + "' did match '" + value + "'");
} else {
return new PredicateResult(false, "Actual value '" + accessorResult + "' did not match '" + value + "'");
}
};
- };
+ },
- // Given a (no-arg) accessor function getBlah(),
- // return a "predicate" equivalient to isBlah(value) that
- // is true when the value returned by the accessor matches the specified value.
- this.createPredicateFromNoArgAccessor = function(accessor) {
+ _predicateForNoArgAccessor: function(accessBlock) {
+ // Given a (no-arg) accessor function getBlah(),
+ // return a "predicate" equivalient to isBlah(value) that
+ // is true when the value returned by the accessor matches the specified value.
return function(value) {
- var accessorResult = accessor.call(this);
+ var accessorResult = accessBlock();
if (PatternMatcher.matches(value, accessorResult)) {
return new PredicateResult(true, "Actual value '" + accessorResult + "' did match '" + value + "'");
} else {
return new PredicateResult(false, "Actual value '" + accessorResult + "' did not match '" + value + "'");
}
};
- };
+ },
- // Given a boolean accessor function isBlah(),
- // return a "predicate" equivalient to isBlah() that
- // returns an appropriate PredicateResult value.
- this.createPredicateFromBooleanAccessor = function(accessor) {
+ _predicateForBooleanAccessor: function(accessBlock) {
+ // Given a boolean accessor function isBlah(),
+ // return a "predicate" equivalient to isBlah() that
+ // returns an appropriate PredicateResult value.
return function() {
var accessorResult;
if (arguments.length > 2) throw new SeleniumError("Too many arguments! " + arguments.length);
if (arguments.length == 2) {
- accessorResult = accessor.call(this, arguments[0], arguments[1]);
+ accessorResult = accessBlock(arguments[0], arguments[1]);
} else if (arguments.length == 1) {
- accessorResult = accessor.call(this, arguments[0]);
+ accessorResult = accessBlock(arguments[0]);
} else {
- accessorResult = accessor.call(this);
+ accessorResult = accessBlock();
}
if (accessorResult) {
return new PredicateResult(true, "true");
@@ -154,71 +178,56 @@ function CommandHandlerFactory() {
return new PredicateResult(false, "false");
}
};
- };
-
- // Given an accessor fuction getBlah([target]) (target is optional)
- // return a predicate equivalent to isBlah([target,] value) that
- // is true when the value returned by the accessor matches the specified value.
- this.createPredicateFromAccessor = function(accessor) {
- if (accessor.length == 0) {
- return self.createPredicateFromNoArgAccessor(accessor);
- }
- return self.createPredicateFromSingleArgAccessor(accessor);
- };
+ },
- // Given a predicate, return the negation of that predicate.
- // Leaves the message unchanged.
- // Used to create assertNot, verifyNot, and waitForNot commands.
- this.invertPredicate = function(predicate) {
+ _invertPredicate: function(predicateBlock) {
+ // Given a predicate, return the negation of that predicate.
+ // Leaves the message unchanged.
+ // Used to create assertNot, verifyNot, and waitForNot commands.
return function(target, value) {
- var result = predicate.call(this, target, value);
- result.isTrue = ! result.isTrue;
+ var result = predicateBlock(target, value);
+ result.isTrue = !result.isTrue;
return result;
};
- };
+ },
- // Convert an isBlahBlah(target, value) function into an assertBlahBlah(target, value) function.
- this.createAssertionFromPredicate = function(predicate) {
+ createAssertionFromPredicate: function(predicateBlock) {
+ // Convert an isBlahBlah(target, value) function into an assertBlahBlah(target, value) function.
return function(target, value) {
- var result = predicate.call(this, target, value);
+ var result = predicateBlock(target, value);
if (!result.isTrue) {
Assert.fail(result.message);
}
};
- };
-
+ },
- var _negtiveName = function(baseName) {
+ _invertPredicateName: function(baseName) {
var matchResult = /^(.*)Present$/.exec(baseName);
if (matchResult != null) {
return matchResult[1] + "NotPresent";
}
return "Not" + baseName;
- };
-
- // Register an assertion, a verification, a negative assertion,
- // and a negative verification based on the specified accessor.
- this.registerAssertionsBasedOnAccessor = function(accessor, baseName, predicate) {
- if (predicate == null) {
- predicate = self.createPredicateFromAccessor(accessor);
- }
- var assertion = self.createAssertionFromPredicate(predicate);
- self.registerAssert("assert" + baseName, assertion, true);
- self.registerAssert("verify" + baseName, assertion, false);
-
- var invertedPredicate = self.invertPredicate(predicate);
- var negativeAssertion = self.createAssertionFromPredicate(invertedPredicate);
- self.registerAssert("assert" + _negtiveName(baseName), negativeAssertion, true);
- self.registerAssert("verify" + _negtiveName(baseName), negativeAssertion, false);
- };
-
- // Convert an isBlahBlah(target, value) function into a waitForBlahBlah(target, value) function.
- this.createWaitForActionFromPredicate = function(predicate) {
+ },
+
+ _registerAssertionsForPredicate: function(baseName, predicateBlock) {
+ // Register an assertion, a verification, a negative assertion,
+ // and a negative verification based on the specified accessor.
+ var assertBlock = this.createAssertionFromPredicate(predicateBlock);
+ this.registerAssert("assert" + baseName, assertBlock, true);
+ this.registerAssert("verify" + baseName, assertBlock, false);
+
+ var invertedPredicateBlock = this._invertPredicate(predicateBlock);
+ var negativeassertBlock = this.createAssertionFromPredicate(invertedPredicateBlock);
+ this.registerAssert("assert" + this._invertPredicateName(baseName), negativeassertBlock, true);
+ this.registerAssert("verify" + this._invertPredicateName(baseName), negativeassertBlock, false);
+ },
+
+ _waitForActionForPredicate: function(predicateBlock) {
+ // Convert an isBlahBlah(target, value) function into a waitForBlahBlah(target, value) function.
return function(target, value) {
- var seleniumApi = this;
- return function () {
+ var terminationCondition = function () {
try {
- return predicate.call(seleniumApi, target, value).isTrue;
+ return predicateBlock(target, value).isTrue;
} catch (e) {
// Treat exceptions as meaning the condition is not yet met.
// Useful, for example, for waitForValue when the element has
@@ -227,40 +236,41 @@ function CommandHandlerFactory() {
return false;
}
};
+ return Selenium.decorateFunctionWithTimeout(terminationCondition, this.defaultTimeout);
};
- };
-
- // Register a waitForBlahBlah and waitForNotBlahBlah based on the specified accessor.
- this.registerWaitForCommandsBasedOnAccessor = function(accessor, baseName, predicate) {
- if (predicate==null) {
- predicate = self.createPredicateFromAccessor(accessor);
- }
- var waitForAction = self.createWaitForActionFromPredicate(predicate);
- self.registerAction("waitFor"+baseName, waitForAction, false, true);
- var invertedPredicate = self.invertPredicate(predicate);
- var waitForNotAction = self.createWaitForActionFromPredicate(invertedPredicate);
- self.registerAction("waitFor"+_negtiveName(baseName), waitForNotAction, false, true);
+ },
+
+ _registerWaitForCommandsForPredicate: function(seleniumApi, baseName, predicateBlock) {
+ // Register a waitForBlahBlah and waitForNotBlahBlah based on the specified accessor.
+ var waitForActionMethod = this._waitForActionForPredicate(predicateBlock);
+ var waitForActionBlock = fnBind(waitForActionMethod, seleniumApi);
+
+ var invertedPredicateBlock = this._invertPredicate(predicateBlock);
+ var waitForNotActionMethod = this._waitForActionForPredicate(invertedPredicateBlock);
+ var waitForNotActionBlock = fnBind(waitForNotActionMethod, seleniumApi);
+
+ this.registerAction("waitFor" + baseName, waitForActionBlock, false, true);
+ this.registerAction("waitFor" + this._invertPredicateName(baseName), waitForNotActionBlock, false, true);
//TODO decide remove "waitForNot.*Present" action name or not
//for the back compatiblity issues we still make waitForNot.*Present availble
- self.registerAction("waitForNot"+baseName, waitForNotAction, false, true);
- }
+ this.registerAction("waitForNot" + baseName, waitForNotActionBlock, false, true);
+ },
- // Register a storeBlahBlah based on the specified accessor.
- this.registerStoreCommandBasedOnAccessor = function(accessor, baseName) {
+ _registerStoreCommandForAccessor: function(baseName, accessBlock, requiresTarget) {
var action;
- if (accessor.length == 1) {
+ if (requiresTarget) {
action = function(target, varName) {
- storedVars[varName] = accessor.call(this, target);
+ storedVars[varName] = accessBlock(target);
};
} else {
action = function(varName) {
- storedVars[varName] = accessor.call(this);
+ storedVars[varName] = accessBlock();
};
}
- self.registerAction("store"+baseName, action, false, accessor.dontCheckAlertsAndConfirms);
- };
+ this.registerAction("store" + baseName, action, false, true);
+ }
-}
+});
function PredicateResult(isTrue, message) {
this.isTrue = isTrue;
@@ -271,17 +281,17 @@ function PredicateResult(isTrue, message) {
// various handlers including ActionHandler, AccessorHandler and AssertHandler.
// Subclasses need to implement an execute(seleniumApi, command) function,
// where seleniumApi is the Selenium object, and command a SeleniumCommand object.
-function CommandHandler(type, haltOnFailure, executor) {
+function CommandHandler(type, haltOnFailure) {
this.type = type;
this.haltOnFailure = haltOnFailure;
- this.executor = executor;
}
// An ActionHandler is a command handler that executes the sepcified action,
// possibly checking for alerts and confirmations (if checkAlerts is set), and
// possibly waiting for a page load if wait is set.
-function ActionHandler(action, wait, dontCheckAlerts) {
- CommandHandler.call(this, "action", true, action);
+function ActionHandler(actionBlock, wait, dontCheckAlerts) {
+ this.actionBlock = actionBlock;
+ CommandHandler.call(this, "action", true);
if (wait) {
this.wait = true;
}
@@ -290,10 +300,11 @@ function ActionHandler(action, wait, dontCheckAlerts) {
}
ActionHandler.prototype = new CommandHandler;
ActionHandler.prototype.execute = function(seleniumApi, command) {
- if (this.checkAlerts && (null==/(Alert|Confirmation)(Not)?Present/.exec(command.command))) {
+ if (this.checkAlerts && (null == /(Alert|Confirmation)(Not)?Present/.exec(command.command))) {
+ // todo: this conditional logic is ugly
seleniumApi.ensureNoUnhandledPopups();
}
- var terminationCondition = this.executor.call(seleniumApi, command.target, command.value);
+ var terminationCondition = this.actionBlock(command.target, command.value);
// If the handler didn't return a wait flag, check to see if the
// handler was registered with the wait flag.
if (terminationCondition == undefined && this.wait) {
@@ -306,12 +317,13 @@ function ActionResult(terminationCondition) {
this.terminationCondition = terminationCondition;
}
-function AccessorHandler(accessor) {
- CommandHandler.call(this, "accessor", true, accessor);
+function AccessorHandler(accessBlock) {
+ this.accessBlock = accessBlock;
+ CommandHandler.call(this, "accessor", true);
}
AccessorHandler.prototype = new CommandHandler;
AccessorHandler.prototype.execute = function(seleniumApi, command) {
- var returnValue = this.executor.call(seleniumApi, command.target, command.value);
+ var returnValue = this.accessBlock(command.target, command.value);
return new AccessorResult(returnValue);
};
@@ -322,14 +334,15 @@ function AccessorResult(result) {
/**
* Handler for assertions and verifications.
*/
-function AssertHandler(assertion, haltOnFailure) {
- CommandHandler.call(this, "assert", haltOnFailure || false, assertion);
+function AssertHandler(assertBlock, haltOnFailure) {
+ this.assertBlock = assertBlock;
+ CommandHandler.call(this, "assert", haltOnFailure || false);
}
AssertHandler.prototype = new CommandHandler;
AssertHandler.prototype.execute = function(seleniumApi, command) {
var result = new AssertResult();
try {
- this.executor.call(seleniumApi, command.target, command.value);
+ this.assertBlock(command.target, command.value);
} catch (e) {
// If this is not a AssertionFailedError, or we should haltOnFailure, rethrow.
if (!e.isAssertionFailedError) {
diff --git a/tests/test_tools/selenium/core/scripts/selenium-executionloop.js b/tests/test_tools/selenium/core/scripts/selenium-executionloop.js
index d59fc148..be54115e 100644
--- a/tests/test_tools/selenium/core/scripts/selenium-executionloop.js
+++ b/tests/test_tools/selenium/core/scripts/selenium-executionloop.js
@@ -64,7 +64,7 @@ TestLoop.prototype = {
// Pause: enable the "next/continue" button
this.pause();
} else {
- window.setTimeout(this.resume.bind(this), delay);
+ window.setTimeout(fnBind(this.resume, this), delay);
}
},
@@ -78,9 +78,11 @@ TestLoop.prototype = {
this._executeCurrentCommand();
this.continueTestWhenConditionIsTrue();
} catch (e) {
- this._handleCommandError(e);
- this._testComplete();
- return;
+ if (!this._handleCommandError(e)) {
+ this._testComplete();
+ } else {
+ this.continueTest();
+ }
}
},
@@ -107,11 +109,10 @@ TestLoop.prototype = {
command.target = selenium.preprocessParameter(command.target);
command.value = selenium.preprocessParameter(command.value);
LOG.debug("Command found, going to execute " + command.command);
- var result = handler.execute(selenium, command);
- LOG.debug("Command complete");
- this.commandComplete(result);
+ this.result = handler.execute(selenium, command);
+
- this.waitForCondition = result.terminationCondition;
+ this.waitForCondition = this.result.terminationCondition;
},
@@ -122,10 +123,10 @@ TestLoop.prototype = {
if (e.message) {
msg += " The error message is: " + e.message;
}
- this.commandError(msg);
+ return this.commandError(msg);
} else {
LOG.error(e.message);
- this.commandError(e.message);
+ return this.commandError(e.message);
}
},
@@ -135,23 +136,30 @@ TestLoop.prototype = {
* on with test. Fail the current test if there's a timeout or an
* exception.
*/
- LOG.debug("currentTest.continueTestWhenConditionIsTrue()");
+ //LOG.debug("currentTest.continueTestWhenConditionIsTrue()");
selenium.browserbot.runScheduledPollers();
try {
- if (this.waitForCondition == null || this.waitForCondition()) {
+ if (this.waitForCondition == null) {
+ LOG.debug("null condition; let's continueTest()");
+ LOG.debug("Command complete");
+ this.commandComplete(this.result);
+ this.continueTest();
+ } else if (this.waitForCondition()) {
LOG.debug("condition satisfied; let's continueTest()");
this.waitForCondition = null;
+ LOG.debug("Command complete");
+ this.commandComplete(this.result);
this.continueTest();
} else {
- LOG.debug("waitForCondition was false; keep waiting!");
- window.setTimeout(this.continueTestWhenConditionIsTrue.bind(this), 100);
+ //LOG.debug("waitForCondition was false; keep waiting!");
+ window.setTimeout(fnBind(this.continueTestWhenConditionIsTrue, this), 10);
}
} catch (e) {
- var lastResult = {};
- lastResult.failed = true;
- lastResult.failureMessage = e.message;
- this.commandComplete(lastResult);
- this.testComplete();
+ this.result = {};
+ this.result.failed = true;
+ this.result.failureMessage = extractExceptionMessage(e);
+ this.commandComplete(this.result);
+ this.continueTest();
}
},
@@ -167,20 +175,3 @@ TestLoop.prototype = {
}
}
-
-function decorateFunctionWithTimeout(f, timeout) {
- if (f == null) {
- return null;
- }
- if (isNaN(timeout)) {
- throw new SeleniumError("Timeout is not a number: " + timeout);
- }
- var now = new Date().getTime();
- var timeoutTime = now + timeout;
- return function() {
- if (new Date().getTime() > timeoutTime) {
- throw new SeleniumError("Timed out after " + timeout + "ms");
- }
- return f();
- };
-}
diff --git a/tests/test_tools/selenium/core/scripts/selenium-logging.js b/tests/test_tools/selenium/core/scripts/selenium-logging.js
index 25e11463..6dac9518 100644
--- a/tests/test_tools/selenium/core/scripts/selenium-logging.js
+++ b/tests/test_tools/selenium/core/scripts/selenium-logging.js
@@ -52,6 +52,13 @@ Logger.prototype = {
"width=600,height=1000,bottom=0,right=0,status,scrollbars,resizable"
);
this.logWindow.moveTo(window.screenX + 1210, window.screenY + window.outerHeight - 1400);
+ if (browserVersion.appearsToBeBrokenInitialIE6) {
+ // I would really prefer for the message to immediately appear in the log window, the instant the user requests that the log window be
+ // visible. But when I initially coded it this way, thou message simply didn't appear unless I stepped through the code with a debugger.
+ // So obviously there is some timing issue here which I don't have the patience to figure out.
+ var pendingMessage = new LogMessage("warn", "You appear to be running an unpatched IE 6, which is not stable and can crash due to memory problems. We recommend you run Windows update to install a more stable version of IE.");
+ this.pendingMessages.push(pendingMessage);
+ }
return this.logWindow;
},
@@ -59,14 +66,15 @@ Logger.prototype = {
if (! this.getLogWindow()) {
this.openLogWindow();
}
+ setTimeout(function(){LOG.info("Log window displayed");}, 500);
},
- logHook: function(message, className) {
+ logHook: function(className, message) {
},
- log: function(message, className) {
+ log: function(className, message) {
var logWindow = this.getLogWindow();
- this.logHook(message, className);
+ this.logHook(className, message);
if (logWindow) {
if (logWindow.append) {
if (this.pendingMessages.length > 0) {
@@ -84,7 +92,7 @@ Logger.prototype = {
/* these logging messages are never flushed, which creates
an enormous array of strings that never stops growing. Only
turn this on if you need it for debugging! */
- //this.pendingMessages.push(new LogMessage(message, className));
+ //this.pendingMessages.push(new LogMessage(className, message));
}
},
@@ -101,31 +109,31 @@ Logger.prototype = {
},
debug: function(message) {
- this.log(message, "debug");
+ this.log("debug", message);
},
info: function(message) {
- this.log(message, "info");
+ this.log("info", message);
},
warn: function(message) {
- this.log(message, "warn");
+ this.log("warn", message);
},
error: function(message) {
- this.log(message, "error");
+ this.log("error", message);
},
exception: function(exception) {
- var msg = "Unexpected Exception: " + describe(exception, ', ');
- this.error(msg);
+ this.error("Unexpected Exception: " + extractExceptionMessage(exception));
+ this.error("Exception details: " + describe(exception, ', '));
}
};
var LOG = new Logger();
-var LogMessage = function(msg, type) {
+var LogMessage = function(type, msg) {
this.type = type;
this.msg = msg;
}
diff --git a/tests/test_tools/selenium/core/scripts/selenium-testrunner.js b/tests/test_tools/selenium/core/scripts/selenium-testrunner.js
index b5104d39..fd7f2076 100644
--- a/tests/test_tools/selenium/core/scripts/selenium-testrunner.js
+++ b/tests/test_tools/selenium/core/scripts/selenium-testrunner.js
@@ -20,36 +20,55 @@ var currentTest = null; // TODO: get rid of this global, which mirrors the htmlT
var selenium = null;
var htmlTestRunner;
-var HtmlTestRunner = Class.create();
-Object.extend(HtmlTestRunner.prototype, {
+var HtmlTestRunner = classCreate();
+objectExtend(HtmlTestRunner.prototype, {
initialize: function() {
this.metrics = new Metrics();
this.controlPanel = new HtmlTestRunnerControlPanel();
- this.htmlTestSuite = null;
this.testFailed = false;
this.currentTest = null;
this.runAllTests = false;
this.appWindow = null;
// we use a timeout here to make sure the LOG has loaded first, so we can see _every_ error
- setTimeout(function() {
+ setTimeout(fnBind(function() {
this.loadSuiteFrame();
- }.bind(this), 500);
+ }, this), 500);
+ },
+
+ getTestSuite: function() {
+ return suiteFrame.getCurrentTestSuite();
},
markFailed: function() {
this.testFailed = true;
- this.htmlTestSuite.markFailed();
+ this.getTestSuite().markFailed();
},
loadSuiteFrame: function() {
if (selenium == null) {
- selenium = Selenium.createForWindow(this._getApplicationWindow());
+ var appWindow = this._getApplicationWindow();
+ try { appWindow.location; }
+ catch (e) {
+ // when reloading, we may be pointing at an old window (Perm Denied)
+ setTimeout(fnBind(function() {
+ this.loadSuiteFrame();
+ }, this), 50);
+ return;
+ }
+ selenium = Selenium.createForWindow(appWindow);
this._registerCommandHandlers();
}
this.controlPanel.setHighlightOption();
var testSuiteName = this.controlPanel.getTestSuiteName();
+ var self = this;
if (testSuiteName) {
- suiteFrame.load(testSuiteName, this._onloadTestSuite.bind(this));
+ suiteFrame.load(testSuiteName, function() {setTimeout(fnBind(self._onloadTestSuite, self), 50)} );
+ selenium.browserbot.baseUrl = absolutify(testSuiteName, window.location.href);
+ }
+ // DGF or should we use the old default?
+ // selenium.browserbot.baseUrl = window.location.href;
+ if (this.controlPanel.getBaseUrl()) {
+ selenium.browserbot.baseUrl = this.controlPanel.getBaseUrl();
}
},
@@ -62,31 +81,30 @@ Object.extend(HtmlTestRunner.prototype, {
_getSeparateApplicationWindow: function () {
if (this.appWindow == null) {
- this.appWindow = openSeparateApplicationWindow('TestRunner-splash.html');
+ this.appWindow = openSeparateApplicationWindow('TestRunner-splash.html', this.controlPanel.isAutomatedRun());
}
return this.appWindow;
},
_onloadTestSuite:function () {
- this.htmlTestSuite = new HtmlTestSuite(suiteFrame.getDocument());
- if (! this.htmlTestSuite.isAvailable()) {
+ if (! this.getTestSuite().isAvailable()) {
return;
}
if (this.controlPanel.isAutomatedRun()) {
- htmlTestRunner.startTestSuite();
+ this.startTestSuite();
} else if (this.controlPanel.getAutoUrl()) {
//todo what is the autourl doing, left to check it out
- addLoadListener(this._getApplicationWindow(), this._startSingleTest.bind(this));
+ addLoadListener(this._getApplicationWindow(), fnBind(this._startSingleTest, this));
this._getApplicationWindow().src = this.controlPanel.getAutoUrl();
} else {
- this.htmlTestSuite.getSuiteRows()[0].loadTestCase();
+ this.getTestSuite().getSuiteRows()[0].loadTestCase();
}
},
_startSingleTest:function () {
- removeLoadListener(getApplicationWindow(), this._startSingleTest.bind(this));
+ removeLoadListener(getApplicationWindow(), fnBind(this._startSingleTest, this));
var singleTestName = this.controlPanel.getSingleTestName();
- testFrame.load(singleTestName, this.startTest.bind(this));
+ testFrame.load(singleTestName, fnBind(this.startTest, this));
},
_registerCommandHandlers: function () {
@@ -97,16 +115,17 @@ Object.extend(HtmlTestRunner.prototype, {
startTestSuite: function() {
this.controlPanel.reset();
this.metrics.resetMetrics();
- this.htmlTestSuite.reset();
+ this.getTestSuite().reset();
this.runAllTests = true;
this.runNextTest();
},
runNextTest: function () {
+ this.getTestSuite().updateSuiteWithResultOfPreviousTest();
if (!this.runAllTests) {
return;
}
- this.htmlTestSuite.runNextTestInSuite();
+ this.getTestSuite().runNextTestInSuite();
},
startTest: function () {
@@ -127,26 +146,15 @@ Object.extend(HtmlTestRunner.prototype, {
}
});
-var FeedbackColors = Class.create();
-Object.extend(FeedbackColors, {
- passColor : "#ccffcc",
- doneColor : "#eeffee",
- failColor : "#ffcccc",
- workingColor : "#ffffcc",
- breakpointColor : "#cccccc"
-});
-
-
var runInterval = 0;
-
/** SeleniumFrame encapsulates an iframe element */
-var SeleniumFrame = Class.create();
-Object.extend(SeleniumFrame.prototype, {
+var SeleniumFrame = classCreate();
+objectExtend(SeleniumFrame.prototype, {
initialize : function(frame) {
this.frame = frame;
- addLoadListener(this.frame, this._handleLoad.bind(this));
+ addLoadListener(this.frame, fnBind(this._handleLoad, this));
},
getDocument : function() {
@@ -154,13 +162,38 @@ Object.extend(SeleniumFrame.prototype, {
},
_handleLoad: function() {
- this._onLoad();
+ this._attachStylesheet();
+ this._onLoad();
if (this.loadCallback) {
this.loadCallback();
this.loadCallback = null;
}
},
+ _attachStylesheet: function() {
+ var d = this.getDocument();
+ var head = d.getElementsByTagName('head').item(0);
+ var styleLink = d.createElement("link");
+ styleLink.rel = "stylesheet";
+ styleLink.type = "text/css";
+ if (browserVersion && browserVersion.isChrome) {
+ // DGF We have to play a clever trick to get the right absolute path.
+ // This trick works on most browsers, (not IE), but is only needed in
+ // chrome
+ var tempLink = window.document.createElement("link");
+ tempLink.href = "selenium-test.css"; // this will become an absolute href
+ styleLink.href = tempLink.href;
+ } else {
+ // this works in every browser (except Firefox in chrome mode)
+ var styleSheetPath = window.location.pathname.replace(/[^\/\\]+$/, "selenium-test.css");
+ if (browserVersion.isIE && window.location.protocol == "file:") {
+ styleSheetPath = "file://" + styleSheetPath;
+ }
+ styleLink.href = styleSheetPath;
+ }
+ head.appendChild(styleLink);
+ },
+
_onLoad: function() {
},
@@ -169,10 +202,14 @@ Object.extend(SeleniumFrame.prototype, {
},
_setLocation: function(location) {
+ var isChrome = browserVersion.isChrome || false;
+ var isHTA = browserVersion.isHTA || false;
+ // DGF TODO multiWindow
+ location += "?thisIsChrome=" + isChrome + "&thisIsHTA=" + isHTA;
if (browserVersion.isSafari) {
- // safari doesn't reload the page when the location equals to current location.
- // hence, set the location to blank so that the page will reload automatically.
- this.frame.src = "about:blank";
+ // safari doesn't reload the page when the location equals to current location.
+ // hence, set the location to blank so that the page will reload automatically.
+ this.frame.src = "about:blank";
this.frame.src = location;
} else {
this.frame.contentWindow.location.replace(location);
@@ -189,18 +226,27 @@ Object.extend(SeleniumFrame.prototype, {
});
+/** HtmlTestSuiteFrame - encapsulates the suite iframe element */
+var HtmlTestSuiteFrame = classCreate();
+objectExtend(HtmlTestSuiteFrame.prototype, SeleniumFrame.prototype);
+objectExtend(HtmlTestSuiteFrame.prototype, {
+
+ getCurrentTestSuite: function() {
+ if (!this.currentTestSuite) {
+ this.currentTestSuite = new HtmlTestSuite(this.getDocument());
+ }
+ return this.currentTestSuite;
+ }
+
+});
+
/** HtmlTestFrame - encapsulates the test-case iframe element */
-var HtmlTestFrame = Class.create();
-Object.extend(HtmlTestFrame.prototype, SeleniumFrame.prototype);
-Object.extend(HtmlTestFrame.prototype, {
+var HtmlTestFrame = classCreate();
+objectExtend(HtmlTestFrame.prototype, SeleniumFrame.prototype);
+objectExtend(HtmlTestFrame.prototype, {
_onLoad: function() {
- this.setCurrentTestCase();
- },
-
- setCurrentTestCase: function() {
- //todo: this is not good looking
- this.currentTestCase = new HtmlTestCase(this.getDocument(), htmlTestRunner.htmlTestSuite.getCurrentRow());
+ this.currentTestCase = new HtmlTestCase(this.getDocument(), htmlTestRunner.getTestSuite().getCurrentRow());
},
getCurrentTestCase: function() {
@@ -210,14 +256,14 @@ Object.extend(HtmlTestFrame.prototype, {
});
function onSeleniumLoad() {
- suiteFrame = new SeleniumFrame(getSuiteFrame());
+ suiteFrame = new HtmlTestSuiteFrame(getSuiteFrame());
testFrame = new HtmlTestFrame(getTestFrame());
htmlTestRunner = new HtmlTestRunner();
}
-
var suiteFrame;
var testFrame;
+
function getSuiteFrame() {
var f = $('testSuiteFrame');
if (f == null) {
@@ -236,9 +282,9 @@ function getTestFrame() {
return f;
}
-var HtmlTestRunnerControlPanel = Class.create();
-Object.extend(HtmlTestRunnerControlPanel.prototype, URLConfiguration.prototype);
-Object.extend(HtmlTestRunnerControlPanel.prototype, {
+var HtmlTestRunnerControlPanel = classCreate();
+objectExtend(HtmlTestRunnerControlPanel.prototype, URLConfiguration.prototype);
+objectExtend(HtmlTestRunnerControlPanel.prototype, {
initialize: function() {
this._acquireQueryString();
@@ -248,16 +294,17 @@ Object.extend(HtmlTestRunnerControlPanel.prototype, {
this.pauseButton = $('pauseTest');
this.stepButton = $('stepTest');
- this.highlightOption.onclick = (function() {
+ this.highlightOption.onclick = fnBindAsEventListener((function() {
this.setHighlightOption();
- }).bindAsEventListener(this);
- this.pauseButton.onclick = this.pauseCurrentTest.bindAsEventListener(this);
- this.stepButton.onclick = this.stepCurrentTest.bindAsEventListener(this);
+ }), this);
+ this.pauseButton.onclick = fnBindAsEventListener(this.pauseCurrentTest, this);
+ this.stepButton.onclick = fnBindAsEventListener(this.stepCurrentTest, this);
+
this.speedController = new Control.Slider('speedHandle', 'speedTrack', {
range: $R(0, 1000),
- onSlide: this.setRunInterval.bindAsEventListener(this),
- onChange: this.setRunInterval.bindAsEventListener(this)
+ onSlide: fnBindAsEventListener(this.setRunInterval, this),
+ onChange: fnBindAsEventListener(this.setRunInterval, this)
});
this._parseQueryParameter();
@@ -265,7 +312,7 @@ Object.extend(HtmlTestRunnerControlPanel.prototype, {
setHighlightOption: function () {
var isHighlight = this.highlightOption.checked;
- selenium.browserbot.getCurrentPage().setHighlightElement(isHighlight);
+ selenium.browserbot.setShouldHighlightElement(isHighlight);
},
_parseQueryParameter: function() {
@@ -295,19 +342,19 @@ Object.extend(HtmlTestRunnerControlPanel.prototype, {
},
reset: function() {
- this.runInterval = this.speedController.value;
+ // this.runInterval = this.speedController.value;
this._switchContinueButtonToPause();
},
_switchContinueButtonToPause: function() {
- this.pauseButton.innerHTML = "Pause";
- this.pauseButton.onclick = this.pauseCurrentTest.bindAsEventListener(this);
+ this.pauseButton.className = "cssPauseTest";
+ this.pauseButton.onclick = fnBindAsEventListener(this.pauseCurrentTest, this);
},
_switchPauseButtonToContinue: function() {
$('stepTest').disabled = false;
- this.pauseButton.innerHTML = "Continue";
- this.pauseButton.onclick = this.continueCurrentTest.bindAsEventListener(this);
+ this.pauseButton.className = "cssContinueTest";
+ this.pauseButton.onclick = fnBindAsEventListener(this.continueCurrentTest, this);
},
stepCurrentTest: function () {
@@ -356,36 +403,58 @@ Object.extend(HtmlTestRunnerControlPanel.prototype, {
});
-
-var AbstractResultAwareRow = Class.create();
-Object.extend(AbstractResultAwareRow.prototype, {
+var AbstractResultAwareRow = classCreate();
+objectExtend(AbstractResultAwareRow.prototype, {
initialize: function(trElement) {
this.trElement = trElement;
},
- markWorking: function() {
- this.trElement.bgColor = FeedbackColors.workingColor;
+ setStatus: function(status) {
+ this.unselect();
+ this.trElement.className = this.trElement.className.replace(/status_[a-z]+/, "");
+ if (status) {
+ addClassName(this.trElement, "status_" + status);
+ }
+ },
+
+ select: function() {
+ addClassName(this.trElement, "selected");
safeScrollIntoView(this.trElement);
},
+ unselect: function() {
+ removeClassName(this.trElement, "selected");
+ },
+
markPassed: function() {
- this.trElement.bgColor = FeedbackColors.passColor;
+ this.setStatus("passed");
},
markDone: function() {
- this.trElement.bgColor = FeedbackColors.doneColor;
+ this.setStatus("done");
},
markFailed: function() {
- this.trElement.bgColor = FeedbackColors.failColor;
+ this.setStatus("failed");
+ }
+
+});
+
+var TitleRow = classCreate();
+objectExtend(TitleRow.prototype, AbstractResultAwareRow.prototype);
+objectExtend(TitleRow.prototype, {
+
+ initialize: function(trElement) {
+ this.trElement = trElement;
+ trElement.className = "title";
}
});
-var HtmlTestCaseRow = Class.create();
-Object.extend(HtmlTestCaseRow.prototype, AbstractResultAwareRow.prototype);
-Object.extend(HtmlTestCaseRow.prototype, {
+var HtmlTestCaseRow = classCreate();
+objectExtend(HtmlTestCaseRow.prototype, AbstractResultAwareRow.prototype);
+objectExtend(HtmlTestCaseRow.prototype, {
getCommand: function () {
return new SeleniumCommand(getText(this.trElement.cells[0]),
@@ -395,16 +464,16 @@ Object.extend(HtmlTestCaseRow.prototype, {
},
markFailed: function(errorMsg) {
- this.trElement.bgColor = FeedbackColors.failColor;
+ AbstractResultAwareRow.prototype.markFailed.call(this, errorMsg);
this.setMessage(errorMsg);
},
setMessage: function(message) {
- this.trElement.cells[2].innerHTML = message;
+ setText(this.trElement.cells[2], message);
},
reset: function() {
- this.trElement.bgColor = '';
+ this.setStatus(null);
var thirdCell = this.trElement.cells[2];
if (thirdCell) {
if (thirdCell.originalHTML) {
@@ -418,19 +487,18 @@ Object.extend(HtmlTestCaseRow.prototype, {
onClick: function() {
if (this.trElement.isBreakpoint == undefined) {
this.trElement.isBreakpoint = true;
- this.trElement.beforeBackgroundColor = Element.getStyle(this.trElement, "backgroundColor");
- Element.setStyle(this.trElement, {"background-color" : FeedbackColors.breakpointColor});
+ addClassName(this.trElement, "breakpoint");
} else {
this.trElement.isBreakpoint = undefined;
- Element.setStyle(this.trElement, {"background-color" : this.trElement.beforeBackgroundColor});
+ removeClassName(this.trElement, "breakpoint");
}
},
addBreakpointSupport: function() {
- Element.setStyle(this.trElement, {"cursor" : "pointer"});
- this.trElement.onclick = function() {
+ elementSetStyle(this.trElement, {"cursor" : "pointer"});
+ this.trElement.onclick = fnBindAsEventListener(function() {
this.onClick();
- }.bindAsEventListener(this);
+ }, this);
},
isBreakpoint: function() {
@@ -441,42 +509,44 @@ Object.extend(HtmlTestCaseRow.prototype, {
}
});
-var HtmlTestSuiteRow = Class.create();
-Object.extend(HtmlTestSuiteRow.prototype, AbstractResultAwareRow.prototype);
-Object.extend(HtmlTestSuiteRow.prototype, {
+var HtmlTestSuiteRow = classCreate();
+objectExtend(HtmlTestSuiteRow.prototype, AbstractResultAwareRow.prototype);
+objectExtend(HtmlTestSuiteRow.prototype, {
initialize: function(trElement, testFrame, htmlTestSuite) {
this.trElement = trElement;
this.testFrame = testFrame;
this.htmlTestSuite = htmlTestSuite;
this.link = trElement.getElementsByTagName("a")[0];
- this.link.onclick = this._onClick.bindAsEventListener(this);
+ this.link.onclick = fnBindAsEventListener(this._onClick, this);
},
reset: function() {
- this.trElement.bgColor = '';
+ this.setStatus(null);
},
_onClick: function() {
- // todo: just send a message to the testSuite
this.loadTestCase(null);
return false;
},
loadTestCase: function(onloadFunction) {
+ this.htmlTestSuite.unselectCurrentRow();
+ this.select();
this.htmlTestSuite.currentRowInSuite = this.trElement.rowIndex - 1;
// If the row has a stored results table, use that
var resultsFromPreviousRun = this.trElement.cells[1];
if (resultsFromPreviousRun) {
- // this.testFrame.restoreTestCase(resultsFromPreviousRun.innerHTML);
+ // todo: delegate to TestFrame, e.g.
+ // this.testFrame.restoreTestCase(resultsFromPreviousRun.innerHTML);
var testBody = this.testFrame.getDocument().body;
testBody.innerHTML = resultsFromPreviousRun.innerHTML;
- testFrame.setCurrentTestCase();
+ this.testFrame._onLoad();
if (onloadFunction) {
onloadFunction();
}
} else {
- this.testFrame.load(this.link.href, onloadFunction);
+ this.testFrame.load(this.link.href, onloadFunction);
}
},
@@ -498,28 +568,24 @@ Object.extend(HtmlTestSuiteRow.prototype, {
});
-var HtmlTestSuite = Class.create();
-Object.extend(HtmlTestSuite.prototype, {
+var HtmlTestSuite = classCreate();
+objectExtend(HtmlTestSuite.prototype, {
initialize: function(suiteDocument) {
this.suiteDocument = suiteDocument;
this.suiteRows = this._collectSuiteRows();
- this.titleRow = this.getTestTable().rows[0];
- this.title = this.titleRow.cells[0].innerHTML;
+ this.titleRow = new TitleRow(this.getTestTable().rows[0]);
this.reset();
},
reset: function() {
this.failed = false;
this.currentRowInSuite = -1;
- this.titleRow.bgColor = '';
- this.suiteRows.each(function(row) {
+ this.titleRow.setStatus(null);
+ for (var i = 0; i < this.suiteRows.length; i++) {
+ var row = this.suiteRows[i];
row.reset();
- });
- },
-
- getTitle: function() {
- return this.title;
+ }
},
getSuiteRows: function() {
@@ -537,31 +603,51 @@ Object.extend(HtmlTestSuite.prototype, {
_collectSuiteRows: function () {
var result = [];
- for (rowNum = 1; rowNum < this.getTestTable().rows.length; rowNum++) {
- var rowElement = this.getTestTable().rows[rowNum];
+ var tables = $A(this.suiteDocument.getElementsByTagName("table"));
+ var testTable = tables[0];
+ for (rowNum = 1; rowNum < testTable.rows.length; rowNum++) {
+ var rowElement = testTable.rows[rowNum];
result.push(new HtmlTestSuiteRow(rowElement, testFrame, this));
}
+
+ // process the unsuited rows as well
+ for (var tableNum = 1; tableNum < $A(this.suiteDocument.getElementsByTagName("table")).length; tableNum++) {
+ testTable = tables[tableNum];
+ for (rowNum = 1; rowNum < testTable.rows.length; rowNum++) {
+ var rowElement = testTable.rows[rowNum];
+ new HtmlTestSuiteRow(rowElement, testFrame, this);
+ }
+ }
return result;
},
getCurrentRow: function() {
+ if (this.currentRowInSuite == -1) {
+ return null;
+ }
return this.suiteRows[this.currentRowInSuite];
},
+ unselectCurrentRow: function() {
+ var currentRow = this.getCurrentRow()
+ if (currentRow) {
+ currentRow.unselect();
+ }
+ },
+
markFailed: function() {
this.failed = true;
- this.titleRow.bgColor = FeedbackColors.failColor;
+ this.titleRow.markFailed();
},
markDone: function() {
if (!this.failed) {
- this.titleRow.bgColor = FeedbackColors.passColor;
+ this.titleRow.markPassed();
}
},
_startCurrentTestCase: function() {
- this.getCurrentRow().markWorking();
- this.getCurrentRow().loadTestCase(htmlTestRunner.startTest.bind(htmlTestRunner));
+ this.getCurrentRow().loadTestCase(fnBind(htmlTestRunner.startTest, htmlTestRunner));
},
_onTestSuiteComplete: function() {
@@ -569,14 +655,13 @@ Object.extend(HtmlTestSuite.prototype, {
new TestResult(this.failed, this.getTestTable()).post();
},
- _updateSuiteWithResultOfPreviousTest: function() {
+ updateSuiteWithResultOfPreviousTest: function() {
if (this.currentRowInSuite >= 0) {
this.getCurrentRow().saveTestResults();
}
},
runNextTestInSuite: function() {
- this._updateSuiteWithResultOfPreviousTest();
this.currentRowInSuite++;
// If we are done with all of the tests, set the title bar as pass or fail
@@ -591,8 +676,8 @@ Object.extend(HtmlTestSuite.prototype, {
});
-var TestResult = Class.create();
-Object.extend(TestResult.prototype, {
+var TestResult = classCreate();
+objectExtend(TestResult.prototype, {
// Post the results to a servlet, CGI-script, etc. The URL of the
// results-handler defaults to "/postResults", but an alternative location
@@ -724,8 +809,8 @@ Object.extend(TestResult.prototype, {
});
/** HtmlTestCase encapsulates an HTML test document */
-var HtmlTestCase = Class.create();
-Object.extend(HtmlTestCase.prototype, {
+var HtmlTestCase = classCreate();
+objectExtend(HtmlTestCase.prototype, {
initialize: function(testDocument, htmlTestSuiteRow) {
if (testDocument == null) {
@@ -736,6 +821,7 @@ Object.extend(HtmlTestCase.prototype, {
}
this.testDocument = testDocument;
this.htmlTestSuiteRow = htmlTestSuiteRow;
+ this.headerRow = new TitleRow(this.testDocument.getElementsByTagName("tr")[0]);
this.commandRows = this._collectCommandRows();
this.nextCommandRowIndex = 0;
this._addBreakpointSupport();
@@ -745,13 +831,16 @@ Object.extend(HtmlTestCase.prototype, {
var commandRows = [];
var tables = $A(this.testDocument.getElementsByTagName("table"));
var self = this;
- tables.each(function (table) {
- $A(table.rows).each(function(candidateRow) {
+ for (var i = 0; i < tables.length; i++) {
+ var table = tables[i];
+ var tableRows = $A(table.rows);
+ for (var j = 0; j < tableRows.length; j++) {
+ var candidateRow = tableRows[j];
if (self.isCommandRow(candidateRow)) {
commandRows.push(new HtmlTestCaseRow(candidateRow));
}
- }.bind(this));
- });
+ }
+ }
return commandRows;
},
@@ -765,15 +854,16 @@ Object.extend(HtmlTestCase.prototype, {
*/
this.nextCommandRowIndex = 0;
- this._setTitleColor('');
- this.commandRows.each(function(row) {
+ this.setStatus('');
+ for (var i = 0; i < this.commandRows.length; i++) {
+ var row = this.commandRows[i];
row.reset();
- });
+ }
// remove any additional fake "error" row added to the end of the document
var errorElement = this.testDocument.getElementById('error');
if (errorElement) {
- Element.remove(errorElement);
+ errorElement.parentNode.removeChild(errorElement);
}
},
@@ -781,39 +871,38 @@ Object.extend(HtmlTestCase.prototype, {
return this.commandRows;
},
- _setTitleColor: function(color) {
- var headerRow = this.testDocument.getElementsByTagName("tr")[0];
- if (headerRow) {
- headerRow.bgColor = color;
- }
+ setStatus: function(status) {
+ this.headerRow.setStatus(status);
},
markFailed: function() {
- this._setTitleColor(FeedbackColors.failColor);
+ this.setStatus("failed");
this.htmlTestSuiteRow.markFailed();
},
markPassed: function() {
- this._setTitleColor(FeedbackColors.passColor);
+ this.setStatus("passed");
this.htmlTestSuiteRow.markPassed();
},
addErrorMessage: function(errorMsg, currentRow) {
+ errorMsg = errorMsg.replace(/ /g, String.fromCharCode(160)).replace("\n", '\\n');
if (currentRow) {
currentRow.markFailed(errorMsg);
} else {
var errorElement = this.testDocument.createElement("p");
errorElement.id = "error";
- errorElement.innerHTML = errorMsg;
+ setText(errorElement, errorMsg);
this.testDocument.body.appendChild(errorElement);
- Element.setStyle(errorElement, {'backgroundColor': FeedbackColors.failColor});
+ errorElement.className = "status_failed";
}
},
_addBreakpointSupport: function() {
- this.commandRows.each(function(row) {
+ for (var i = 0; i < this.commandRows.length; i++) {
+ var row = this.commandRows[i];
row.addBreakpointSupport();
- });
+ }
},
hasMoreCommandRows: function() {
@@ -850,8 +939,8 @@ var get_new_rows = function() {
};
-var Metrics = Class.create();
-Object.extend(Metrics.prototype, {
+var Metrics = classCreate();
+objectExtend(Metrics.prototype, {
initialize: function() {
// The number of tests run
this.numTestPasses = 0;
@@ -903,20 +992,13 @@ Object.extend(Metrics.prototype, {
});
-var HtmlRunnerCommandFactory = Class.create();
-Object.extend(HtmlRunnerCommandFactory.prototype, {
+var HtmlRunnerCommandFactory = classCreate();
+objectExtend(HtmlRunnerCommandFactory.prototype, {
initialize: function(seleniumCommandFactory, testLoop) {
this.seleniumCommandFactory = seleniumCommandFactory;
this.testLoop = testLoop;
- this.handlers = {
- pause: {
- execute: function(selenium, command) {
- testLoop.pauseInterval = command.target;
- return {};
- }
- }
- };
+ this.handlers = {};
//todo: register commands
},
@@ -929,9 +1011,9 @@ Object.extend(HtmlRunnerCommandFactory.prototype, {
});
-var HtmlRunnerTestLoop = Class.create();
-Object.extend(HtmlRunnerTestLoop.prototype, new TestLoop());
-Object.extend(HtmlRunnerTestLoop.prototype, {
+var HtmlRunnerTestLoop = classCreate();
+objectExtend(HtmlRunnerTestLoop.prototype, new TestLoop());
+objectExtend(HtmlRunnerTestLoop.prototype, {
initialize: function(htmlTestCase, metrics, seleniumCommandFactory) {
this.commandFactory = new HtmlRunnerCommandFactory(seleniumCommandFactory, this);
@@ -948,6 +1030,8 @@ Object.extend(HtmlRunnerTestLoop.prototype, {
// used for selenium tests in javascript
this.currentItem = null;
this.commandAgenda = new Array();
+ this.expectedFailure = null;
+ this.expectedFailureType = null;
this.htmlTestCase.reset();
@@ -986,11 +1070,12 @@ Object.extend(HtmlRunnerTestLoop.prototype, {
commandStarted : function() {
$('pauseTest').disabled = false;
- this.currentRow.markWorking();
+ this.currentRow.select();
this.metrics.printMetrics();
},
commandComplete : function(result) {
+ this._checkExpectedFailure(result);
if (result.failed) {
this.metrics.numCommandFailures += 1;
this._recordFailure(result.failureMessage);
@@ -1002,7 +1087,49 @@ Object.extend(HtmlRunnerTestLoop.prototype, {
}
},
+ _checkExpectedFailure : function(result) {
+ if (this.expectedFailure != null) {
+ if (this.expectedFailureJustSet) {
+ this.expectedFailureJustSet = false;
+ return;
+ }
+ if (!result.failed) {
+ result.passed = false;
+ result.failed = true;
+ result.failureMessage = "Expected " + this.expectedFailureType + " did not occur.";
+ } else {
+ if (PatternMatcher.matches(this.expectedFailure, result.failureMessage)) {
+ var failureType = result.error ? "error" : "failure";
+ if (failureType == this.expectedFailureType) {
+ result.failed = false;
+ result.passed = true;
+ } else {
+ result.failed = true;
+ result.failureMessage = "Expected "+this.expectedFailureType+", but "+failureType+" occurred instead";
+ }
+ } else {
+ result.failed = true;
+ result.failureMessage = "Expected " + this.expectedFailureType + " message '" + this.expectedFailure
+ + "' but was '" + result.failureMessage + "'";
+ }
+ }
+ this.expectedFailure = null;
+ this.expectedFailureType = null;
+ }
+ },
+
commandError : function(errorMessage) {
+ var tempResult = {};
+ tempResult.passed = false;
+ tempResult.failed = true;
+ tempResult.error = true;
+ tempResult.failureMessage = errorMessage;
+ this._checkExpectedFailure(tempResult);
+ if (tempResult.passed) {
+ this.currentRow.markDone();
+ return true;
+ }
+ errorMessage = tempResult.failureMessage;
this.metrics.numCommandErrors += 1;
this._recordFailure(errorMessage);
},
@@ -1062,6 +1189,13 @@ Object.extend(HtmlRunnerTestLoop.prototype, {
});
+Selenium.prototype.doPause = function(waitTime) {
+ /** Wait for the specified amount of time (in milliseconds)
+ * @param waitTime the amount of time to sleep (in milliseconds)
+ */
+ // todo: should not refer to currentTest directly
+ currentTest.pauseInterval = waitTime;
+};
Selenium.prototype.doBreak = function() {
/** Halt the currently running test, and wait for the user to press the Continue button.
@@ -1115,81 +1249,33 @@ Selenium.prototype.assertSelected = function(selectLocator, optionLocator) {
locator.assertSelected(element);
};
-/**
- * Tell Selenium to expect a failure on the next command execution. This
- * command temporarily installs a CommandFactory that generates
- * CommandHandlers that expect a failure.
- */
Selenium.prototype.assertFailureOnNext = function(message) {
+ /**
+ * Tell Selenium to expect a failure on the next command execution.
+ * @param message The failure message we should expect. This command will fail if the wrong failure message appears.
+ */
if (!message) {
- throw new Error("Message must be provided");
+ throw new SeleniumError("Message must be provided");
}
- var expectFailureCommandFactory =
- new ExpectFailureCommandFactory(currentTest.commandFactory, message, "failure", executeCommandAndReturnFailureMessage);
- currentTest.commandFactory = expectFailureCommandFactory;
+ currentTest.expectedFailure = message;
+ currentTest.expectedFailureType = "failure";
+ currentTest.expectedFailureJustSet = true;
};
-/**
- * Tell Selenium to expect an error on the next command execution. This
- * command temporarily installs a CommandFactory that generates
- * CommandHandlers that expect a failure.
- */
Selenium.prototype.assertErrorOnNext = function(message) {
+ /**
+ * Tell Selenium to expect an error on the next command execution.
+ * @param message The error message we should expect. This command will fail if the wrong error message appears.
+ */
+ // This command temporarily installs a CommandFactory that generates
+ // CommandHandlers that expect an error.
if (!message) {
- throw new Error("Message must be provided");
+ throw new SeleniumError("Message must be provided");
}
- var expectFailureCommandFactory =
- new ExpectFailureCommandFactory(currentTest.commandFactory, message, "error", executeCommandAndReturnErrorMessage);
- currentTest.commandFactory = expectFailureCommandFactory;
-};
-
-function executeCommandAndReturnFailureMessage(baseHandler, originalArguments) {
- var baseResult = baseHandler.execute.apply(baseHandler, originalArguments);
- if (baseResult.passed) {
- return null;
- }
- return baseResult.failureMessage;
-};
-
-function executeCommandAndReturnErrorMessage(baseHandler, originalArguments) {
- try {
- baseHandler.execute.apply(baseHandler, originalArguments);
- return null;
- }
- catch (expected) {
- return expected.message;
- }
+ currentTest.expectedFailure = message;
+ currentTest.expectedFailureType = "error";
+ currentTest.expectedFailureJustSet = true;
};
-function ExpectFailureCommandHandler(baseHandler, originalCommandFactory, expectedErrorMessage, errorType, decoratedExecutor) {
- this.execute = function() {
- var baseFailureMessage = decoratedExecutor(baseHandler, arguments);
- var result = {};
- if (!baseFailureMessage) {
- result.failed = true;
- result.failureMessage = "Expected " + errorType + " did not occur.";
- }
- else {
- if (! PatternMatcher.matches(expectedErrorMessage, baseFailureMessage)) {
- result.failed = true;
- result.failureMessage = "Expected " + errorType + " message '" + expectedErrorMessage
- + "' but was '" + baseFailureMessage + "'";
- }
- else {
- result.passed = true;
- result.result = baseFailureMessage;
- }
- }
- currentTest.commandFactory = originalCommandFactory;
- return result;
- };
-}
-
-function ExpectFailureCommandFactory(originalCommandFactory, expectedErrorMessage, errorType, decoratedExecutor) {
- this.getCommandHandler = function(name) {
- var baseHandler = originalCommandFactory.getCommandHandler(name);
- return new ExpectFailureCommandHandler(baseHandler, originalCommandFactory, expectedErrorMessage, errorType, decoratedExecutor);
- };
-};
diff --git a/tests/test_tools/selenium/core/scripts/selenium-version.js b/tests/test_tools/selenium/core/scripts/selenium-version.js
index c4a5508c..4156723d 100644
--- a/tests/test_tools/selenium/core/scripts/selenium-version.js
+++ b/tests/test_tools/selenium/core/scripts/selenium-version.js
@@ -1,5 +1,5 @@
-Selenium.version = "0.8.0";
-Selenium.revision = "1472:1473";
+Selenium.version = "0.8.2";
+Selenium.revision = "1727";
window.top.document.title += " v" + Selenium.version + " [" + Selenium.revision + "]";
diff --git a/tests/test_tools/selenium/core/selenium.css b/tests/test_tools/selenium/core/selenium.css
index 963c63ad..f5240822 100644
--- a/tests/test_tools/selenium/core/selenium.css
+++ b/tests/test_tools/selenium/core/selenium.css
@@ -17,12 +17,12 @@
/*---( Layout )---*/
* {
- margin: 0px;
- padding: 0px;
}
body {
overflow: auto;
+ margin: 0px;
+ padding: 0px;
}
td {
@@ -84,9 +84,6 @@ body, html {
color: black;
}
-#controlPanel button {
- margin: 0.5ex;
-}
#controlPanel table {
font-size: 100%;
@@ -206,33 +203,6 @@ button, label {
color: green;
}
-table.selenium {
- font-family: Verdana, Arial, sans-serif;
- font-size: 12;
- border-width: 1px 1px 1px 1px;
- border-spacing: 2px;
- border-style: solid none solid none;
- border-color: gray gray gray gray;
- border-collapse: separate;
- background-color: white;
-}
-
-table.selenium th {
- border-width: 1px 1px 1px 1px;
- padding: 1px 1px 1px 1px;
- border-style: none none none none;
- border-color: gray gray gray gray;
- -moz-border-radius: 0px 0px 0px 0px;
-}
-
-table.selenium td {
- border-width: 1px 1px 1px 1px;
- padding: 1px 1px 1px 1px;
- border-style: none none none none;
- border-color: gray gray gray gray;
- -moz-border-radius: 0px 0px 0px 0px;
-}
-
div.executionOptions {
padding-left: 5em;
}
@@ -259,10 +229,7 @@ div.executionOptions br {
background-color: #333;
width: 260px;
height: 2px;
- <!--[if IE]>
- height: 2px;
- line-height: 2px;
- <![endif]-->
+ line-height: 2px;
z-index: 1;
border: 1px solid;
border-color: #999 #ddd #ddd #999;
@@ -276,10 +243,7 @@ div.executionOptions br {
position: relative;
margin: 0px;
height: 8px;
- <!--[if IE]>
- height: 8px;
- line-height: 8px;
- <![endif]-->
+ line-height: 8px;
z-index: 1;
border: 1px solid;
border-color: #999 #333 #333 #999;
diff --git a/tests/test_tools/selenium/php/TestRunner.php b/tests/test_tools/selenium/php/TestRunner.php
index 54e46647..ec2e32d3 100644
--- a/tests/test_tools/selenium/php/TestRunner.php
+++ b/tests/test_tools/selenium/php/TestRunner.php
@@ -29,13 +29,13 @@ to work-around a bug in IE on Win2K whereby the HTA application doesn't function
<script type="text/javascript" src="<?php echo $base_dir; ?>core/scripts/narcissus-defs.js"></script>
<script type="text/javascript" src="<?php echo $base_dir; ?>core/scripts/narcissus-parse.js"></script>
<script type="text/javascript" src="<?php echo $base_dir; ?>core/scripts/narcissus-exec.js"></script>
+ <script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>core/scripts/htmlutils.js"></script>
<script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>core/lib/prototype.js"></script>
<script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>core/lib/scriptaculous/scriptaculous.js"></script>
<script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>core/lib/cssQuery/cssQuery-p.js"></script>
<script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>core/scripts/selenium-browserdetect.js"></script>
<script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>core/scripts/selenium-browserbot.js"></script>
<script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>core/scripts/find_matching_child.js"></script>
- <script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>core/scripts/htmlutils.js"></script>
<script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>core/scripts/selenium-api.js"></script>
<script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>core/scripts/selenium-commandhandlers.js"></script>
<script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>core/scripts/selenium-executionloop.js"></script>
@@ -49,6 +49,7 @@ to work-around a bug in IE on Win2K whereby the HTA application doesn't function
<script language="JavaScript" type="text/javascript">
var post_results_to = "<?php echo $driver; ?>";
+ var script_base_url = "<?php echo $base_dir; ?>";
</script>
</head>
diff --git a/tests/test_tools/selenium/php/selenium.php b/tests/test_tools/selenium/php/selenium.php
index bfba617a..32b773db 100644
--- a/tests/test_tools/selenium/php/selenium.php
+++ b/tests/test_tools/selenium/php/selenium.php
@@ -142,7 +142,7 @@ class SeleneseInterpreter
$command = array($func, $ID, $value);
if(is_int(strpos(strtolower($func), 'visible')))
- $this->addCommand(array('pause','500',''),$trace);
+ $this->addCommand(array('pause','250',''),$trace);
return $this->addCommand($command, $trace);
}
diff --git a/tests/test_tools/selenium/prado-functional-test.js b/tests/test_tools/selenium/prado-functional-test.js
index 4cda378c..9d4446f0 100644
--- a/tests/test_tools/selenium/prado-functional-test.js
+++ b/tests/test_tools/selenium/prado-functional-test.js
@@ -1,19 +1,45 @@
-Object.extend(HtmlTestRunner.prototype, {
- loadSuiteFrame: function() {
- if (selenium == null) {
- selenium = Selenium.createForWindow(this._getApplicationWindow());
- this._registerCommandHandlers();
- }
- this.controlPanel.setHighlightOption();
- //var testSuiteName = this.controlPanel.getTestSuiteName();
- var testSuiteName = document.location+'?testSuites';
- if (testSuiteName) {
- suiteFrame.load(testSuiteName, this._onloadTestSuite.bind(this));
- }
+
+objectExtend(HtmlTestRunnerControlPanel.prototype, {
+ getTestSuiteName: function() {
+ return document.location+'?testSuites'; //this._getQueryParameter("test");
}
});
-Object.extend(HtmlTestRunnerControlPanel.prototype, {
+SeleniumFrame.prototype._setLocation = function(location) {
+ /* var isChrome = browserVersion.isChrome || false;
+ var isHTA = browserVersion.isHTA || false;
+ // DGF TODO multiWindow
+ location += "?thisIsChrome=" + isChrome + "&thisIsHTA=" + isHTA;*/
+ if (browserVersion.isSafari) {
+ // safari doesn't reload the page when the location equals to current location.
+ // hence, set the location to blank so that the page will reload automatically.
+ this.frame.src = "about:blank";
+ this.frame.src = location;
+ } else {
+ this.frame.contentWindow.location.replace(location);
+ }
+ };
+
+SeleniumFrame.prototype._attachStylesheet = function()
+{
+ var base_url = script_base_url;
+ var d = this.getDocument();
+ var head = d.getElementsByTagName('head').item(0);
+ var styleLink = d.createElement("link");
+ styleLink.rel = "stylesheet";
+ styleLink.type = "text/css";
+ styleLink.href = base_url + "core/selenium-test.css";
+ head.appendChild(styleLink);
+};
+
+HtmlTestFrame.prototype._setLocation = SeleniumFrame.prototype._setLocation;
+HtmlTestSuiteFrame.prototype._setLocation = SeleniumFrame.prototype._setLocation;
+
+HtmlTestFrame.prototype._attachStylesheet = SeleniumFrame.prototype._attachStylesheet;
+HtmlTestSuiteFrame.prototype._attachStylesheet = SeleniumFrame.prototype._attachStylesheet;
+
+
+objectExtend(HtmlTestRunnerControlPanel.prototype, {
_parseQueryParameter: function() {
var tempRunInterval = this._getQueryParameter("runInterval");
if (tempRunInterval) {
@@ -37,7 +63,7 @@ Selenium.prototype.getAttribute = function(target) {
*/
Selenium.prototype.isVisible = function(locator) {
var element;
- element = this.page().findElement(locator);
+ element = this.page().findElement(locator);
if(/Konqueror|Safari|KHTML/.test(navigator.userAgent))
var visibility = element.style["visibility"];
@@ -64,13 +90,13 @@ Selenium.prototype._isDisplayed = function(element) {
return true;
};
-Selenium.prototype.assertEmptySelection = function(selectLocator, optionLocator)
+Selenium.prototype.assertEmptySelection = function(selectLocator, optionLocator)
{
/**
* Verifies that the selected option of a drop-down satisfies the optionSpecifier.
- *
+ *
* <p>See the select command for more information about option locators.</p>
- *
+ *
* @param selectLocator an <a href="#locators">element locator</a> identifying a drop-down menu
* @param optionLocator an option locator, typically just an option label (e.g. "John Smith")
*/
@@ -78,9 +104,9 @@ Selenium.prototype.assertEmptySelection = function(selectLocator, optionLocator)
var locator = this.optionLocatorFactory.fromLocatorString(optionLocator);
return element.selectedIndex == -1;
}
-
-Object.extend(HtmlTestSuite.prototype, {
+
+objectExtend(HtmlTestSuite.prototype, {
_onTestSuiteComplete: function() {
this.markDone();
var result = new TestResult(this.failed, this.getTestTable());
@@ -89,7 +115,7 @@ Object.extend(HtmlTestSuite.prototype, {
});
-
+
// Post the results to a servlet, CGI-script, etc. The URL of the
// results-handler defaults to "/postResults", but an alternative location
@@ -208,10 +234,10 @@ function parse_resultCell(resultCell,rowNum,form)
function get_color_status(element)
{
- var color = element.getAttribute("bgcolor");
- if(color == FeedbackColors.passColor) return "passed";
- if(color == FeedbackColors.failColor) return "failed";
- if(color == FeedbackColors.doneColor) return "done";
+ var color = element.className
+ if(color == 'status_passed') return "passed";
+ if(color == 'status_failed') return "failed";
+ if(color == 'status_done') return "done";
return "";
}