summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitattributes9
-rw-r--r--buildscripts/jsbuilder/build.php28
-rw-r--r--buildscripts/jsbuilder/jsmin.php668
-rw-r--r--framework/Web/Javascripts/extra/json.js2
-rw-r--r--framework/Web/Javascripts/js/ajax.js2007
-rw-r--r--framework/Web/Javascripts/prado/ajax3.js181
-rw-r--r--framework/Web/Javascripts/prototype/ajax.js2
-rw-r--r--framework/Web/UI/ActiveControls/TActiveControl.php23
-rw-r--r--framework/Web/UI/ActiveControls/TActiveControlAdapter.php25
-rw-r--r--framework/Web/UI/ActiveControls/TActivePageAdapter.php219
-rw-r--r--framework/Web/UI/ActiveControls/TCallbackClientScript.php485
-rw-r--r--framework/Web/UI/ActiveControls/TCallbackResponse.php20
-rw-r--r--framework/Web/UI/TControl.php5
-rw-r--r--framework/Web/UI/TPage.php163
-rw-r--r--framework/Web/UI/TTemplateControl.php2
-rw-r--r--framework/Web/UI/WebControls/TWizard.php4
-rw-r--r--framework/interfaces.php37
-rw-r--r--tests/FunctionalTests/features/protected/pages/ActiveControls/ActiveControl.page17
-rw-r--r--tests/FunctionalTests/features/protected/pages/ActiveControls/config.xml7
19 files changed, 1924 insertions, 1980 deletions
diff --git a/.gitattributes b/.gitattributes
index df9af126..ea8660ec 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -6,6 +6,7 @@
buildscripts/.htaccess -text
buildscripts/jsbuilder/JavaScript[!!-~]Documentation[!!-~]Tool.html -text
buildscripts/jsbuilder/build.php -text
+buildscripts/jsbuilder/jsmin.php -text
buildscripts/phing/style/coverage-frames.xsl -text
buildscripts/phing/style/log.xsl -text
buildscripts/phing/style/phpunit2-frames.xsl -text
@@ -880,6 +881,7 @@ framework/Web/Javascripts/js/rico.js -text
framework/Web/Javascripts/js/validator.js -text
framework/Web/Javascripts/prado/activecontrols.js -text
framework/Web/Javascripts/prado/ajax.js -text
+framework/Web/Javascripts/prado/ajax3.js -text
framework/Web/Javascripts/prado/controls.js -text
framework/Web/Javascripts/prado/element.js -text
framework/Web/Javascripts/prado/form.js -text
@@ -924,6 +926,11 @@ framework/Web/THttpRequest.php -text
framework/Web/THttpResponse.php -text
framework/Web/THttpSession.php -text
framework/Web/THttpUtility.php -text
+framework/Web/UI/ActiveControls/TActiveControl.php -text
+framework/Web/UI/ActiveControls/TActiveControlAdapter.php -text
+framework/Web/UI/ActiveControls/TActivePageAdapter.php -text
+framework/Web/UI/ActiveControls/TCallbackClientScript.php -text
+framework/Web/UI/ActiveControls/TCallbackResponse.php -text
framework/Web/UI/TClientScriptManager.php -text
framework/Web/UI/TControl.php -text
framework/Web/UI/TControlAdapter.php -text
@@ -1025,6 +1032,8 @@ tests/FunctionalTests/features/protected/controls/LabeledTextBox.tpl -text
tests/FunctionalTests/features/protected/controls/LabeledTextbox.php -text
tests/FunctionalTests/features/protected/controls/Layout.php -text
tests/FunctionalTests/features/protected/controls/Layout.tpl -text
+tests/FunctionalTests/features/protected/pages/ActiveControls/ActiveControl.page -text
+tests/FunctionalTests/features/protected/pages/ActiveControls/config.xml -text
tests/FunctionalTests/features/protected/pages/ColorPicker.page -text
tests/FunctionalTests/features/protected/pages/CompositeControl.page -text
tests/FunctionalTests/features/protected/pages/DatePicker.page -text
diff --git a/buildscripts/jsbuilder/build.php b/buildscripts/jsbuilder/build.php
index cc41c963..7070f67f 100644
--- a/buildscripts/jsbuilder/build.php
+++ b/buildscripts/jsbuilder/build.php
@@ -45,6 +45,8 @@ if(SOURCE_DIR===false || TARGET_DIR===false)
if(!is_writable(TARGET_DIR))
die('Unable to create files under '.TARGET_DIR.'.');
+include(dirname(__FILE__).'/jsmin.php');
+
/**
* list of js library files to be compressed and built
*/
@@ -98,12 +100,12 @@ $libraries = array(
//active controls
'ajax.js' => array(
'prototype/ajax.js',
- 'prado/ajax.js',
- 'extra/json.js',
- 'effects/controls.js',
+ 'prado/ajax3.js',
+ 'extra/json.js'
+/* 'effects/controls.js',
'effects/dragdrop.js',
'effects/slider.js',
- 'prado/activecontrols.js'
+ 'prado/activecontrols.js'*/
),
//logging
'logger.js' => array(
@@ -161,8 +163,12 @@ foreach($libraries as $libFile => $sourceFiles)
echo "...adding $sourceFile\n";
$contents.=file_get_contents($sourceFile)."\n\n";
}
-
- file_put_contents($libFile,compress_js($contents));
+ $tempFile=$libFile.'.tmp';
+ file_put_contents($tempFile,$contents);
+ $jsMin = new JSMin($tempFile, $libFile);
+ $jsMin -> minify();
+ unset($jsMin);
+ @unlink($tempFile);
echo "Saving file {$libFile}\n";
$builds++;
}
@@ -182,14 +188,4 @@ else if($builds > 0)
else
echo "No files to build.";
-//remove comments from javascript files.
-function compress_js($string)
-{
- $string = preg_replace('/\/\/[^\n\r]*[\n\r]/', ' ', $string);
- $string = preg_replace('/\/\*[^*]*\*+([^\/][^*]*\*+)*\//', ' ', $string);
- $string = preg_replace('/ |\t|\r/', '', $string);
- $string = preg_replace('/(\n[ \t]*){2,}/', "\n", $string);
- return $string;
-}
-
?> \ No newline at end of file
diff --git a/buildscripts/jsbuilder/jsmin.php b/buildscripts/jsbuilder/jsmin.php
new file mode 100644
index 00000000..4abf1b34
--- /dev/null
+++ b/buildscripts/jsbuilder/jsmin.php
@@ -0,0 +1,668 @@
+<?php
+
+/**
+* JSMin.php (for PHP 5)
+*
+* PHP adaptation of JSMin, published by Douglas Crockford as jsmin.c, also based
+* on its Java translation by John Reilly.
+*
+* Permission is hereby granted to use the PHP version under the same conditions
+* as jsmin.c, which has the following notice :
+*
+* ----------------------------------------------------------------------------
+*
+* Copyright (c) 2002 Douglas Crockford (www.crockford.com)
+*
+* 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.
+*
+* The Software shall be used for Good, not Evil.
+*
+* 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.
+*
+* ----------------------------------------------------------------------------
+*
+* @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
+*
+* 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).
+*
+* Note : whereas jsmin.c works specifically with the standard input and output
+* streams, this implementation only falls back on them if file pathnames are
+* not provided to the JSMin() constructor.
+*
+* Examples comparing with the application compiled from jsmin.c :
+*
+* jsmin < orig.js > mini.js JSMin.php orig.js mini.js
+* JSMin.php orig.js > mini.js
+* JSMin.php - mini.js < orig.js
+* jsmin < orig.js JSMin.php orig.js
+* JSMin.php orig.js -
+* jsmin > mini.js JSMin.php - mini.js
+* JSMin.php > mini.js
+* jsmin comm1 comm2 < a.js > b.js JSMin.php a.js b.js comm1 comm2
+* JSMin.php a.js b.js -c comm1 comm2
+* JSMin.php a.js --comm comm1 comm2 > b.js
+* JSMin.php -c comm1 comm2 < a.js > b.js
+* (etc...)
+*
+* See JSMin.php -h (or --help) for command-line documentation.
+*/
+
+/**
+* Version of this PHP translation.
+*/
+
+define('VERSION', '0.1');
+
+/**
+* How fgetc() reports an End Of File.
+* N.B. : use === and not == to test the result of fgetc() ! (see manual)
+*/
+
+define('EOF', FALSE);
+
+/**
+* Some ASCII character ordinals.
+* N.B. : PHP identifiers are case-insensitive !
+*/
+
+define('ORD_NL', ord("\n"));
+define('ORD_space', ord(' '));
+define('ORD_cA', ord('A'));
+define('ORD_cZ', ord('Z'));
+define('ORD_a', ord('a'));
+define('ORD_z', ord('z'));
+define('ORD_0', ord('0'));
+define('ORD_9', ord('9'));
+
+/**
+* Generic exception class related to JSMin.
+*/
+
+class JSMinException extends Exception {
+}
+
+/**
+* A JSMin exception indicating that a file provided for input or output could not be properly opened.
+*/
+
+class FileOpenFailedJSMinException extends JSMinException {
+}
+
+/**
+* A JSMin exception indicating that an unterminated comment was encountered in input.
+*/
+
+class UnterminatedCommentJSMinException extends JSMinException {
+}
+
+/**
+* A JSMin exception indicatig that an unterminated string literal was encountered in input.
+*/
+
+class UnterminatedStringLiteralJSMinException extends JSMinException {
+}
+
+/**
+* A JSMin exception indicatig that an unterminated regular expression lieteral was encountered in input.
+*/
+
+class UnterminatedRegExpLiteralJSMinException extends JSMinException {
+}
+
+/**
+* Main JSMin application class.
+*
+* Example of use :
+*
+* $jsMin = new JSMin(...input..., ...output...);
+* $jsMin -> minify();
+*
+* Do not specify input and/or output (or default to '-') to use stdin and/or stdout.
+*/
+
+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
+ */
+
+ private $in;
+
+ /**
+ * The output stream, in which to write the minimized JS file. Obtained by fopen().
+ * @var SplFileObject
+ */
+
+ private $out;
+
+ /**
+ * Temporary I/O character (A).
+ * @var string
+ */
+
+ private $theA;
+
+ /**
+ * Temporary I/O character (B).
+ * @var string
+ */
+
+ private $theB;
+
+ /**
+ * Indicates whether a character is alphanumeric or _, $, \ or non-ASCII.
+ *
+ * @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) {
+
+ // Get ASCII value of character for C-like comparisons
+
+ $a = ord($c);
+
+ // Compare using defined character ordinals, or between PHP strings
+ // Note : === is micro-faster than == when types are known to be the same
+
+ return
+ ($a >= ORD_a && $a <= ORD_z) ||
+ ($a >= ORD_0 && $a <= ORD_9) ||
+ ($a >= ORD_cA && $a <= ORD_cZ) ||
+ $c === '_' || $c === '$' || $c === '\\' || $a > 126
+ ;
+ }
+
+ /**
+ * Get the next character from the input stream.
+ *
+ * If said character is a control character, translate it to a space or linefeed.
+ *
+ * @return string The next character from the specified input stream.
+ * @see $in
+ * @see peek()
+ */
+
+ private function get() {
+
+ // Get next input character and advance position in file
+
+ $c = $this -> in -> fgetc();
+
+ // Test for non-problematic characters
+
+ if ($c === "\n" || $c === EOF || ord($c) >= ORD_space) {
+ return $c;
+ }
+
+ // else
+ // Make linefeeds into newlines
+
+ if ($c === "\r") {
+ return "\n";
+ }
+
+ // else
+ // Consider space
+
+ return ' ';
+ }
+
+ /**
+ * Get the next character from the input stream, without gettng it.
+ *
+ * @return string The next character from the specified input stream, without advancing the position
+ * in the underlying file.
+ * @see $in
+ * @see get()
+ */
+
+ private function peek() {
+
+ // Get next input character
+
+ $c = $this -> in -> fgetc();
+
+ // Regress position in file
+
+ $this -> in -> fseek(-1, SEEK_CUR);
+
+ // Return character obtained
+
+ return $c;
+ }
+
+ /**
+ * Get the next character from the input stream, excluding comments.
+ *
+ * {@link peek()} is used to see if a '/' is followed by a '*' or '/'.
+ * Multiline comments are actually returned as a single space.
+ *
+ * @return string The next character from the specified input stream, skipping comments.
+ * @see $in
+ */
+
+ private function next() {
+
+ // Get next char from input, translated if necessary
+
+ $c = $this -> get();
+
+ // Check comment possibility
+
+ if ($c == '/') {
+
+ // Look ahead : a comment is two slashes or slashes followed by asterisk (to be closed)
+
+ switch ($this -> peek()) {
+
+ case '/' :
+
+ // Comment is up to the end of the line
+ // TOTEST : simple $this -> in -> fgets()
+
+ while (true) {
+
+ $c = $this -> get();
+
+ if (ord($c) <= ORD_NL) {
+ return $c;
+ }
+ }
+
+ case '*' :
+
+ // Comment is up to comment close.
+ // Might not be terminated, if we hit the end of file.
+
+ while (true) {
+
+ // N.B. not using switch() because of having to test EOF with ===
+
+ $c = $this -> get();
+
+ if ($c == '*') {
+
+ // Comment termination if the char ahead is a slash
+
+ if ($this -> peek() == '/') {
+
+ // Advance again and make into a single space
+
+ $this -> get();
+ return ' ';
+ }
+ }
+ else if ($c === EOF) {
+
+ // Whoopsie
+
+ throw new UnterminatedCommentJSMinException();
+ }
+ }
+
+ default :
+
+ // Not a comment after all
+
+ return $c;
+ }
+ }
+
+ // No risk of a comment
+
+ return $c;
+ }
+
+ /**
+ * Do something !
+ *
+ * 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).
+ *
+ * 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.
+ */
+
+ private function action($action) {
+
+ // Choice of possible actions
+ // Note the frequent fallthroughs : the actions are decrementally "long"
+
+ switch ($action) {
+
+ case self :: ACT_FULL :
+
+ // Write A to output, then fall through
+
+ $this -> out -> fwrite($this -> theA);
+
+ case self :: ACT_BUF : // N.B. possible fallthrough from above
+
+ // Copy B to A
+
+ $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
+
+ if ($tmpA == '\'' || $tmpA == '"') {
+
+ while (true) {
+
+ // Output string contents
+
+ $this -> out -> fwrite($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();
+
+ if ($tmpA == $this -> theB) {
+
+ // String terminated
+
+ break; // from while(true)
+ }
+
+ // else
+
+ if (ord($tmpA) <= ORD_NL) {
+
+ // Whoopsie
+
+ throw new UnterminatedStringLiteralJSMinException();
+ }
+
+ // else
+
+ if ($tmpA == '\\') {
+
+ // Escape next char immediately
+
+ $this -> out -> fwrite($tmpA);
+ $tmpA = $this -> theA = $this -> get();
+ }
+ }
+ }
+
+ case self :: ACT_IMM : // N.B. possible fallthrough from above
+
+ // Get the next B
+
+ $this -> theB = $this -> next();
+
+ // Special case of recognising regular expressions (beginning with /) that are
+ // preceded by '(', ',' or '='
+
+ $tmpA = $this -> theA;
+
+ if ($this -> theB == '/' && ($tmpA == '(' || $tmpA == ',' || $tmpA == '=')) {
+
+ // Output the two successive chars
+
+ $this -> out -> fwrite($tmpA);
+ $this -> out -> fwrite($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();
+
+ if ($tmpA == '/') {
+
+ // RE literal terminated
+
+ break; // from while(true)
+ }
+
+ // else
+
+ if ($tmpA == '\\') {
+
+ // Escape next char immediately
+
+ $this -> out -> fwrite($tmpA);
+ $tmpA = $this -> theA = $this -> get();
+ }
+ else if (ord($tmpA) <= ORD_NL) {
+
+ // Whoopsie
+
+ throw new UnterminatedRegExpLiteralJSMinException();
+ }
+
+ // Output RE characters
+
+ $this -> out -> fwrite($tmpA);
+ }
+
+ // Move forward after the RE literal
+
+ $this -> theB = $this -> next();
+ }
+
+ break;
+ default : throw new JSMinException('Expected a JSMin :: ACT_* constant in action().');
+ }
+ }
+
+ /**
+ * 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.
+ * 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.
+ *
+ * Note : name was changed from jsmin() because PHP identifiers are case-insensitive, and it is already
+ * the name of this class.
+ *
+ * @see __construct()
+ */
+
+ public function minify() {
+
+ // Initialize A and run the first (minimal) action
+
+ $this -> theA = "\n";
+ $this -> action(self :: ACT_IMM);
+
+ // Proceed all the way to the end of the input file
+
+ while ($this -> theA !== EOF) {
+
+ switch ($this -> theA) {
+
+ case ' ' :
+
+ if (self :: isAlphaNum($this -> theB)) {
+ $this -> action(self :: ACT_FULL);
+ }
+ else {
+ $this -> action(self :: ACT_BUF);
+ }
+
+ break;
+ case "\n" :
+
+ switch ($this -> theB) {
+
+ case '{' : case '[' : case '(' :
+ case '+' : case '-' :
+
+ $this -> action(self :: ACT_FULL);
+
+ break;
+ case ' ' :
+
+ $this -> action(self :: ACT_IMM);
+
+ break;
+ default :
+
+ if (self :: isAlphaNum($this -> theB)) {
+ $this -> action(self :: ACT_FULL);
+ }
+ else {
+ $this -> action(self :: ACT_BUF);
+ }
+
+ break;
+ }
+
+ break;
+ default :
+
+ switch ($this -> theB) {
+
+ case ' ' :
+
+ if (self :: isAlphaNum($this -> theA)) {
+
+ $this -> action(self :: ACT_FULL);
+ break;
+ }
+
+ // else
+
+ $this -> action(self :: ACT_IMM);
+
+ break;
+ case "\n" :
+
+ switch ($this -> theA) {
+
+ case '}' : case ']' : case ')' : case '+' :
+ case '-' : case '"' : case '\'' :
+
+ $this -> action(self :: ACT_FULL);
+
+ break;
+ default :
+
+ if (self :: isAlphaNum($this -> theA)) {
+ $this -> action(self :: ACT_FULL);
+ }
+ else {
+ $this -> action(self :: ACT_IMM);
+ }
+
+ break;
+ }
+
+ break;
+ default :
+
+ $this -> action(self :: ACT_FULL);
+
+ break;
+ }
+
+ break;
+ }
+ }
+ }
+
+ /**
+ * Prepare a new JSMin application.
+ *
+ * The next step is to {@link minify()} the input into the output.
+ *
+ * @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.
+ * @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).
+ */
+
+ public function __construct($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';
+
+ try {
+
+ $this -> in = new SplFileObject($inFileName, 'rb', TRUE);
+ }
+ catch (Exception $e) {
+
+ throw new FileOpenFailedJSMinException(
+ 'Failed to open "'.$inFileName.'" for reading only.'
+ );
+ }
+
+ try {
+
+ $this -> out = new SplFileObject($outFileName, 'wb', TRUE);
+ }
+ catch (Exception $e) {
+
+ throw new FileOpenFailedJSMinException(
+ 'Failed to open "'.$outFileName.'" for writing only.'
+ );
+ }
+
+ // Present possible initial comments
+
+ if ($comments !== NULL) {
+ foreach ($comments as $comm) {
+ $this -> out -> fwrite('// '.$comm."\n");
+ }
+ }
+ }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Web/Javascripts/extra/json.js b/framework/Web/Javascripts/extra/json.js
index 0981169d..25b17b5f 100644
--- a/framework/Web/Javascripts/extra/json.js
+++ b/framework/Web/Javascripts/extra/json.js
@@ -21,7 +21,7 @@ SOFTWARE.
Array.prototype.______array = '______array';
-Prado.AJAX.JSON = {
+Prado.JSON = {
org: 'http://www.JSON.org',
copyright: '(c)2005 JSON.org',
license: 'http://www.crockford.com/JSON/license.html',
diff --git a/framework/Web/Javascripts/js/ajax.js b/framework/Web/Javascripts/js/ajax.js
index 62c08d4f..91542d45 100644
--- a/framework/Web/Javascripts/js/ajax.js
+++ b/framework/Web/Javascripts/js/ajax.js
@@ -93,7 +93,9 @@ setRequestHeaders: function() {
var requestHeaders =
['X-Requested-With', 'XMLHttpRequest',
'X-Prototype-Version', Prototype.Version,
- 'Accept', 'text/javascript, text/html, application/xml, text/xml, *
+ 'Accept', 'text/javascript, text/html, application/xml, text/xml'];
+if (this.options.method == 'post') {
+requestHeaders.push('Content-type', this.options.contentType);
if (this.transport.overrideMimeType)
requestHeaders.push('Connection', 'close');
}
@@ -222,156 +224,31 @@ onTimerEvent: function() {
this.updater = new Ajax.Updater(this.container, this.url, this.options);
}
});
-Prado.AJAX = { Service : 'Prototype' };
-Prado.AJAX.EvalScript = function(output)
+Prado.Callback = Class.create();
+Object.extend(Prado.Callback,
{
-var match = new RegExp(Ajax.Updater.ScriptFragment, 'img');
-var scripts= output.match(match);
-if (scripts)
+FIELD_CALLBACK_TARGET : 'PRADO_CALLBACK_TARGET',
+FIELD_CALLBACK_PARAMETER : 'PRADO_CALLBACK_PARAMETER',
+PostDataLoaders : ['PRADO_PAGESTATE'],
+Exception :
{
-match = new RegExp(Ajax.Updater.ScriptFragment, 'im');
-setTimeout((function()
-{
-for (var i = 0; i < scripts.length; i++)
-eval(scripts[i].match(match)[1]);
-}).bind(this), 50);
-}
-}
-Prado.AJAX.Request = Class.create();
-Prado.AJAX.Request.prototype = Object.extend(Ajax.Request.prototype,
-{
-evalJSON: function()
-{
-try
-{
-var json = this.transport.getResponseHeader('X-JSON'), object;
-object = eval(json);
-return object;
-}
-catch (e)
-{
-if(isString(json))
-{
-return Prado.AJAX.JSON.parse(json);
-}
-}
-},
-respondToReadyState: function(readyState) {
-var event = Ajax.Request.Events[readyState];
-var transport = this.transport, json = this.evalJSON();
-if(event == 'Complete' && transport.status)
-Ajax.Responders.dispatch('on' + transport.status, this, transport, json);
-(this.options['on' + event] || Prototype.emptyFunction)(transport, json);
-Ajax.Responders.dispatch('on' + event, this, transport, json);
-if (event == 'Complete')
-(this.options['on' + this.transport.status]
- || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
- || Prototype.emptyFunction)(transport, json);
-if (event == 'Complete')
-this.transport.onreadystatechange = Prototype.emptyFunction;
-}
-});
-Prado.AJAX.Error = function(e, code)
-{
-e.name = 'Prado.AJAX.Error';
-e.code = code;
-return e;
-}
-Prado.AJAX.RequestBuilder = Class.create();
-Prado.AJAX.RequestBuilder.prototype =
-{
-initialize : function()
-{
-this.body = '';
-this.data = [];
-},
-encode : function(data)
-{
-return Prado.AJAX.JSON.stringify(data);
-},
-build : function(data)
-{
-var sep = '';
-for ( var argName in data)
-{
-if(isFunction(data[argName])) continue;
-try
-{
-this.body += sep + argName + '=';
-this.body += encodeURIComponent(this.encode(data[argName]));
-} catch (e) {
-throw Prado.AJAX.Error(e, 1006);
-}
-sep = '&';
-}
-},
-getAll : function()
-{
-this.build(this.data);
-return this.body;
-}
-}
-Prado.AJAX.RemoteObject = function(){};
-Prado.AJAX.RemoteObject.Request = Class.create();
-Prado.AJAX.RemoteObject.Request.prototype = Object.extend(Prado.AJAX.Request.prototype,
-{
-initialize : function(options)
-{
-this.transport = Ajax.getTransport();
-this.setOptions(options);
-this.post = new Prado.AJAX.RequestBuilder();
-},
-invokeRemoteObject : function(url, args)
-{
-this.initParameters(args);
-this.options.postBody = this.post.getAll();
-this.request(url);
-},
-initParameters : function(args)
-{
-this.post.data['__parameters'] = [];
-for(var i = 0; i<args.length; i++)
-this.post.data['__parameters'][i] = args[i];
-}
-});
-Prado.AJAX.RemoteObject.prototype =
-{
-baseInitialize : function(handlers, options)
-{
-this.__handlers = handlers || {};
-this.__service = new Prado.AJAX.RemoteObject.Request(options);
-},
-__call : function(url, method, args)
-{
-this.__service.options.onSuccess = this.__onSuccess.bind(this);
-this.__callback = method;
-return this.__service.invokeRemoteObject(url+"/"+method, args);
-},
-__onSuccess : function(transport, json)
-{
-if(this.__handlers[this.__callback])
-this.__handlers[this.__callback](json, transport.responseText);
-}
-};
-Prado.AJAX.Exception =
-{
-"on505" : function(request, transport, e)
+"on505" : function(request, transport, data)
{
var msg = 'HTTP '+transport.status+" with response";
Logger.error(msg, transport.responseText);
-Logger.exception(e);
+this.logException(data);
},
-onComplete : function(request, transport, e)
+onComplete : function(request, transport, data)
{
if(transport.status != 505)
{
var msg = 'HTTP '+transport.status+" with response : \n";
msg += transport.responseText + "\n";
-msg += "Data : \n"+inspect(e);
+msg += "Data : \n"+inspect(data);
Logger.warn(msg);
}
},
-format : function(e)
+formatException : function(e)
{
var msg = e.type + " with message \""+e.message+"\"";
msg += " in "+e.file+"("+e.line+")\n";
@@ -387,115 +264,65 @@ return msg;
},
logException : function(e)
{
-var msg = Prado.AJAX.Exception.format(e);
-Logger.error("Server Error "+e.code, msg);
-}
+Logger.error("Callback Request Error "+e.code, this.formatException(e));
}
-Event.OnLoad(function()
-{
-if(typeof Logger != "undefined")
-{
-Logger.exception = Prado.AJAX.Exception.logException;
-Ajax.Responders.register(Prado.AJAX.Exception);
-}
-});
-Prado.AJAX.Callback = Class.create();
-Prado.AJAX.Callback.prototype = Object.extend(new Prado.AJAX.RemoteObject(),
-{
-initialize : function(ID, options)
-{
-if(!isString(ID) && typeof(ID.id) != "undefined")
-ID = ID.id;
-if(!isString(ID))
-throw new Error('A Control ID must be specified');
-this.baseInitialize(this, options);
-this.options = options || [];
-this.__service.post.data['__ID'] = ID;
-this.requestCallback();
},
-collectPostData : function()
-{
-var IDs = Prado.AJAX.Callback.IDs;
-this.__service.post.data['__data'] = {};
-for(var i = 0; i<IDs.length; i++)
+encode : function(data)
{
-var id = IDs[i];
-if(id.indexOf("[]") > -1)
-this.__service.post.data['__data'][id] =
-this.collectArrayPostData(id);
-else if(isObject($(id)))
-this.__service.post.data['__data'][id] = $F(id);
-}
+Prado.JSON.stringify(data);
},
-collectArrayPostData : function(name)
+decode : function(data)
{
-var elements = document.getElementsByName(name);
-var data = [];
-$A(elements).each(function(el)
+return Prado.JSON.parse(data);
+}
+})
+Event.OnLoad(function()
{
-if($F(el)) data.push($F(el));
+if(typeof Logger != "undefined")
+Ajax.Responders.register(Prado.Callback.Exception);
});
-return data;
-},
-requestCallback : function()
+Prado.Callback.prototype =
{
-this.collectPostData();
-if(Prado.AJAX.Validate(this.options))
-return this.__call(Prado.AJAX.Callback.Server, 'handleCallback', this.options.params);
-},
-handleCallback : function(result, output)
+url : window.location.href,
+options : {},
+id : null,
+parameters : null,
+initialize : function(id, parameters, onSuccess, options)
{
-if(typeof(result) != "undefined" && !isNull(result))
+this.options = options || {};
+this.id = id;
+this.parameters = parameters;
+var request =
{
-this.options.onSuccess(result['data'], output);
-if(result['actions'])
-result.actions.each(Prado.AJAX.Callback.Action.__run);
-}
+postBody : this._getPostData(),
+onSuccess : this._onSuccess.bind(this)
}
-});
-Prado.AJAX.Callback.Action =
+Object.extend(this.options || {},request);
+new Ajax.Request(this.url, this.options);
+},
+_getPostData : function()
{
-__run : function(command)
+var data = {};
+Prado.Callback.PostDataLoaders.each(function(name)
{
-for(var name in command)
+$A(document.getElementsByName(name)).each(function(element)
{
-if(command[name][0] && ($(command[name][0]) || command[name][0].indexOf("[]") > -1))
+var value = $F(element);
+if(typeof(value) != "undefined")
+data[name] = value;
+})
+})
+if(typeof(this.parameters) != "undefined")
+data[Prado.Callback.FIELD_CALLBACK_PARAMETER] = Prado.Callback.encode(this.parameters);
+data[Prado.Callback.FIELD_CALLBACK_TARGET] = this.id;
+return $H(data).toQueryString();
+},
+_onSuccess : function(response, transport, json)
{
-name.toFunction().apply(this,command[name]);
-}
-}
}
-};
-Prado.AJAX.Validate = function(options)
-{
-if(options.CausesValidation)
-{
-if(options.ValidatorGroup)
-return Prado.Validation.ValidateValidatorGroup(options.ValidatorGroup);
-else if(options.ValidationGroup)
-return Prado.Validation.ValidateValidationGroup(options.ValidationGroup);
-else
-return Prado.Validation.ValidateNonGroup(options.ValidationForm);
-}
-else
-return true;
-};
-Prado.AJAX.Callback.Server = '';
-Prado.AJAX.Callback.IDs = [];
-Prado.Callback = function(ID, params, onSuccess, options)
-{
-var callback =
-{
-'params' : [params] || [],
-'onSuccess' : onSuccess || Prototype.emptyFunction,
-'CausesValidation' : true
-};
-Object.extend(callback, options || {});
-new Prado.AJAX.Callback(ID, callback);
-return false;
}
Array.prototype.______array = '______array';
-Prado.AJAX.JSON = {
+Prado.JSON = {
org: 'http:
copyright: '(c)2005 JSON.org',
license: 'http:
@@ -796,1727 +623,3 @@ return ch >= '0' && ch <= '9' ? number() : word();
return value();
}
};
-var Autocompleter = {}
-Autocompleter.Base = function() {};
-Autocompleter.Base.prototype = {
-baseInitialize: function(element, update, options) {
-this.element = $(element);
-this.update= $(update);
-this.hasFocus= false;
-this.changed = false;
-this.active= false;
-this.index = 0;
-this.entryCount= 0;
-if (this.setOptions)
-this.setOptions(options);
-else
-this.options = options || {};
-this.options.paramName= this.options.paramName || this.element.name;
-this.options.tokens = this.options.tokens || [];
-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});
-};
-this.options.onHide = this.options.onHide ||
-function(element, update){ new Effect.Fade(update,{duration:0.15}) };
-if (typeof(this.options.tokens) == 'string')
-this.options.tokens = new Array(this.options.tokens);
-this.observer = null;
-this.element.setAttribute('autocomplete','off');
-Element.hide(this.update);
-Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this));
-Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this));
-},
-show: function() {
-if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
-if(!this.iefix &&
-(navigator.appVersion.indexOf('MSIE')>0) &&
-(navigator.userAgent.indexOf('Opera')<0) &&
-(Element.getStyle(this.update, 'position')=='absolute')) {
-new Insertion.After(this.update,
- '<iframe id="' + this.update.id + '_iefix" '+
- 'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
- 'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
-this.iefix = $(this.update.id+'_iefix');
-}
-if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
-},
-fixIEOverlapping: function() {
-Position.clone(this.update, this.iefix);
-this.iefix.style.zIndex = 1;
-this.update.style.zIndex = 2;
-Element.show(this.iefix);
-},
-hide: function() {
-this.stopIndicator();
-if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
-if(this.iefix) Element.hide(this.iefix);
-},
-startIndicator: function() {
-if(this.options.indicator) Element.show(this.options.indicator);
-},
-stopIndicator: function() {
-if(this.options.indicator) Element.hide(this.options.indicator);
-},
-onKeyPress: function(event) {
-if(this.active)
-switch(event.keyCode) {
- case Event.KEY_TAB:
- case Event.KEY_RETURN:
- this.selectEntry();
- Event.stop(event);
- case Event.KEY_ESC:
- this.hide();
- this.active = false;
- Event.stop(event);
- return;
- case Event.KEY_LEFT:
- case Event.KEY_RIGHT:
- return;
- case Event.KEY_UP:
- this.markPrevious();
- this.render();
- if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
- return;
- case Event.KEY_DOWN:
- this.markNext();
- this.render();
- if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
- return;
-}
- else
- if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
- (navigator.appVersion.indexOf('AppleWebKit') > 0 && event.keyCode == 0)) return;
-this.changed = true;
-this.hasFocus = true;
-if(this.observer) clearTimeout(this.observer);
-this.observer =
-setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
-},
-activate: function() {
-this.changed = false;
-this.hasFocus = true;
-this.getUpdatedChoices();
-},
-onHover: function(event) {
-var element = Event.findElement(event, 'LI');
-if(this.index != element.autocompleteIndex)
-{
-this.index = element.autocompleteIndex;
-this.render();
-}
-Event.stop(event);
-},
-onClick: function(event) {
-var element = Event.findElement(event, 'LI');
-this.index = element.autocompleteIndex;
-this.selectEntry();
-this.hide();
-},
-onBlur: function(event) {
- setTimeout(this.hide.bind(this), 250);
-this.hasFocus = false;
-this.active = false;
-},
-render: function() {
-if(this.entryCount > 0) {
-for (var i = 0; i < this.entryCount; i++)
-this.index==i ?
-Element.addClassName(this.getEntry(i),"selected") :
-Element.removeClassName(this.getEntry(i),"selected");
-if(this.hasFocus) {
-this.show();
-this.active = true;
-}
-} else {
-this.active = false;
-this.hide();
-}
-},
-markPrevious: function() {
-if(this.index > 0) this.index--
-else this.index = this.entryCount-1;
-},
-markNext: function() {
-if(this.index < this.entryCount-1) this.index++
-else this.index = 0;
-},
-getEntry: function(index) {
-return this.update.firstChild.childNodes[index];
-},
-getCurrentEntry: function() {
-return this.getEntry(this.index);
-},
-selectEntry: function() {
-this.active = false;
-this.updateElement(this.getCurrentEntry());
-},
-updateElement: function(selectedElement) {
-if (this.options.updateElement) {
-this.options.updateElement(selectedElement);
-return;
-}
-var value = '';
-if (this.options.select) {
-var nodes = document.getElementsByClassName(this.options.select, selectedElement) || [];
-if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
-} else
-value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
-var lastTokenPos = this.findLastToken();
-if (lastTokenPos != -1) {
-var newValue = this.element.value.substr(0, lastTokenPos + 1);
-var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
-if (whitespace)
-newValue += whitespace[0];
-this.element.value = newValue + value;
-} else {
-this.element.value = value;
-}
-this.element.focus();
-if (this.options.afterUpdateElement)
-this.options.afterUpdateElement(this.element, selectedElement);
-},
-updateChoices: function(choices) {
-if(!this.changed && this.hasFocus) {
-this.update.innerHTML = choices;
-Element.cleanWhitespace(this.update);
-Element.cleanWhitespace(this.update.firstChild);
-if(this.update.firstChild && this.update.firstChild.childNodes) {
-this.entryCount =
-this.update.firstChild.childNodes.length;
-for (var i = 0; i < this.entryCount; i++) {
-var entry = this.getEntry(i);
-entry.autocompleteIndex = i;
-this.addObservers(entry);
-}
-} else {
-this.entryCount = 0;
-}
-this.stopIndicator();
-this.index = 0;
-this.render();
-}
-},
-addObservers: function(element) {
-Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
-Event.observe(element, "click", this.onClick.bindAsEventListener(this));
-},
-onObserverEvent: function() {
-this.changed = false;
-if(this.getToken().length>=this.options.minChars) {
-this.startIndicator();
-this.getUpdatedChoices();
-} else {
-this.active = false;
-this.hide();
-}
-},
-getToken: function() {
-var tokenPos = this.findLastToken();
-if (tokenPos != -1)
-var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,'');
-else
-var ret = this.element.value;
-return /\n/.test(ret) ? '' : ret;
-},
-findLastToken: function() {
-var lastTokenPos = -1;
-for (var i=0; i<this.options.tokens.length; i++) {
-var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]);
-if (thisTokenPos > lastTokenPos)
-lastTokenPos = thisTokenPos;
-}
-return lastTokenPos;
-}
-}
-Ajax.Autocompleter = Class.create();
-Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), {
-initialize: function(element, update, url, options) {
-this.baseInitialize(element, update, options);
-this.options.asynchronous= true;
-this.options.onComplete= this.onComplete.bind(this);
-this.options.defaultParams = this.options.parameters || null;
-this.url = url;
-},
-getUpdatedChoices: function() {
-entry = encodeURIComponent(this.options.paramName) + '=' +
-encodeURIComponent(this.getToken());
-this.options.parameters = this.options.callback ?
-this.options.callback(this.element, entry) : entry;
-if(this.options.defaultParams)
-this.options.parameters += '&' + this.options.defaultParams;
-new Ajax.Request(this.url, this.options);
-},
-onComplete: function(request) {
-this.updateChoices(request.responseText);
-}
-});
-Autocompleter.Local = Class.create();
-Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
-initialize: function(element, update, array, options) {
-this.baseInitialize(element, update, options);
-this.options.array = array;
-},
-getUpdatedChoices: function() {
-this.updateChoices(this.options.selector(this));
-},
-setOptions: function(options) {
-this.options = Object.extend({
-choices: 10,
-partialSearch: true,
-partialChars: 2,
-ignoreCase: true,
-fullSearch: false,
-selector: function(instance) {
-var ret = [];var partial = [];var entry = instance.getToken();
-var count = 0;
-for (var i = 0; i < instance.options.array.length &&
-ret.length < instance.options.choices ; i++) {
-var elem = instance.options.array[i];
-var foundPos = instance.options.ignoreCase ?
-elem.toLowerCase().indexOf(entry.toLowerCase()) :
-elem.indexOf(entry);
-while (foundPos != -1) {
-if (foundPos == 0 && elem.length != entry.length) {
-ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
-elem.substr(entry.length) + "</li>");
-break;
-} else if (entry.length >= instance.options.partialChars &&
-instance.options.partialSearch && foundPos != -1) {
-if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
-partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
-elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
-foundPos + entry.length) + "</li>");
-break;
-}
-}
-foundPos = instance.options.ignoreCase ?
-elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
-elem.indexOf(entry, foundPos + 1);
-}
-}
-if (partial.length)
-ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
-return "<ul>" + ret.join('') + "</ul>";
-}
-}, options || {});
-}
-});
-Field.scrollFreeActivate = function(field) {
-setTimeout(function() {
-Field.activate(field);
-}, 1);
-}
-Ajax.InPlaceEditor = Class.create();
-Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99";
-Ajax.InPlaceEditor.prototype = {
-initialize: function(element, url, options) {
-this.url = url;
-this.element = $(element);
-this.options = Object.extend({
-okButton: true,
-okText: "ok",
-cancelLink: true,
-cancelText: "cancel",
-savingText: "Saving...",
-clickToEditText: "Click to edit",
-okText: "ok",
-rows: 1,
-onComplete: function(transport, element) {
-new Effect.Highlight(element, {startcolor: this.options.highlightcolor});
-},
-onFailure: function(transport) {
-alert("Error communicating with the server: " + transport.responseText.stripTags());
-},
-callback: function(form) {
-return Form.serialize(form);
-},
-handleLineBreaks: true,
-loadingText: 'Loading...',
-savingClassName: 'inplaceeditor-saving',
-loadingClassName: 'inplaceeditor-loading',
-formClassName: 'inplaceeditor-form',
-highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor,
-highlightendcolor: "#FFFFFF",
-externalControl: null,
-submitOnBlur: false,
-ajaxOptions: {},
-evalScripts: false
-}, options || {});
-if(!this.options.formId && this.element.id) {
-this.options.formId = this.element.id + "-inplaceeditor";
-if ($(this.options.formId)) {
- this.options.formId = null;
-}
-}
-if (this.options.externalControl) {
-this.options.externalControl = $(this.options.externalControl);
-}
-this.originalBackground = Element.getStyle(this.element, 'background-color');
-if (!this.originalBackground) {
-this.originalBackground = "transparent";
-}
-this.element.title = this.options.clickToEditText;
-this.onclickListener = this.enterEditMode.bindAsEventListener(this);
-this.mouseoverListener = this.enterHover.bindAsEventListener(this);
-this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
-Event.observe(this.element, 'click', this.onclickListener);
-Event.observe(this.element, 'mouseover', this.mouseoverListener);
-Event.observe(this.element, 'mouseout', this.mouseoutListener);
-if (this.options.externalControl) {
-Event.observe(this.options.externalControl, 'click', this.onclickListener);
-Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
-Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
-}
-},
-enterEditMode: function(evt) {
-if (this.saving) return;
-if (this.editing) return;
-this.editing = true;
-this.onEnterEditMode();
-if (this.options.externalControl) {
-Element.hide(this.options.externalControl);
-}
-Element.hide(this.element);
-this.createForm();
-this.element.parentNode.insertBefore(this.form, this.element);
-Field.scrollFreeActivate(this.editField);
- if (evt) {
-Event.stop(evt);
-}
-return false;
-},
-createForm: function() {
-this.form = document.createElement("form");
-this.form.id = this.options.formId;
-Element.addClassName(this.form, this.options.formClassName)
-this.form.onsubmit = this.onSubmit.bind(this);
-this.createEditField();
-if (this.options.textarea) {
-var br = document.createElement("br");
-this.form.appendChild(br);
-}
-if (this.options.okButton) {
-okButton = document.createElement("input");
-okButton.type = "submit";
-okButton.value = this.options.okText;
-okButton.className = 'editor_ok_button';
-this.form.appendChild(okButton);
-}
-if (this.options.cancelLink) {
-cancelLink = document.createElement("a");
-cancelLink.href = "#";
-cancelLink.appendChild(document.createTextNode(this.options.cancelText));
-cancelLink.onclick = this.onclickCancel.bind(this);
-cancelLink.className = 'editor_cancel';
-this.form.appendChild(cancelLink);
-}
-},
-hasHTMLLineBreaks: function(string) {
-if (!this.options.handleLineBreaks) return false;
-return string.match(/<br/i) || string.match(/<p>/i);
-},
-convertHTMLLineBreaks: function(string) {
-return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, "");
-},
-createEditField: function() {
-var text;
-if(this.options.loadTextURL) {
-text = this.options.loadingText;
-} else {
-text = this.getText();
-}
-var obj = this;
-if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) {
-this.options.textarea = false;
-var textField = document.createElement("input");
-textField.obj = this;
-textField.type = "text";
-textField.name = "value";
-textField.value = text;
-textField.style.backgroundColor = this.options.highlightcolor;
-textField.className = 'editor_field';
-var size = this.options.size || this.options.cols || 0;
-if (size != 0) textField.size = size;
-if (this.options.submitOnBlur)
-textField.onblur = this.onSubmit.bind(this);
-this.editField = textField;
-} else {
-this.options.textarea = true;
-var textArea = document.createElement("textarea");
-textArea.obj = this;
-textArea.name = "value";
-textArea.value = this.convertHTMLLineBreaks(text);
-textArea.rows = this.options.rows;
-textArea.cols = this.options.cols || 40;
-textArea.className = 'editor_field';
-if (this.options.submitOnBlur)
-textArea.onblur = this.onSubmit.bind(this);
-this.editField = textArea;
-}
-if(this.options.loadTextURL) {
-this.loadExternalText();
-}
-this.form.appendChild(this.editField);
-},
-getText: function() {
-return this.element.innerHTML;
-},
-loadExternalText: function() {
-Element.addClassName(this.form, this.options.loadingClassName);
-this.editField.disabled = true;
-new Ajax.Request(
-this.options.loadTextURL,
-Object.extend({
-asynchronous: true,
-onComplete: this.onLoadedExternalText.bind(this)
-}, this.options.ajaxOptions)
-);
-},
-onLoadedExternalText: function(transport) {
-Element.removeClassName(this.form, this.options.loadingClassName);
-this.editField.disabled = false;
-this.editField.value = transport.responseText.stripTags();
-},
-onclickCancel: function() {
-this.onComplete();
-this.leaveEditMode();
-return false;
-},
-onFailure: function(transport) {
-this.options.onFailure(transport);
-if (this.oldInnerHTML) {
-this.element.innerHTML = this.oldInnerHTML;
-this.oldInnerHTML = null;
-}
-return false;
-},
-onSubmit: function() {
- var form = this.form;
-var value = this.editField.value;
-this.onLoading();
-if (this.options.evalScripts) {
-new Ajax.Request(
-this.url, Object.extend({
-parameters: this.options.callback(form, value),
-onComplete: this.onComplete.bind(this),
-onFailure: this.onFailure.bind(this),
-asynchronous:true,
-evalScripts:true
-}, this.options.ajaxOptions));
-} else{
-new Ajax.Updater(
-{ success: this.element,
- failure: null },
-this.url, Object.extend({
-parameters: this.options.callback(form, value),
-onComplete: this.onComplete.bind(this),
-onFailure: this.onFailure.bind(this)
-}, this.options.ajaxOptions));
-}
- if (arguments.length > 1) {
-Event.stop(arguments[0]);
-}
-return false;
-},
-onLoading: function() {
-this.saving = true;
-this.removeForm();
-this.leaveHover();
-this.showSaving();
-},
-showSaving: function() {
-this.oldInnerHTML = this.element.innerHTML;
-this.element.innerHTML = this.options.savingText;
-Element.addClassName(this.element, this.options.savingClassName);
-this.element.style.backgroundColor = this.originalBackground;
-Element.show(this.element);
-},
-removeForm: function() {
-if(this.form) {
-if (this.form.parentNode) Element.remove(this.form);
-this.form = null;
-}
-},
-enterHover: function() {
-if (this.saving) return;
-this.element.style.backgroundColor = this.options.highlightcolor;
-if (this.effect) {
-this.effect.cancel();
-}
-Element.addClassName(this.element, this.options.hoverClassName)
-},
-leaveHover: function() {
-if (this.options.backgroundColor) {
-this.element.style.backgroundColor = this.oldBackground;
-}
-Element.removeClassName(this.element, this.options.hoverClassName)
-if (this.saving) return;
-this.effect = new Effect.Highlight(this.element, {
-startcolor: this.options.highlightcolor,
-endcolor: this.options.highlightendcolor,
-restorecolor: this.originalBackground
-});
-},
-leaveEditMode: function() {
-Element.removeClassName(this.element, this.options.savingClassName);
-this.removeForm();
-this.leaveHover();
-this.element.style.backgroundColor = this.originalBackground;
-Element.show(this.element);
-if (this.options.externalControl) {
-Element.show(this.options.externalControl);
-}
-this.editing = false;
-this.saving = false;
-this.oldInnerHTML = null;
-this.onLeaveEditMode();
-},
-onComplete: function(transport) {
-this.leaveEditMode();
-this.options.onComplete.bind(this)(transport, this.element);
-},
-onEnterEditMode: function() {},
-onLeaveEditMode: function() {},
-dispose: function() {
-if (this.oldInnerHTML) {
-this.element.innerHTML = this.oldInnerHTML;
-}
-this.leaveEditMode();
-Event.stopObserving(this.element, 'click', this.onclickListener);
-Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
-Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
-if (this.options.externalControl) {
-Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
-Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
-Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
-}
-}
-};
-Ajax.InPlaceCollectionEditor = Class.create();
-Object.extend(Ajax.InPlaceCollectionEditor.prototype, Ajax.InPlaceEditor.prototype);
-Object.extend(Ajax.InPlaceCollectionEditor.prototype, {
-createEditField: function() {
-if (!this.cached_selectTag) {
-var selectTag = document.createElement("select");
-var collection = this.options.collection || [];
-var optionTag;
-collection.each(function(e,i) {
-optionTag = document.createElement("option");
-optionTag.value = (e instanceof Array) ? e[0] : e;
-if(this.options.value==optionTag.value) optionTag.selected = true;
-optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e));
-selectTag.appendChild(optionTag);
-}.bind(this));
-this.cached_selectTag = selectTag;
-}
-this.editField = this.cached_selectTag;
-if(this.options.loadTextURL) this.loadExternalText();
-this.form.appendChild(this.editField);
-this.options.callback = function(form, value) {
-return "value=" + encodeURIComponent(value);
-}
-}
-});
-Form.Element.DelayedObserver = Class.create();
-Form.Element.DelayedObserver.prototype = {
-initialize: function(element, delay, callback) {
-this.delay = delay || 0.5;
-this.element = $(element);
-this.callback= callback;
-this.timer = null;
-this.lastValue = $F(this.element);
-Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
-},
-delayedListener: function(event) {
-if(this.lastValue == $F(this.element)) return;
-if(this.timer) clearTimeout(this.timer);
-this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
-this.lastValue = $F(this.element);
-},
-onTimerEvent: function() {
-this.timer = null;
-this.callback(this.element, $F(this.element));
-}
-};
-var Droppables = {
-drops: [],
-remove: function(element) {
-this.drops = this.drops.reject(function(d) { return d.element==$(element) });
-},
-add: function(element) {
-element = $(element);
-var options = Object.extend({
-greedy: true,
-hoverclass: null,
-tree: false
-}, arguments[1] || {});
-if(options.containment) {
-options._containers = [];
-var containment = options.containment;
-if((typeof containment == 'object') &&
-(containment.constructor == Array)) {
-containment.each( function(c) { options._containers.push($(c)) });
-} else {
-options._containers.push($(containment));
-}
-}
-if(options.accept) options.accept = [options.accept].flatten();
-Element.makePositioned(element);options.element = element;
-this.drops.push(options);
-},
-findDeepestChild: function(drops) {
-deepest = drops[0];
-for (i = 1; i < drops.length; ++i)
-if (Element.isParent(drops[i].element, deepest.element))
-deepest = drops[i];
-return deepest;
-},
-isContained: function(element, drop) {
-var containmentNode;
-if(drop.tree) {
-containmentNode = element.treeNode;
-} else {
-containmentNode = element.parentNode;
-}
-return drop._containers.detect(function(c) { return containmentNode == c });
-},
-isAffected: function(point, element, drop) {
-return (
-(drop.element!=element) &&
-((!drop._containers) ||
-this.isContained(element, drop)) &&
-((!drop.accept) ||
-(Element.classNames(element).detect(
-function(v) { return drop.accept.include(v) } ) )) &&
-Position.within(drop.element, point[0], point[1]) );
-},
-deactivate: function(drop) {
-if(drop.hoverclass)
-Element.removeClassName(drop.element, drop.hoverclass);
-this.last_active = null;
-},
-activate: function(drop) {
-if(drop.hoverclass)
-Element.addClassName(drop.element, drop.hoverclass);
-this.last_active = drop;
-},
-show: function(point, element) {
-if(!this.drops.length) return;
-var affected = [];
-if(this.last_active) this.deactivate(this.last_active);
-this.drops.each( function(drop) {
-if(Droppables.isAffected(point, element, drop))
-affected.push(drop);
-});
-if(affected.length>0) {
-drop = Droppables.findDeepestChild(affected);
-Position.within(drop.element, point[0], point[1]);
-if(drop.onHover)
-drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
-Droppables.activate(drop);
-}
-},
-fire: function(event, element) {
-if(!this.last_active) return;
-Position.prepare();
-if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
-if (this.last_active.onDrop)
-this.last_active.onDrop(element, this.last_active.element, event);
-},
-reset: function() {
-if(this.last_active)
-this.deactivate(this.last_active);
-}
-}
-var Draggables = {
-drags: [],
-observers: [],
-register: function(draggable) {
-if(this.drags.length == 0) {
-this.eventMouseUp = this.endDrag.bindAsEventListener(this);
-this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
-this.eventKeypress= this.keyPress.bindAsEventListener(this);
-Event.observe(document, "mouseup", this.eventMouseUp);
-Event.observe(document, "mousemove", this.eventMouseMove);
-Event.observe(document, "keypress", this.eventKeypress);
-}
-this.drags.push(draggable);
-},
-unregister: function(draggable) {
-this.drags = this.drags.reject(function(d) { return d==draggable });
-if(this.drags.length == 0) {
-Event.stopObserving(document, "mouseup", this.eventMouseUp);
-Event.stopObserving(document, "mousemove", this.eventMouseMove);
-Event.stopObserving(document, "keypress", this.eventKeypress);
-}
-},
-activate: function(draggable) {
-window.focus();this.activeDraggable = draggable;
-},
-deactivate: function() {
-this.activeDraggable = null;
-},
-updateDrag: function(event) {
-if(!this.activeDraggable) return;
-var pointer = [Event.pointerX(event), Event.pointerY(event)];
-if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
-this._lastPointer = pointer;
-this.activeDraggable.updateDrag(event, pointer);
-},
-endDrag: function(event) {
-if(!this.activeDraggable) return;
-this._lastPointer = null;
-this.activeDraggable.endDrag(event);
-this.activeDraggable = null;
-},
-keyPress: function(event) {
-if(this.activeDraggable)
-this.activeDraggable.keyPress(event);
-},
-addObserver: function(observer) {
-this.observers.push(observer);
-this._cacheObserverCallbacks();
-},
-removeObserver: function(element) { this.observers = this.observers.reject( function(o) { return o.element==element });
-this._cacheObserverCallbacks();
-},
-notify: function(eventName, draggable, event) { if(this[eventName+'Count'] > 0)
-this.observers.each( function(o) {
-if(o[eventName]) o[eventName](eventName, draggable, event);
-});
-},
-_cacheObserverCallbacks: function() {
-['onStart','onEnd','onDrag'].each( function(eventName) {
-Draggables[eventName+'Count'] = Draggables.observers.select(
-function(o) { return o[eventName]; }
-).length;
-});
-}
-}
-var Draggable = Class.create();
-Draggable.prototype = {
-initialize: function(element) {
-var options = Object.extend({
-handle: false,
-starteffect: function(element) {
-element._opacity = Element.getOpacity(element);
-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;
-element._revert = new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur});
-},
-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});
-},
-zindex: 1000,
-revert: false,
-scroll: false,
-scrollSensitivity: 20,
-scrollSpeed: 15,
-snap: false}, 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(!this.handle) this.handle = $(options.handle);
-if(!this.handle) this.handle = this.element;
-if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML)
-options.scroll = $(options.scroll);
-Element.makePositioned(this.element);
-this.delta= this.currentDelta();
-this.options= options;
-this.dragging = false;
-this.eventMouseDown = this.initDrag.bindAsEventListener(this);
-Event.observe(this.handle, "mousedown", this.eventMouseDown);
-Draggables.register(this);
-},
-destroy: function() {
-Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
-Draggables.unregister(this);
-},
-currentDelta: function() {
-return([
-parseInt(Element.getStyle(this.element,'left') || '0'),
-parseInt(Element.getStyle(this.element,'top') || '0')]);
-},
-initDrag: function(event) {
-if(Event.isLeftClick(event)) {
- 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(this.element._revert) {
-this.element._revert.cancel();
-this.element._revert = 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]) });
-Draggables.activate(this);
-Event.stop(event);
-}
-},
-startDrag: function(event) {
-this.dragging = true;
-if(this.options.zindex) {
-this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
-this.element.style.zIndex = this.options.zindex;
-}
-if(this.options.ghosting) {
-this._clone = this.element.cloneNode(true);
-Position.absolutize(this.element);
-this.element.parentNode.insertBefore(this._clone, this.element);
-}
-if(this.options.scroll) {
-if (this.options.scroll == window) {
-var where = this._getWindowScroll(this.options.scroll);
-this.originalScrollLeft = where.left;
-this.originalScrollTop = where.top;
-} else {
-this.originalScrollLeft = this.options.scroll.scrollLeft;
-this.originalScrollTop = this.options.scroll.scrollTop;
-}
-}
-Draggables.notify('onStart', this, event);
-if(this.options.starteffect) this.options.starteffect(this.element);
-},
-updateDrag: function(event, pointer) {
-if(!this.dragging) this.startDrag(event);
-Position.prepare();
-Droppables.show(pointer, this.element);
-Draggables.notify('onDrag', this, event);
-this.draw(pointer);
-if(this.options.change) this.options.change(this);
-if(this.options.scroll) {
-this.stopScrolling();
-var p;
-if (this.options.scroll == window) {
-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.push(p[0]+this.options.scroll.offsetWidth);
-p.push(p[1]+this.options.scroll.offsetHeight);
-}
-var speed = [0,0];
-if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
-if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
-if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
-if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
-this.startScrolling(speed);
-}
-if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
-Event.stop(event);
-},
-finishDrag: function(event, success) {
-this.dragging = false;
-if(this.options.ghosting) {
-Position.relativize(this.element);
-Element.remove(this._clone);
-this._clone = null;
-}
-if(success) Droppables.fire(event, this.element);
-Draggables.notify('onEnd', this, event);
-var revert = this.options.revert;
-if(revert && typeof revert == 'function') revert = revert(this.element);
-var d = this.currentDelta();
-if(revert && this.options.reverteffect) {
-this.options.reverteffect(this.element,
-d[1]-this.delta[1], d[0]-this.delta[0]);
-} else {
-this.delta = d;
-}
-if(this.options.zindex)
-this.element.style.zIndex = this.originalZ;
-if(this.options.endeffect)
-this.options.endeffect(this.element);
-Draggables.deactivate(this);
-Droppables.reset();
-},
-keyPress: function(event) {
-if(event.keyCode!=Event.KEY_ESC) return;
-this.finishDrag(event, false);
-Event.stop(event);
-},
-endDrag: function(event) {
-if(!this.dragging) return;
-this.stopScrolling();
-this.finishDrag(event, true);
-Event.stop(event);
-},
-draw: function(point) {
-var pos = Position.cumulativeOffset(this.element);
-var d = this.currentDelta();
-pos[0] -= d[0]; pos[1] -= d[1];
-if(this.options.scroll && (this.options.scroll != window)) {
-pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
-pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
-}
-var p = [0,1].map(function(i){
-return (point[i]-pos[i]-this.offset[i])
-}.bind(this));
-if(this.options.snap) {
-if(typeof this.options.snap == 'function') {
-p = this.options.snap(p[0],p[1],this);
-} else {
-if(this.options.snap instanceof Array) {
-p = p.map( function(v, i) {
-return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this))
-} else {
-p = p.map( function(v) {
-return Math.round(v/this.options.snap)*this.options.snap }.bind(this))
-}
-}}
-var style = this.element.style;
-if((!this.options.constraint) || (this.options.constraint=='horizontal'))
-style.left = p[0] + "px";
-if((!this.options.constraint) || (this.options.constraint=='vertical'))
-style.top= p[1] + "px";
-if(style.visibility=="hidden") style.visibility = "";},
-stopScrolling: function() {
-if(this.scrollInterval) {
-clearInterval(this.scrollInterval);
-this.scrollInterval = null;
-Draggables._lastScrollPointer = null;
-}
-},
-startScrolling: function(speed) {
-this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
-this.lastScrolled = new Date();
-this.scrollInterval = setInterval(this.scroll.bind(this), 10);
-},
-scroll: function() {
-var current = new Date();
-var delta = current - this.lastScrolled;
-this.lastScrolled = current;
-if(this.options.scroll == window) {
-with (this._getWindowScroll(this.options.scroll)) {
-if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
-var d = delta / 1000;
-this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
-}
-}
-} else {
-this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
-this.options.scroll.scrollTop+= this.scrollSpeed[1] * delta / 1000;
-}
-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.options.change) this.options.change(this);
-},
-_getWindowScroll: function(w) {
-var T, L, W, H;
-with (w.document) {
-if (w.document.documentElement && documentElement.scrollTop) {
-T = documentElement.scrollTop;
-L = documentElement.scrollLeft;
-} else if (w.document.body) {
-T = body.scrollTop;
-L = body.scrollLeft;
-}
-if (w.innerWidth) {
-W = w.innerWidth;
-H = w.innerHeight;
-} else if (w.document.documentElement && documentElement.clientWidth) {
-W = documentElement.clientWidth;
-H = documentElement.clientHeight;
-} else {
-W = body.offsetWidth;
-H = body.offsetHeight
-}
-}
-return { top: T, left: L, width: W, height: H };
-}
-}
-var SortableObserver = Class.create();
-SortableObserver.prototype = {
-initialize: function(element, observer) {
-this.element = $(element);
-this.observer= observer;
-this.lastValue = Sortable.serialize(this.element);
-},
-onStart: function() {
-this.lastValue = Sortable.serialize(this.element);
-},
-onEnd: function() {
-Sortable.unmark();
-if(this.lastValue != Sortable.serialize(this.element))
-this.observer(this.element)
-}
-}
-var Sortable = {
-sortables: {},
-_findRootElement: function(element) {
-while (element.tagName != "BODY") {
-if(element.id && Sortable.sortables[element.id]) return element;
-element = element.parentNode;
-}
-},
-options: function(element) {
-element = Sortable._findRootElement($(element));
-if(!element) return;
-return Sortable.sortables[element.id];
-},
-destroy: function(element){
-var s = Sortable.options(element);
-if(s) {
-Draggables.removeObserver(s.element);
-s.droppables.each(function(d){ Droppables.remove(d) });
-s.draggables.invoke('destroy');
-delete Sortable.sortables[s.element.id];
-}
-},
-create: function(element) {
-element = $(element);
-var options = Object.extend({
-element: element,
-tag: 'li',dropOnEmpty: false,
-tree:false,
-treeTag: 'ul',
-overlap: 'vertical',constraint:'vertical',containment: element, handle:false, only:false,
-hoverclass:null,
-ghosting:false,
-scroll:false,
-scrollSensitivity: 20,
-scrollSpeed: 15,
-format:/^[^_]*_(.*)$/,
-onChange:Prototype.emptyFunction,
-onUpdate:Prototype.emptyFunction
-}, arguments[1] || {});
-this.destroy(element);
-var options_for_draggable = {
-revert:true,
-scroll:options.scroll,
-scrollSpeed: options.scrollSpeed,
-scrollSensitivity: options.scrollSensitivity,
-ghosting:options.ghosting,
-constraint:options.constraint,
-handle:options.handle };
-if(options.starteffect)
-options_for_draggable.starteffect = options.starteffect;
-if(options.reverteffect)
-options_for_draggable.reverteffect = options.reverteffect;
-else
-if(options.ghosting) options_for_draggable.reverteffect = function(element) {
-element.style.top= 0;
-element.style.left = 0;
-};
-if(options.endeffect)
-options_for_draggable.endeffect = options.endeffect;
-if(options.zindex)
-options_for_draggable.zindex = options.zindex;
-var options_for_droppable = {
-overlap: options.overlap,
-containment: options.containment,
-tree:options.tree,
-hoverclass:options.hoverclass,
-onHover: Sortable.onHover
- }
-var options_for_tree = {
-onHover:Sortable.onEmptyHover,
-overlap:options.overlap,
-containment:options.containment,
-hoverclass: options.hoverclass
-}
-Element.cleanWhitespace(element);
-options.draggables = [];
-options.droppables = [];
-if(options.dropOnEmpty || options.tree) {
-Droppables.add(element, options_for_tree);
-options.droppables.push(element);
-}
-(this.findElements(element, options) || []).each( function(e) {
- var handle = options.handle ?
-Element.childrenWithClassName(e, options.handle)[0] : e;
-options.draggables.push(
-new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
-Droppables.add(e, options_for_droppable);
-if(options.tree) e.treeNode = element;
-options.droppables.push(e);
-});
-if(options.tree) {
-(Sortable.findTreeElements(element, options) || []).each( function(e) {
-Droppables.add(e, options_for_tree);
-e.treeNode = element;
-options.droppables.push(e);
-});
-}
-this.sortables[element.id] = options;
-Draggables.addObserver(new SortableObserver(element, options.onUpdate));
-},
-findElements: function(element, options) {
-return Element.findChildren(
-element, options.only, options.tree ? true : false, options.tag);
-},
-findTreeElements: function(element, options) {
-return Element.findChildren(
-element, options.only, options.tree ? true : false, options.treeTag);
-},
-onHover: function(element, dropon, overlap) {
-if(Element.isParent(dropon, element)) return;
-if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
-return;
-} else if(overlap>0.5) {
-Sortable.mark(dropon, 'before');
-if(dropon.previousSibling != element) {
-var oldParentNode = element.parentNode;
-element.style.visibility = "hidden";dropon.parentNode.insertBefore(element, dropon);
-if(dropon.parentNode!=oldParentNode)
-Sortable.options(oldParentNode).onChange(element);
-Sortable.options(dropon.parentNode).onChange(element);
-}
-} else {
-Sortable.mark(dropon, 'after');
-var nextElement = dropon.nextSibling || null;
-if(nextElement != element) {
-var oldParentNode = element.parentNode;
-element.style.visibility = "hidden";dropon.parentNode.insertBefore(element, nextElement);
-if(dropon.parentNode!=oldParentNode)
-Sortable.options(oldParentNode).onChange(element);
-Sortable.options(dropon.parentNode).onChange(element);
-}
-}
-},
-onEmptyHover: function(element, dropon, overlap) {
-var oldParentNode = element.parentNode;
-var droponOptions = Sortable.options(dropon);
-if(!Element.isParent(dropon, element)) {
-var index;
-var children = Sortable.findElements(dropon, {tag: droponOptions.tag});
-var child = null;
-if(children) {
-var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
-for (index = 0; index < children.length; index += 1) {
-if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
-offset -= Element.offsetSize (children[index], droponOptions.overlap);
-} else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
-child = index + 1 < children.length ? children[index + 1] : null;
-break;
-} else {
-child = children[index];
-break;
-}
-}
-}
-dropon.insertBefore(element, child);
-Sortable.options(oldParentNode).onChange(element);
-droponOptions.onChange(element);
-}
-},
-unmark: function() {
-if(Sortable._marker) Element.hide(Sortable._marker);
-},
-mark: function(dropon, position) {
- var sortable = Sortable.options(dropon.parentNode);
-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';
-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';
-if(position=='after')
-if(sortable.overlap == 'horizontal')
-Sortable._marker.style.left = (offsets[0]+dropon.clientWidth) + 'px';
-else
-Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px';
-Element.show(Sortable._marker);
-},
-_tree: function(element, options, parent) {
-var children = Sortable.findElements(element, options) || [];
-for (var i = 0; i < children.length; ++i) {
-var match = children[i].id.match(options.format);
-if (!match) continue;
-var child = {
-id: encodeURIComponent(match ? match[1] : null),
-element: element,
-parent: parent,
-children: new Array,
-position: parent.children.length,
-container: Sortable._findChildrenElement(children[i], options.treeTag.toUpperCase())
-}
-if (child.container)
-this._tree(child.container, options, child)
-parent.children.push (child);
-}
-return parent;
-},
-_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);
-var options = Object.extend({
-tag: sortableOptions.tag,
-treeTag: sortableOptions.treeTag,
-only: sortableOptions.only,
-name: element.id,
-format: sortableOptions.format
-}, arguments[1] || {});
-var root = {
-id: null,
-parent: null,
-children: new Array,
-container: element,
-position: 0
-}
-return Sortable._tree (element, options, root);
-},
-_constructIndex: function(node) {
-var index = '';
-do {
-if (node.id) index = '[' + node.position + ']' + index;
-} while ((node = node.parent) != null);
-return index;
-},
-sequence: function(element) {
-element = $(element);
-var options = Object.extend(this.options(element), arguments[1] || {});
-return $(this.findElements(element, options) || []).map( function(item) {
-return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
-});
-},
-setSequence: function(element, new_sequence) {
-element = $(element);
-var options = Object.extend(this.options(element), arguments[2] || {});
-var nodeMap = {};
-this.findElements(element, options).each( function(n) {
-if (n.id.match(options.format))
-nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
-n.parentNode.removeChild(n);
-});
-new_sequence.each(function(ident) {
-var n = nodeMap[ident];
-if (n) {
-n[1].appendChild(n[0]);
-delete nodeMap[ident];
-}
-});
-},
-serialize: function(element) {
-element = $(element);
-var options = Object.extend(Sortable.options(element), arguments[1] || {});
-var name = encodeURIComponent(
-(arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
-if (options.tree) {
-return Sortable.tree(element, arguments[1]).children.map( function (item) {
-return [name + Sortable._constructIndex(item) + "=" +
-encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
-}).flatten().join('&');
-} else {
-return Sortable.sequence(element, arguments[1]).map( function(item) {
-return name + "[]=" + encodeURIComponent(item);
-}).join('&');
-}
-}
-}
-Element.isParent = function(child, element) {
-if (!child.parentNode || child == element) return false;
-if (child.parentNode == element) return true;
-return Element.isParent(child.parentNode, element);
-}
-Element.findChildren = function(element, only, recursive, tagName) {
-if(!element.hasChildNodes()) return null;
-tagName = tagName.toUpperCase();
-if(only) only = [only].flatten();
-var elements = [];
-$A(element.childNodes).each( function(e) {
-if(e.tagName && e.tagName.toUpperCase()==tagName &&
-(!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
-elements.push(e);
-if(recursive) {
-var grandchildren = Element.findChildren(e, only, recursive, tagName);
-if(grandchildren) elements.push(grandchildren);
-}
-});
-return (elements.length>0 ? elements.flatten() : []);
-}
-Element.offsetSize = function (element, type) {
-if (type == 'vertical' || type == 'height')
-return element.offsetHeight;
-else
-return element.offsetWidth;
-}
-if(!Control) var Control = {};
-Control.Slider = Class.create();
-Control.Slider.prototype = {
-initialize: function(handle, track, options) {
-var slider = this;
-if(handle instanceof Array) {
-this.handles = handle.collect( function(e) { return $(e) });
-} else {
-this.handles = [$(handle)];
-}
-this.track = $(track);
-this.options = options || {};
-this.axis= this.options.axis || 'horizontal';
-this.increment = this.options.increment || 1;
-this.step= parseInt(this.options.step || '1');
-this.range = this.options.range || $R(0,1);
-this.value = 0;this.values= this.handles.map( function() { return 0 });
-this.spans = this.options.spans ? this.options.spans.map(function(s){ return $(s) }) : false;
-this.options.startSpan = $(this.options.startSpan || null);
-this.options.endSpan = $(this.options.endSpan || null);
-this.restricted = this.options.restricted || false;
-this.maximum = this.options.maximum || this.range.end;
-this.minimum = this.options.minimum || this.range.start;
-this.alignX = parseInt(this.options.alignX || '0');
-this.alignY = parseInt(this.options.alignY || '0');
-this.trackLength = this.maximumOffset() - this.minimumOffset();
-this.handleLength = this.isVertical() ? this.handles[0].offsetHeight : this.handles[0].offsetWidth;
-this.active = false;
-this.dragging = false;
-this.disabled = false;
-if(this.options.disabled) this.setDisabled();
-this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false;
-if(this.allowedValues) {
-this.minimum = this.allowedValues.min();
-this.maximum = this.allowedValues.max();
-}
-this.eventMouseDown = this.startDrag.bindAsEventListener(this);
-this.eventMouseUp = this.endDrag.bindAsEventListener(this);
-this.eventMouseMove = this.update.bindAsEventListener(this);
-this.handles.each( function(h,i) {
-i = slider.handles.length-1-i;
-slider.setValue(parseFloat(
-(slider.options.sliderValue instanceof Array ?
-slider.options.sliderValue[i] : slider.options.sliderValue) ||
- slider.range.start), i);
-Element.makePositioned(h);Event.observe(h, "mousedown", slider.eventMouseDown);
-});
-Event.observe(this.track, "mousedown", this.eventMouseDown);
-Event.observe(document, "mouseup", this.eventMouseUp);
-Event.observe(document, "mousemove", this.eventMouseMove);
-this.initialized = true;
-},
-dispose: function() {
-var slider = this;
-Event.stopObserving(this.track, "mousedown", this.eventMouseDown);
-Event.stopObserving(document, "mouseup", this.eventMouseUp);
-Event.stopObserving(document, "mousemove", this.eventMouseMove);
-this.handles.each( function(h) {
-Event.stopObserving(h, "mousedown", slider.eventMouseDown);
-});
-},
-setDisabled: function(){
-this.disabled = true;
-},
-setEnabled: function(){
-this.disabled = false;
-},
-getNearestValue: function(value){
-if(this.allowedValues){
-if(value >= this.allowedValues.max()) return(this.allowedValues.max());
-if(value <= this.allowedValues.min()) return(this.allowedValues.min());
-var offset = Math.abs(this.allowedValues[0] - value);
-var newValue = this.allowedValues[0];
-this.allowedValues.each( function(v) {
-var currentOffset = Math.abs(v - value);
-if(currentOffset <= offset){
-newValue = v;
-offset = currentOffset;
-}
-});
-return newValue;
-}
-if(value > this.range.end) return this.range.end;
-if(value < this.range.start) return this.range.start;
-return value;
-},
-setValue: function(sliderValue, handleIdx){
-if(!this.active) {
-this.activeHandle= this.handles[handleIdx];
-this.activeHandleIdx = handleIdx;
-this.updateStyles();
-}
-handleIdx = handleIdx || this.activeHandleIdx || 0;
-if(this.initialized && this.restricted) {
-if((handleIdx>0) && (sliderValue<this.values[handleIdx-1]))
-sliderValue = this.values[handleIdx-1];
-if((handleIdx < (this.handles.length-1)) && (sliderValue>this.values[handleIdx+1]))
-sliderValue = this.values[handleIdx+1];
-}
-sliderValue = this.getNearestValue(sliderValue);
-this.values[handleIdx] = sliderValue;
-this.value = this.values[0];
-this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] =
-this.translateToPx(sliderValue);
-this.drawSpans();
-if(!this.dragging || !this.event) this.updateFinished();
-},
-setValueBy: function(delta, handleIdx) {
-this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta,
-handleIdx || this.activeHandleIdx || 0);
-},
-translateToPx: function(value) {
-return Math.round(
-((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) *
-(value - this.range.start)) + "px";
-},
-translateToValue: function(offset) {
-return ((offset/(this.trackLength-this.handleLength) *
-(this.range.end-this.range.start)) + this.range.start);
-},
-getRange: function(range) {
-var v = this.values.sortBy(Prototype.K);
-range = range || 0;
-return $R(v[range],v[range+1]);
-},
-minimumOffset: function(){
-return(this.isVertical() ? this.alignY : this.alignX);
-},
-maximumOffset: function(){
-return(this.isVertical() ?
-this.track.offsetHeight - this.alignY : this.track.offsetWidth - this.alignX);
-},
-isVertical:function(){
-return (this.axis == 'vertical');
-},
-drawSpans: function() {
-var slider = this;
-if(this.spans)
-$R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) });
-if(this.options.startSpan)
-this.setSpan(this.options.startSpan,
-$R(0, this.values.length>1 ? this.getRange(0).min() : this.value ));
-if(this.options.endSpan)
-this.setSpan(this.options.endSpan,
-$R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum));
-},
-setSpan: function(span, range) {
-if(this.isVertical()) {
-span.style.top = this.translateToPx(range.start);
-span.style.height = this.translateToPx(range.end - range.start + this.range.start);
-} else {
-span.style.left = this.translateToPx(range.start);
-span.style.width = this.translateToPx(range.end - range.start + this.range.start);
-}
-},
-updateStyles: function() {
-this.handles.each( function(h){ Element.removeClassName(h, 'selected') });
-Element.addClassName(this.activeHandle, 'selected');
-},
-startDrag: function(event) {
-if(Event.isLeftClick(event)) {
-if(!this.disabled){
-this.active = true;
-var handle = Event.element(event);
-var pointer= [Event.pointerX(event), Event.pointerY(event)];
-if(handle==this.track) {
-var offsets= Position.cumulativeOffset(this.track);
-this.event = event;
-this.setValue(this.translateToValue(
- (this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2)
-));
-var offsets= Position.cumulativeOffset(this.activeHandle);
-this.offsetX = (pointer[0] - offsets[0]);
-this.offsetY = (pointer[1] - offsets[1]);
-} else {
- 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]);
-}
-}
-Event.stop(event);
-}
-},
-update: function(event) {
- if(this.active) {
-if(!this.dragging) this.dragging = true;
-this.draw(event);
- if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
-Event.stop(event);
- }
-},
-draw: function(event) {
-var pointer = [Event.pointerX(event), Event.pointerY(event)];
-var offsets = Position.cumulativeOffset(this.track);
-pointer[0] -= this.offsetX + offsets[0];
-pointer[1] -= this.offsetY + offsets[1];
-this.event = event;
-this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] ));
-if(this.initialized && this.options.onSlide)
-this.options.onSlide(this.values.length>1 ? this.values : this.value, this);
-},
-endDrag: function(event) {
-if(this.active && this.dragging) {
-this.finishDrag(event, true);
-Event.stop(event);
-}
-this.active = false;
-this.dragging = false;
-},
-finishDrag: function(event, success) {
-this.active = false;
-this.dragging = false;
-this.updateFinished();
-},
-updateFinished: function() {
-if(this.initialized && this.options.onChange)
-this.options.onChange(this.values.length>1 ? this.values : this.value, this);
-this.event = null;
-}
-}
-Prado.AutoCompleter = Class.create();
-Prado.AutoCompleter.Base = function(){};
-Prado.AutoCompleter.Base.prototype = Object.extend(Autocompleter.Base.prototype,
-{
-updateElement: function(selectedElement)
-{
-if (this.options.updateElement) {
-this.options.updateElement(selectedElement);
-return;
-}
-var value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
-var lastTokenPos = this.findLastToken();
-if (lastTokenPos != -1) {
-var newValue = this.element.value.substr(0, lastTokenPos + 1);
-var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
-if (whitespace)
-newValue += whitespace[0];
-this.element.value = (newValue + value).trim();
-} else {
-this.element.value = value.trim();
-}
-this.element.focus();
-if (this.options.afterUpdateElement)
-this.options.afterUpdateElement(this.element, selectedElement);
-}
-});
-Prado.AutoCompleter.prototype = Object.extend(new Autocompleter.Base(),
-{
-initialize : function(element, update, options)
-{
-this.baseInitialize(element, update, options);
-},
-onUpdateReturn : function(result)
-{
-if(isString(result) && result.length > 0)
-this.updateChoices(result);
-},
-getUpdatedChoices : function()
-{
-Prado.Callback(this.element.id, this.getToken(), this.onUpdateReturn.bind(this));
-}
-});
-Prado.ActivePanel =
-{
-callbacks : {},
-register : function(id, options)
-{
-Prado.ActivePanel.callbacks[id] = options;
-},
-update : function(id, param)
-{
-var request = new Prado.ActivePanel.Request(id,
-Prado.ActivePanel.callbacks[id]);
-request.callback(param);
-}
-}
-Prado.ActivePanel.Request = Class.create();
-Prado.ActivePanel.Request.prototype =
-{
-initialize : function(element, options)
-{
-this.element = element;
-this.setOptions(options);
-},
-setOptions : function(options)
-{
-this.options =
-{
-onSuccess : this.onSuccess.bind(this)
-}
-Object.extend(this.options, options || {});
-},
-callback : function(param)
-{
-this.options.params = [param];
-new Prado.AJAX.Callback(this.element, this.options);
-},
-onSuccess : function(result, output)
-{
-if(this.options.update)
-{
-if (!this.options.evalScripts)
-output = output.stripScripts();
-Element.update(this.options.update, output);
-}
-}
-}
-Prado.DropContainer = Class.create();
-Prado.DropContainer.prototype = Object.extend(new Prado.ActivePanel.Request(),
-{
-initialize : function(element, options)
-{
-this.element = element;
-this.setOptions(options);
-Object.extend(this.options,
-{
-onDrop : this.onDrop.bind(this),
-evalScripts : true,
-onSuccess : options.onSuccess || this.onSuccess.bind(this)
-});
-Droppables.add(element, this.options);
-},
-onDrop : function(draggable, droppable)
-{
-this.callback(draggable.id)
-}
-});
-Prado.ActiveImageButton = Class.create();
-Prado.ActiveImageButton.prototype =
-{
-initialize : function(element, options)
-{
-this.element = $(element);
-this.options = options;
-Event.observe(this.element, "click", this.click.bind(this));
-},
-click : function(e)
-{
-var el = $('{$this->ClientID}');
-var imagePos = Position.cumulativeOffset(this.element);
-var clickedPos = [e.clientX, e.clientY];
-var param = (clickedPos[0]-imagePos[0]+1)+","+(clickedPos[1]-imagePos[1]+1);
-Prado.Callback(this.element, param, null, this.options);
-Event.stop(e);
-}
-}
diff --git a/framework/Web/Javascripts/prado/ajax3.js b/framework/Web/Javascripts/prado/ajax3.js
new file mode 100644
index 00000000..ded63104
--- /dev/null
+++ b/framework/Web/Javascripts/prado/ajax3.js
@@ -0,0 +1,181 @@
+/**
+ * Prado Callback client-side request handler.
+ */
+Prado.Callback = Class.create();
+
+/**
+ * Static definitions.
+ */
+Object.extend(Prado.Callback,
+{
+ /**
+ * Callback request target POST field name.
+ */
+ FIELD_CALLBACK_TARGET : 'PRADO_CALLBACK_TARGET',
+
+ /**
+ * Callback request parameter POST field name.
+ */
+ FIELD_CALLBACK_PARAMETER : 'PRADO_CALLBACK_PARAMETER',
+
+ /**
+ * List of form fields that will be collected during callback.
+ */
+ PostDataLoaders : ['PRADO_PAGESTATE'],
+
+ /**
+ * Respond to Prado Callback request exceptions.
+ */
+ Exception :
+ {
+ /**
+ * Server returns 505 exception. Just log it.
+ */
+ "on505" : function(request, transport, data)
+ {
+ var msg = 'HTTP '+transport.status+" with response";
+ Logger.error(msg, transport.responseText);
+ this.logException(data);
+ },
+
+ /**
+ * Callback OnComplete event,logs reponse and data to console.
+ */
+ onComplete : function(request, transport, data)
+ {
+ if(transport.status != 505)
+ {
+ var msg = 'HTTP '+transport.status+" with response : \n";
+ msg += transport.responseText + "\n";
+ msg += "Data : \n"+inspect(data);
+ Logger.warn(msg);
+ }
+ },
+
+ /**
+ * Formats the exception message for display in console.
+ */
+ formatException : function(e)
+ {
+ var msg = e.type + " with message \""+e.message+"\"";
+ msg += " in "+e.file+"("+e.line+")\n";
+ msg += "Stack trace:\n";
+ var trace = e.trace;
+ for(var i = 0; i<trace.length; i++)
+ {
+ msg += " #"+i+" "+trace[i].file;
+ msg += "("+trace[i].line+"): ";
+ msg += trace[i]["class"]+"->"+trace[i]["function"]+"()"+"\n";
+ }
+ return msg;
+ },
+
+ /**
+ * Log Callback response exceptions to console.
+ */
+ logException : function(e)
+ {
+ Logger.error("Callback Request Error "+e.code, this.formatException(e));
+ }
+ },
+
+ /**
+ * @return string JSON encoded data.
+ */
+ encode : function(data)
+ {
+ Prado.JSON.stringify(data);
+ },
+
+ /**
+ * @return mixed javascript data decoded from string using JSON decoding.
+ */
+ decode : function(data)
+ {
+ return Prado.JSON.parse(data);
+ }
+})
+
+//Add HTTP exception respones when logger is enabled.
+Event.OnLoad(function()
+{
+ if(typeof Logger != "undefined")
+ Ajax.Responders.register(Prado.Callback.Exception);
+});
+
+/**
+ * Create and prepare a new callback request.
+ */
+Prado.Callback.prototype =
+{
+ /**
+ * Callback URL, same url as the current page.
+ */
+ url : window.location.href,
+
+ /**
+ * Callback options, including onXXX events.
+ */
+ options : {},
+
+ /**
+ * Callback target ID. E.g. $control->getUniqueID();
+ */
+ id : null,
+
+ /**
+ * Callback parameters.
+ */
+ parameters : null,
+
+ /**
+ * Prepare and inititate a callback request.
+ */
+ initialize : function(id, parameters, onSuccess, options)
+ {
+ this.options = options || {};
+ this.id = id;
+ this.parameters = parameters;
+
+ var request =
+ {
+ postBody : this._getPostData(),
+ onSuccess : this._onSuccess.bind(this)
+ }
+ Object.extend(this.options || {},request);
+
+ new Ajax.Request(this.url, this.options);
+ },
+
+ /**
+ * Collects the form inputs, encode the parameters, and sets the callback
+ * target id. The resulting string is the request content body.
+ * @return string request body content containing post data.
+ */
+ _getPostData : function()
+ {
+ var data = {};
+
+ Prado.Callback.PostDataLoaders.each(function(name)
+ {
+ $A(document.getElementsByName(name)).each(function(element)
+ {
+ var value = $F(element);
+ if(typeof(value) != "undefined")
+ data[name] = value;
+ })
+ })
+ if(typeof(this.parameters) != "undefined")
+ data[Prado.Callback.FIELD_CALLBACK_PARAMETER] = Prado.Callback.encode(this.parameters);
+ data[Prado.Callback.FIELD_CALLBACK_TARGET] = this.id;
+ return $H(data).toQueryString();
+ },
+
+ /**
+ * Dispatch a successfull response to the appropriate responders.
+ */
+ _onSuccess : function(response, transport, json)
+ {
+ //Logger.info("asd");
+ }
+} \ No newline at end of file
diff --git a/framework/Web/Javascripts/prototype/ajax.js b/framework/Web/Javascripts/prototype/ajax.js
index 7ec7716c..9fcb473b 100644
--- a/framework/Web/Javascripts/prototype/ajax.js
+++ b/framework/Web/Javascripts/prototype/ajax.js
@@ -116,7 +116,7 @@ Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
var requestHeaders =
['X-Requested-With', 'XMLHttpRequest',
'X-Prototype-Version', Prototype.Version,
- 'Accept', 'text/javascript, text/html, application/xml, text/xml, */*'];
+ 'Accept', 'text/javascript, text/html, application/xml, text/xml'];
if (this.options.method == 'post') {
requestHeaders.push('Content-type', this.options.contentType);
diff --git a/framework/Web/UI/ActiveControls/TActiveControl.php b/framework/Web/UI/ActiveControls/TActiveControl.php
new file mode 100644
index 00000000..d289bab9
--- /dev/null
+++ b/framework/Web/UI/ActiveControls/TActiveControl.php
@@ -0,0 +1,23 @@
+<?php
+
+/*
+ * Created on 25/04/2006
+ */
+
+class TActiveControl extends TControl implements ICallbackEventHandler, IActiveControl
+{
+ public function __construct()
+ {
+ parent::__construct();
+ $this->setAdapter(new TActiveControlAdapter($this));
+ }
+
+ public function raiseCallbackEvent($param)
+ {
+ var_dump($param);
+ $client = $this->getPage()->getCallbackClient();
+ $client->hide($this);
+ }
+}
+
+?>
diff --git a/framework/Web/UI/ActiveControls/TActiveControlAdapter.php b/framework/Web/UI/ActiveControls/TActiveControlAdapter.php
new file mode 100644
index 00000000..187b2cac
--- /dev/null
+++ b/framework/Web/UI/ActiveControls/TActiveControlAdapter.php
@@ -0,0 +1,25 @@
+<?php
+/*
+ * Created on 29/04/2006
+ */
+
+class TActiveControlAdapter extends TControlAdapter
+{
+ private static $_renderedPosts = false;
+
+ /**
+ * Render the callback request post data loaders once only.
+ */
+ public function render($writer)
+ {
+ if(!self::$_renderedPosts)
+ {
+ $options = TJavascript::encode($this->getPage()->getPostDataLoaders(),false);
+ $script = "Prado.Callback.PostDataLoaders.concat({$options});";
+ $this->getPage()->getClientScript()->registerEndScript(get_class($this), $script);
+ self::$_renderedPosts = true;
+ }
+ parent::render($writer);
+ }
+}
+?>
diff --git a/framework/Web/UI/ActiveControls/TActivePageAdapter.php b/framework/Web/UI/ActiveControls/TActivePageAdapter.php
new file mode 100644
index 00000000..ab042d54
--- /dev/null
+++ b/framework/Web/UI/ActiveControls/TActivePageAdapter.php
@@ -0,0 +1,219 @@
+<?php
+/**
+ * TActivePageAdapter class file
+ *
+ * @author Wei Zhuo <weizhuo[at]gamil[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Revision: $ $Date: $
+ * @package System.Web.UI.ActiveControls
+ */
+
+/**
+ * TActivePageAdapter class.
+ *
+ * Callback request page handler.
+ *
+ * @author Wei Zhuo <weizhuo[at]gamil[dot]com>
+ * @version $Revision: $ $Date: $
+ * @package System.Web.UI.ActiveControls
+ * @since 3.0
+ */
+class TActivePageAdapter extends TControlAdapter
+{
+ /**
+ * @var ICallbackEventHandler callback event handler.
+ */
+ private $_callbackEventTarget;
+ /**
+ * @var mixed callback event parameter.
+ */
+ private $_callbackEventParameter;
+ /**
+ * @var TCallbackClientScript callback client script handler
+ */
+ private $_callbackClient;
+ /**
+ * @var TCallbackResponse callback response handler.
+ */
+ private $_callbackResponse;
+
+ private $_callbackEventResult;
+
+ /**
+ * Constructor, trap errors and exception to let the callback response
+ * handle them.
+ */
+ public function __construct(TPage $control)
+ {
+ parent::__construct($control);
+ $this->trapCallbackErrorsExceptions();
+ }
+
+ /**
+ * Process the callback request.
+ */
+ public function processCallbackEvent($writer)
+ {
+ Prado::trace("ActivePage raiseCallbackEvent()",'System.Web.UI.ActiveControls.TActivePageAdapter');
+ $this->raiseCallbackEvent();
+ }
+
+ protected function trapCallbackErrorsExceptions()
+ {
+ //TODO: How to trap the errors and exceptions and return them
+ // as part of the response.
+ }
+
+ public function renderCallbackResponse($writer)
+ {
+ Prado::trace("ActivePage renderCallbackResponse()",'System.Web.UI.ActiveControls.TActivePageAdapter');
+ $this->renderResponse($writer);
+ }
+
+ protected function renderResponse($writer)
+ {
+ //var_dump(getallheaders());
+ //TODO: How to render the response, it will contain 3 pieces of data
+ // 1) The arbituary data returned to the client-side callback handler
+ // 2) client-side function call statements
+ // 3) Content body, which may need to be partitioned
+
+ /*
+ $response = $this->getCallbackResponseHandler();
+ $response->writeClientScriptResponse($this->getCallbackClientHandler());
+ $response->writeResponseData($this->getCallbackEventResult());
+ $response->flush();
+ */
+ }
+
+ /**
+ * Trys to find the callback event handler and raise its callback event.
+ * @throws TInvalidCallbackRequestException if call back target is not
+ * found.
+ * @throws TInvalidCallbackHandlerException if the requested target does not
+ * implement ICallbackEventHandler.
+ */
+ private function raiseCallbackEvent()
+ {
+ if(($callbackHandler=$this->getCallbackEventTarget())!==null)
+ {
+ if($callbackHandler instanceof ICallbackEventHandler)
+ $callbackHandler->raiseCallbackEvent($this->getCallbackEventParameter());
+ else
+ throw new TInvalidCallbackHandlerException($callbackHandler->getUniqueID());
+ }
+ else
+ {
+ $target = $this->getRequest()->itemAt(TPage::FIELD_CALLBACK_TARGET);
+ throw new TInvalidCallbackRequestException($target);
+ }
+ }
+
+ /**
+ * @return mixed callback event result.
+ */
+ public function getCallbackEventResult()
+ {
+ return $this->_callbackEventResult->getResult();
+ }
+
+ /**
+ * @return TControl the control responsible for the current callback event,
+ * null if nonexistent
+ */
+ public function getCallbackEventTarget()
+ {
+ if($this->_callbackEventTarget===null)
+ {
+ $eventTarget=$this->getRequest()->itemAt(TPage::FIELD_CALLBACK_TARGET);
+ if(!empty($eventTarget))
+ $this->_callbackEventTarget=$this->getPage()->findControl($eventTarget);
+ }
+ return $this->_callbackEventTarget;
+ }
+
+ /**
+ * Registers a control to raise callback event in the current request.
+ * @param TControl control registered to raise callback event.
+ */
+ public function setCallbackEventTarget(TControl $control)
+ {
+ $this->_callbackEventTarget=$control;
+ }
+
+ /**
+ * Callback parameter is decoded assuming JSON encoding.
+ * @return string postback event parameter
+ */
+ public function getCallbackEventParameter()
+ {
+ if($this->_callbackEventParameter===null)
+ {
+ $param = $this->getRequest()->itemAt(TPage::FIELD_CALLBACK_PARAMETER);
+ if(strlen($param) > 0)
+ $this->_callbackEventParameter=TJavascript::jsonDecode((string)$param);
+ var_dump($param);
+ }
+ return $this->_callbackEventParameter;
+ }
+
+ /**
+ * @param mixed postback event parameter
+ */
+ public function setCallbackEventParameter($value)
+ {
+ $this->_callbackEventParameter=$value;
+ }
+
+ /**
+ * Gets the callback client script handler that allows javascript functions
+ * to be executed during the callback response.
+ * @return TCallbackClientScript callback client handler.
+ */
+ public function getCallbackClientHandler()
+ {
+ if(is_null($this->_callbackClient))
+ $this->_callbackClient = new TCallbackClientScript;
+ return $this->_callbackClient;
+ }
+
+ /**
+ * @param TCallbackClientScript new callback client handler.
+ */
+ public function setCallbackClientHandler($handler)
+ {
+ $this->_callbackClient = $handler;
+ }
+
+ /**
+ * Gets the callback response handler.
+ * @return TCallbackResponse callback response
+ */
+ public function getCallbackResponseHandler()
+ {
+ if(is_null($this->_callbackResponse))
+ $this->_callbackResponse = new TCallbackResponse;
+ return $this->_callbackResponse;
+ }
+
+ /**
+ * @param TCallbackResponse new callback response handler.
+ */
+ public function setCallbackResponseHandler($handler)
+ {
+ $this->_callbackResponse = $handler;
+ }
+}
+
+class TInvalidCallbackHandlerException extends TException
+{
+
+}
+
+class TInvalidCallbackRequestException extends TException
+{
+}
+
+?>
diff --git a/framework/Web/UI/ActiveControls/TCallbackClientScript.php b/framework/Web/UI/ActiveControls/TCallbackClientScript.php
new file mode 100644
index 00000000..550c88b5
--- /dev/null
+++ b/framework/Web/UI/ActiveControls/TCallbackClientScript.php
@@ -0,0 +1,485 @@
+<?php
+/**
+ * TCallbackClientScript class file
+ *
+ * @author Wei Zhuo <weizhuo[at]gamil[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Revision: $ $Date: $
+ * @package System.Web.UI.ActiveControls
+ */
+
+/**
+ * TCallbackClientScript class.
+ *
+ * The TCallbackClientScript class provides corresponding methods that can be
+ * executed on the client-side (i.e. the browser client that is viewing
+ * the page) during a callback response.
+ *
+ * The avaiable methods includes setting/clicking input elements, changing Css
+ * styles, hiding/showing elements, and adding visual effects to elements on the
+ * page. The client-side methods can be access through the CallbackClient
+ * property available in TPage.
+ *
+ * For example, to hide "$myTextBox" element during callback response, do
+ * <code>
+ * $this->getPage()->getCallbackClient()->hide($myTextBox);
+ * </code>
+ *
+ * @author Wei Zhuo <weizhuo[at]gamil[dot]com>
+ * @version $Revision: $ $Date: $
+ * @package System.Web.UI.ActiveControls
+ * @since 3.0
+ */
+class TCallbackClientScript
+{
+ /**
+ * @var TList list of client functions to execute.
+ */
+ private $_actions;
+
+ /**
+ * Constructor.
+ */
+ public function __construct()
+ {
+ $this->_actions = new TList;
+ }
+
+ /**
+ * @return array list of client function to be executed during callback
+ * response.
+ */
+ public function getClientFunctionsToExecute()
+ {
+ return $this->_actions;
+ }
+
+ /**
+ * Executes a client-side statement.
+ * @param string javascript function name
+ * @param array list of arguments for the function
+ */
+ public function callClientFunction($function, $params=null)
+ {
+ if(!is_array($params) && $params !== null)
+ $params = array($params);
+ else
+ $params = array();
+
+ if(count($params) > 0)
+ {
+ if($params[0] instanceof TControl)
+ $params[0] = $params[0]->getID();
+ }
+ $this->_actions->add(array($function => $params));
+ }
+
+ /**
+ * Client script to set the value of a particular input element.
+ * @param TControl|string control element to set the new value
+ * @param string new value
+ */
+ public function setValue($input, $text)
+ {
+ $this->callClientFunction('Prado.Element.setValue', array($input, $text));
+ }
+
+ /**
+ * Client script to select/clear/check a drop down list, check box list,
+ * or radio button list.
+ * The second parameter determines the selection method. Valid methods are
+ * - <b>Value</b>, select or check by value
+ * - <b>Index</b>, select or check by list index (zero based index)
+ * - <b>All</b>, selects or checks all in the list
+ * - <b>Clear</b>, clears or selections or checks in the list
+ * - <b>Invert</b>, inverts the current selection or checks.
+ * @param TControl|string list control
+ * @param string selection method
+ * @param string|int the value or index to select/check.
+ */
+ public function select($listControl, $method="Value", $valueOrIndex=null)
+ {
+ $this->callClientFunction('Prado.Element.select', array($listControl, $method, $valueOrIndex));
+ }
+
+ /**
+ * Client script to click on an element. <b>This client-side function
+ * is unpredictable.</b>
+ * @param TControl|string control element or element id
+ */
+ public function click($control)
+ {
+ $this->callClientFunction('Prado.Element.click', $control);
+ }
+
+ /**
+ * Client script to check or uncheck a checkbox or radio button.
+ * @param TControl|string control element or element id
+ * @param boolean check or uncheck the checkbox or radio button.
+ */
+ public function check($checkbox, $checked=true)
+ {
+ $this->select($checkbox, "Value", $checked);
+ }
+
+ /**
+ * Sets the attribute of a particular control.
+ * @param TControl|string control element or element id
+ * @param string attribute name
+ * @param string attribute value
+ */
+ public function setAttribute($control, $name, $value)
+ {
+ $this->callClientFunction('Prado.Element.setAttribute',array($control, $name, $value));
+ }
+
+ /**
+ * Sets the options of a select input element.
+ * @param TControl|string control element or element id
+ * @param TCollection a list of new options
+ */
+ public function setOptions($control, $items)
+ {
+ $options = array();
+ foreach($items as $item)
+ $options[] = array($item->getText(),$item->getValue());
+ $this->callClientFunction('Prado.Element.setOptions', array($control, $options));
+ }
+
+ /**
+ * Shows an element by changing its CSS display style as empty.
+ * @param TControl|string control element or element id
+ */
+ public function show($element)
+ {
+ $this->callClientFunction('Element.show', $element);
+ }
+
+ /**
+ * Hides an element by changing its CSS display style to "none".
+ * @param TControl|string control element or element id
+ */
+ public function hide($element)
+ {
+ $this->callClientFunction('Element.hide', $element);
+ }
+
+ /**
+ * Toggles the visibility of the element.
+ * @param TControl|string control element or element id
+ */
+ public function toggle($element)
+ {
+ $this->callClientFunction('Element.toggle', $element);
+ }
+
+ /**
+ * Removes an element from the HTML page.
+ * @param TControl|string control element or element id
+ */
+ public function remove($element)
+ {
+ $this->callClientFunction('Element.remove', $element);
+ }
+
+ /**
+ * Update the element's innerHTML with new content.
+ * @param TControl|string control element or element id
+ * @param TControl|string new HTML content, if content is of a TControl, the
+ * controls render method is called.
+ */
+ public function update($element, $innerHTML)
+ {
+ if($innerHTML instanceof TControl)
+ $innerHTML = $innerHTML->render();
+ $this->callClientFunction('Element.update', array($element, $innerHTML));
+ }
+
+ /**
+ * Replace the innerHTML of a content with fragements of the response body.
+ * @param TControl|string control element or element id
+ */
+ public function replaceContent($element)
+ {
+ $this->callClientFunction('Prado.Element.replaceContent', $element);
+ }
+
+ /**
+ * Add a Css class name to the element.
+ * @param TControl|string control element or element id
+ * @param string CssClass name to add.
+ */
+ public function addCssClass($element, $cssClass)
+ {
+ $this->callClientFunction('Element.addClassName', array($element, $cssClass));
+ }
+
+ /**
+ * Remove a Css class name from the element.
+ * @param TControl|string control element or element id
+ * @param string CssClass name to remove.
+ */
+ public function removeCssClass($element, $cssClass)
+ {
+ $this->callClientFunction('Element.removeClassName', array($element, $cssClass));
+ }
+
+ /**
+ * Sets the CssClass of an element.
+ * @param TControl|string control element or element id
+ * @param string new CssClass name for the element.
+ */
+ public function setCssClass($element, $cssClass)
+ {
+ $this->callClientFunction('Prado.Element.CssClass.set', array($element, $cssClass));
+ }
+
+ /**
+ * Scroll the top of the browser viewing area to the location of the
+ * element.
+ * @param TControl|string control element or element id
+ */
+ public function scrollTo($element)
+ {
+ $this->callClientFunction('Element.scrollTo', $element);
+ }
+
+ /**
+ * Sets the style of element. The style must be a key-value array where the
+ * key is the style property and the value is the style value.
+ * @param TControl|string control element or element id
+ * @param array list of key-value pairs as style property and style value.
+ */
+ public function setStyle($element, $styles)
+ {
+ $this->callClientFunction('Element.setStyle', array($element, $styles));
+ }
+
+ /**
+ * Insert a HTML fragement after the element.
+ * @param TControl|string control element or element id
+ * @param TControl|string HTML fragement, otherwise if TControl, its render
+ * method will be called.
+ */
+ public function insertAfter($element, $innerHTML)
+ {
+ if($innerHTML instanceof TControl)
+ $innerHTML = $innerHTML->render();
+ $this->callClientFunction('Prado.Element.Insert.After', array($element, $innerHTML));
+ }
+
+ /**
+ * Insert a HTML fragement before the element.
+ * @param TControl|string control element or element id
+ * @param TControl|string HTML fragement, otherwise if TControl, its render
+ * method will be called.
+ */
+ public function insertBefore($element, $innerHTML)
+ {
+ if($innerHTML instanceof TControl)
+ $innerHTML = $innerHTML->render();
+ $this->callClientFunction('Prado.Element.Insert.Before', array($element, $innerHTML));
+ }
+
+ /**
+ * Insert a HTML fragement below the element.
+ * @param TControl|string control element or element id
+ * @param TControl|string HTML fragement, otherwise if TControl, its render
+ * method will be called.
+ */
+ public function insertBelow($element, $innerHTML)
+ {
+ if($innerHTML instanceof TControl)
+ $innerHTML = $innerHTML->render();
+ $this->callClientFunction('Prado.Element.Insert.Below', array($element, $innerHTML));
+ }
+
+ /**
+ * Insert a HTML fragement above the element.
+ * @param TControl|string control element or element id
+ * @param TControl|string HTML fragement, otherwise if TControl, its render
+ * method will be called.
+ */
+ public function insertAbove($element, $innerHTML)
+ {
+ if($innerHTML instanceof TControl)
+ $innerHTML = $innerHTML->render();
+ $this->callClientFunction('Prado.Element.Insert.Above', array($element, $innerHTML));
+ }
+
+ /**
+ * Add a visual effect the element.
+ * @param string visual effect function name.
+ * @param TControl|string control element or element id
+ * @param array visual effect key-value pair options.
+ */
+ public function visualEffect($type, $element, $options=null)
+ {
+ $this->callClientFunction($type, is_array($options) ? array($element, $options) : $element);
+ }
+
+ /**
+ * Visual Effect: Gradually make the element appear.
+ * @param TControl|string control element or element id
+ * @param array visual effect key-value pair options.
+ */
+ public function appear($element, $options=null)
+ {
+ $this->visualEffect('Effect.Appear', $element, $options);
+ }
+
+ /**
+ * Visual Effect: Blind down.
+ * @param TControl|string control element or element id
+ * @param array visual effect key-value pair options.
+ */
+ public function blindDown($element, $options=null)
+ {
+ $this->visualEffect('Effect.BlindDown', $element, $options);
+ }
+
+ /**
+ * Visual Effect: Blind up.
+ * @param TControl|string control element or element id
+ * @param array visual effect key-value pair options.
+ */
+ public function blindUp($element, $options=null)
+ {
+ $this->visualEffect('Effect.BlindUp', $element, $options);
+
+ }
+
+ /**
+ * Visual Effect: Drop out.
+ * @param TControl|string control element or element id
+ * @param array visual effect key-value pair options.
+ */
+ public function dropOut($element, $options=null)
+ {
+ $this->visualEffect('Effect.DropOut', $element, $options);
+ }
+
+ /**
+ * Visual Effect: Gradually fade the element.
+ * @param TControl|string control element or element id
+ * @param array visual effect key-value pair options.
+ */
+ public function fade($element, $options=null)
+ {
+ $this->visualEffect('Effect.Fade', $element, $options);
+ }
+
+ /**
+ * Visual Effect: Fold.
+ * @param TControl|string control element or element id
+ * @param array visual effect key-value pair options.
+ */
+ public function fold($element, $options = null)
+ {
+ $this->visualEffect('Effect.Fold', $element, $options);
+ }
+
+ /**
+ * Visual Effect: Gradually make an element grow to a predetermined size.
+ * @param TControl|string control element or element id
+ * @param array visual effect key-value pair options.
+ */
+ public function grow($element, $options=null)
+ {
+ $this->visualEffect('Effect.Grow', $element, $options);
+ }
+
+ /**
+ * Visual Effect: Gradually grow and fade the element.
+ * @param TControl|string control element or element id
+ * @param array visual effect key-value pair options.
+ */
+ public function puff($element, $options=null)
+ {
+ $this->visualEffect('Effect.Puff', $element, $options);
+ }
+
+ /**
+ * Visual Effect: Pulsate.
+ * @param TControl|string control element or element id
+ * @param array visual effect key-value pair options.
+ */
+ public function pulsate($element, $options=null)
+ {
+ $this->visualEffect('Effect.Pulsate', $element, $options);
+ }
+
+ /**
+ * Visual Effect: Shake the element.
+ * @param TControl|string control element or element id
+ * @param array visual effect key-value pair options.
+ */
+ public function shake($element, $options=null)
+ {
+ $this->visualEffect('Effect.Shake', $element, $options);
+ }
+
+ /**
+ * Visual Effect: Shrink the element.
+ * @param TControl|string control element or element id
+ * @param array visual effect key-value pair options.
+ */
+ public function shrink($element, $options=null)
+ {
+ $this->visualEffect('Effect.Shrink', $element, $options);
+ }
+
+ /**
+ * Visual Effect: Slide down.
+ * @param TControl|string control element or element id
+ * @param array visual effect key-value pair options.
+ */
+ public function slideDown($element, $options=null)
+ {
+ $this->visualEffect('Effect.SlideDown', $element, $options);
+ }
+
+ /**
+ * Visual Effect: Side up.
+ * @param TControl|string control element or element id
+ * @param array visual effect key-value pair options.
+ */
+ public function slideUp($element, $options=null)
+ {
+ $this->visualEffect('Effect.SlideUp', $element, $options);
+ }
+
+ /**
+ * Visual Effect: Squish the element.
+ * @param TControl|string control element or element id
+ * @param array visual effect key-value pair options.
+ */
+ public function squish($element, $options=null)
+ {
+ $this->visualEffect('Effect.Squish', $element, $options);
+ }
+
+ /**
+ * Visual Effect: Switch Off effect.
+ * @param TControl|string control element or element id
+ * @param array visual effect key-value pair options.
+ */
+ public function switchOff($element, $options=null)
+ {
+ $this->visualEffect('Effect.SwitchOff', $element, $options);
+ }
+
+ /**
+ * Visual Effect: High light the element for about 2 seconds.
+ * @param TControl|string control element or element id
+ * @param array visual effect key-value pair options.
+ */
+ public function highlight($element, $options=null)
+ {
+ $this->visualEffect('Effect.Highlight', $element, $options);
+ }
+}
+
+?>
diff --git a/framework/Web/UI/ActiveControls/TCallbackResponse.php b/framework/Web/UI/ActiveControls/TCallbackResponse.php
new file mode 100644
index 00000000..bda4e916
--- /dev/null
+++ b/framework/Web/UI/ActiveControls/TCallbackResponse.php
@@ -0,0 +1,20 @@
+<?php
+/*
+ * Created on 29/04/2006
+ */
+
+// See TActivePageAdapter::renderResponse()
+//TODO: How to render the response, it will contain 3 pieces of data
+// 1) The arbituary data returned to the client-side callback handler
+// 2) client-side function call statements
+// 3) Content body, which may need to be partitioned
+
+class TCallbackResponse extends THttpResponse
+{
+ const CALLBACK_DATA_HEADER = 'X-PRADO-DATA';
+ const CALLBACK_ACTION_HEADER = 'X-PRADO-ACTIONS';
+}
+
+
+
+?>
diff --git a/framework/Web/UI/TControl.php b/framework/Web/UI/TControl.php
index 1f1d6c4d..1b5394ba 100644
--- a/framework/Web/UI/TControl.php
+++ b/framework/Web/UI/TControl.php
@@ -867,7 +867,7 @@ class TControl extends TApplicationComponent implements IRenderable, IBindable
* Do not call this method directly. Instead, call {@link ensureChildControls}
* to ensure child controls are created only once.
*/
- protected function createChildControls()
+ public function createChildControls()
{
}
@@ -1215,6 +1215,9 @@ class TControl extends TApplicationComponent implements IRenderable, IBindable
protected function preRenderRecursive()
{
$this->autoDataBindProperties();
+
+ if($this->getEnabled() && $this instanceof IPostBackDataHandler)
+ $this->getPage()->registerPostDataLoader($this);
if($this->getVisible(false))
{
diff --git a/framework/Web/UI/TPage.php b/framework/Web/UI/TPage.php
index e34101d9..31c80320 100644
--- a/framework/Web/UI/TPage.php
+++ b/framework/Web/UI/TPage.php
@@ -36,7 +36,7 @@ class TPage extends TTemplateControl
const FIELD_PAGESTATE='PRADO_PAGESTATE';
const FIELD_CALLBACK_TARGET='PRADO_CALLBACK_TARGET';
const FIELD_CALLBACK_PARAMETER='PRADO_CALLBACK_PARAMETER';
- const FIELD_CALLBACK_ID='PRADO_CALLBACK_ID';
+// const FIELD_CALLBACK_ID='PRADO_CALLBACK_ID';
/**
* @var array system post fields
*/
@@ -46,8 +46,8 @@ class TPage extends TTemplateControl
'PRADO_LASTFOCUS'=>true,
'PRADO_PAGESTATE'=>true,
'PRADO_CALLBACK_TARGET'=>true,
- 'PRADO_CALLBACK_PARAMETER'=>true,
- 'PRADO_CALLBACK_ID'=>true
+ 'PRADO_CALLBACK_PARAMETER'=>true
+ //'PRADO_CALLBACK_ID'=>true
);
/**
* @var TForm form instance
@@ -149,7 +149,11 @@ class TPage extends TTemplateControl
* @var string state string to be stored on the client side
*/
private $_clientState='';
-
+ /**
+ * @var array post data loader IDs.
+ */
+ private $_postDataLoaders=array();
+
/**
* Constructor.
* Sets the page object to itself.
@@ -259,11 +263,159 @@ class TPage extends TTemplateControl
$this->unloadRecursive();
}
+ /**
+ * Sets Adapter to TActivePageAdapter and calls apter to process the
+ * callback request.
+ */
protected function processCallbackRequest($writer)
{
+ $this->setAdapter(new TActivePageAdapter($this));
+
+ Prado::trace("Page onPreInit()",'System.Web.UI.TPage');
+ $this->onPreInit(null);
+
+ Prado::trace("Page initRecursive()",'System.Web.UI.TPage');
+ $this->initRecursive();
+
+ Prado::trace("Page onInitComplete()",'System.Web.UI.TPage');
+ $this->onInitComplete(null);
+
+ $this->_restPostData=new TMap;
+ Prado::trace("Page loadPageState()",'System.Web.UI.TPage');
+ $this->loadPageState();
+ Prado::trace("Page processPostData()",'System.Web.UI.TPage');
+ $this->processPostData($this->_postData,true);
+ Prado::trace("Page onPreLoad()",'System.Web.UI.TPage');
+ $this->onPreLoad(null);
+ Prado::trace("Page loadRecursive()",'System.Web.UI.TPage');
+ $this->loadRecursive();
+ Prado::trace("Page processPostData()",'System.Web.UI.TPage');
+ $this->processPostData($this->_restPostData,false);
+ Prado::trace("Page raiseChangedEvents()",'System.Web.UI.TPage');
+ $this->raiseChangedEvents();
+
+ $this->getAdapter()->processCallbackEvent($writer);
+
+/*
+ Prado::trace("Page raisePostBackEvent()",'System.Web.UI.TPage');
+ $this->raisePostBackEvent();
+*/
+ Prado::trace("Page onLoadComplete()",'System.Web.UI.TPage');
+ $this->onLoadComplete(null);
+
+ Prado::trace("Page preRenderRecursive()",'System.Web.UI.TPage');
+ $this->preRenderRecursive();
+ Prado::trace("Page onPreRenderComplete()",'System.Web.UI.TPage');
+ $this->onPreRenderComplete(null);
+
+/* Prado::trace("Page savePageState()",'System.Web.UI.TPage');
+ $this->savePageState();
+ Prado::trace("Page onSaveStateComplete()",'System.Web.UI.TPage');
+ $this->onSaveStateComplete(null);
+
+ Prado::trace("Page renderControl()",'System.Web.UI.TPage');
+ $this->renderControl($writer);
+*/
+ $this->getAdapter()->renderCallbackResponse($writer);
+
+ Prado::trace("Page unloadRecursive()",'System.Web.UI.TPage');
+ $this->unloadRecursive();
+ }
+
+ /**
+ * Gets the callback response handler that permits changing the callback
+ * response headers and contents.
+ * @return TCallbackResponse callback response handler.
+ */
+ public function getCallbackResponse()
+ {
+ return $this->getAdapter()->getCallbackResponseHandler();
+ }
+
+ /**
+ * Set a new callback respond handler.
+ * @param TCallbackResponse a different callback response handler.
+ */
+ public function setCallbackResponse($responder)
+ {
+ $this->getAdapter()->setCallbackResponseHandler($responder);
+ }
+
+ /**
+ * Gets the callback client script handler that allows javascript functions
+ * to be executed during the callback response.
+ * @return TCallbackClientScript interface to client-side javascript code.
+ */
+ public function getCallbackClient()
+ {
+ return $this->getAdapter()->getCallbackClientHandler();
+ }
+
+ /**
+ * Set a new callback client handler.
+ * @param TCallbackClientScript new callback client script handler.
+ */
+ public function setCallbackClient($client)
+ {
+ $this->getAdapter()->setCallbackClientHandler($client);
+ }
+
+ /**
+ * @return TControl the control responsible for the current callback event,
+ * null if nonexistent
+ */
+ public function getCallbackEventTarget()
+ {
+ return $this->getAdapter()->getCallbackEventTarget();
}
/**
+ * Registers a control to raise callback event in the current request.
+ * @param TControl control registered to raise callback event.
+ */
+ public function setCallbackEventTarget(TControl $control)
+ {
+ $this->getAdapter()->setCallbackEventTarget($control);
+ }
+
+ /**
+ * Callback parameter is decoded assuming JSON encoding.
+ * @return string callback event parameter
+ */
+ public function getCallbackEventParameter()
+ {
+ return $this->getAdapter()->getCallbackEventParameter();
+ }
+
+ /**
+ * @param mixed callback event parameter
+ */
+ public function setCallbackEventParameter($value)
+ {
+ $this->getAdapter()->setCallbackEventParameter($value);
+ }
+
+ /**
+ * Register post data loaders for Callback to collect post data.
+ * This method should only be called by framework developers.
+ * @param TControl control that requires post data.
+ * @see TControl::preRenderRecursive();
+ */
+ public function registerPostDataLoader($control)
+ {
+ $this->_postDataLoaders[] = $control->getUniqueID();
+ }
+
+ /**
+ * Get a list of IDs of controls that are enabled and require post data.
+ * @return array list of IDs implementing IPostBackDataHandler
+ */
+ public function getPostDataLoaders()
+ {
+ return $this->_postDataLoaders;
+ }
+
+ /**
* @return TForm the form on the page
*/
public function getForm()
@@ -541,12 +693,11 @@ class TPage extends TTemplateControl
}
/**
- * TBD
* @return boolean whether this is a callback request
*/
public function getIsCallback()
{
- return false;
+ return $this->getIsPostBack() && $this->getRequest()->contains(self::FIELD_CALLBACK_TARGET);
}
/**
diff --git a/framework/Web/UI/TTemplateControl.php b/framework/Web/UI/TTemplateControl.php
index 971f0288..f18678f1 100644
--- a/framework/Web/UI/TTemplateControl.php
+++ b/framework/Web/UI/TTemplateControl.php
@@ -123,7 +123,7 @@ class TTemplateControl extends TControl implements INamingContainer
* This method is overriden to load and instantiate control template.
* This method should only be used by framework and control developers.
*/
- protected function createChildControls()
+ public function createChildControls()
{
if($tpl=$this->getTemplate(true))
{
diff --git a/framework/Web/UI/WebControls/TWizard.php b/framework/Web/UI/WebControls/TWizard.php
index 9a2c4ce6..20e27f2b 100644
--- a/framework/Web/UI/WebControls/TWizard.php
+++ b/framework/Web/UI/WebControls/TWizard.php
@@ -1005,7 +1005,7 @@ class TWizard extends TWebControl implements INamingContainer
/**
* Creates child controls within the wizard
*/
- protected function createChildControls()
+ public function createChildControls()
{
$this->reset();
$this->createSideBar();
@@ -1650,7 +1650,7 @@ class TTemplatedWizardStep extends TWizardStep implements INamingContainer
* Creates child controls.
* This method mainly instantiates the content template, if any.
*/
- protected function createChildControls()
+ public function createChildControls()
{
$this->getControls()->clear();
if($this->_contentTemplate)
diff --git a/framework/interfaces.php b/framework/interfaces.php
index 2a407696..eb9084b3 100644
--- a/framework/interfaces.php
+++ b/framework/interfaces.php
@@ -280,4 +280,41 @@ interface IBindable
public function dataBind();
}
+/**
+ * IActiveControl interface.
+ *
+ * Active controls must implement IActiveControl interface.
+ *
+ * @author Wei Zhuo <weizhuo[at]gamil[dot]com>
+ * @version $Revision: $ $Date: $
+ * @package System
+ * @since 3.0
+ */
+interface IActiveControl
+{
+
+}
+
+/**
+ * ICallbackEventHandler interface.
+ *
+ * If a control wants to respond to callback event, it must implement this
+ * interface.
+ *
+ * @author Wei Zhuo <weizhuo[at]gamil[dot]com>
+ * @version $Revision: $ $Date: $
+ * @package System
+ * @since 3.0
+ */
+interface ICallbackEventHandler
+{
+ /**
+ * Raises callback event. The implementation of this function should raise
+ * appropriate event(s) (e.g. OnClick, OnCommand) indicating the component
+ * is responsible for the callback event.
+ * @param TCallbackEventParameter the parameter associated with the callback event
+ */
+ public function raiseCallbackEvent($eventArgument);
+}
+
?> \ No newline at end of file
diff --git a/tests/FunctionalTests/features/protected/pages/ActiveControls/ActiveControl.page b/tests/FunctionalTests/features/protected/pages/ActiveControls/ActiveControl.page
new file mode 100644
index 00000000..63bc7f24
--- /dev/null
+++ b/tests/FunctionalTests/features/protected/pages/ActiveControls/ActiveControl.page
@@ -0,0 +1,17 @@
+<com:TContent ID="Content">
+ <com:TClientScript UsingPradoScripts="ajax" />
+ <com:TActiveControl id="control1" />
+ <com:TTextBox />
+ <com:TCheckBoxList>
+ <com:TListItem Text="One" />
+ <com:TListItem Text="Two" />
+ </com:TCheckBoxList>
+ <com:TButton id="button1" Text="Submit" />
+ <script>
+ Event.observe("<%= $this->button1->ClientID %>", "click", function(event)
+ {
+ new Prado.Callback("<%= $this->control1->UniqueID %>", 1);
+ Event.stop(event);
+ });
+ </script>
+</com:TContent> \ No newline at end of file
diff --git a/tests/FunctionalTests/features/protected/pages/ActiveControls/config.xml b/tests/FunctionalTests/features/protected/pages/ActiveControls/config.xml
new file mode 100644
index 00000000..1bbc0acf
--- /dev/null
+++ b/tests/FunctionalTests/features/protected/pages/ActiveControls/config.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<configuration>
+ <paths>
+ <using namespace="System.Web.UI.ActiveControls.*" />
+ </paths>
+</configuration>