diff options
Diffstat (limited to 'framework')
123 files changed, 8050 insertions, 4276 deletions
diff --git a/framework/3rdParty/FirePHPCore/FirePHP.class.php b/framework/3rdParty/FirePHPCore/FirePHP.class.php new file mode 100644 index 00000000..a6abec0d --- /dev/null +++ b/framework/3rdParty/FirePHPCore/FirePHP.class.php @@ -0,0 +1,1528 @@ +<?php +/** + * *** BEGIN LICENSE BLOCK ***** + * + * This file is part of FirePHP (http://www.firephp.org/). + * + * Software License Agreement (New BSD License) + * + * Copyright (c) 2006-2009, Christoph Dorn + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of Christoph Dorn nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ***** END LICENSE BLOCK ***** + * + * @copyright Copyright (C) 2007-2009 Christoph Dorn + * @author Christoph Dorn <christoph@christophdorn.com> + * @license http://www.opensource.org/licenses/bsd-license.php + * @package FirePHP + */ + + +/** + * Sends the given data to the FirePHP Firefox Extension. + * The data can be displayed in the Firebug Console or in the + * "Server" request tab. + * + * For more information see: http://www.firephp.org/ + * + * @copyright Copyright (C) 2007-2009 Christoph Dorn + * @author Christoph Dorn <christoph@christophdorn.com> + * @license http://www.opensource.org/licenses/bsd-license.php + * @package FirePHP + */ +class FirePHP { + + /** + * FirePHP version + * + * @var string + */ + const VERSION = '0.3'; + + /** + * Firebug LOG level + * + * Logs a message to firebug console. + * + * @var string + */ + const LOG = 'LOG'; + + /** + * Firebug INFO level + * + * Logs a message to firebug console and displays an info icon before the message. + * + * @var string + */ + const INFO = 'INFO'; + + /** + * Firebug WARN level + * + * Logs a message to firebug console, displays an warning icon before the message and colors the line turquoise. + * + * @var string + */ + const WARN = 'WARN'; + + /** + * Firebug ERROR level + * + * Logs a message to firebug console, displays an error icon before the message and colors the line yellow. Also increments the firebug error count. + * + * @var string + */ + const ERROR = 'ERROR'; + + /** + * Dumps a variable to firebug's server panel + * + * @var string + */ + const DUMP = 'DUMP'; + + /** + * Displays a stack trace in firebug console + * + * @var string + */ + const TRACE = 'TRACE'; + + /** + * Displays an exception in firebug console + * + * Increments the firebug error count. + * + * @var string + */ + const EXCEPTION = 'EXCEPTION'; + + /** + * Displays an table in firebug console + * + * @var string + */ + const TABLE = 'TABLE'; + + /** + * Starts a group in firebug console + * + * @var string + */ + const GROUP_START = 'GROUP_START'; + + /** + * Ends a group in firebug console + * + * @var string + */ + const GROUP_END = 'GROUP_END'; + + /** + * Singleton instance of FirePHP + * + * @var FirePHP + */ + protected static $instance = null; + + /** + * Flag whether we are logging from within the exception handler + * + * @var boolean + */ + protected $inExceptionHandler = false; + + /** + * Flag whether to throw PHP errors that have been converted to ErrorExceptions + * + * @var boolean + */ + protected $throwErrorExceptions = true; + + /** + * Flag whether to convert PHP assertion errors to Exceptions + * + * @var boolean + */ + protected $convertAssertionErrorsToExceptions = true; + + /** + * Flag whether to throw PHP assertion errors that have been converted to Exceptions + * + * @var boolean + */ + protected $throwAssertionExceptions = false; + + /** + * Wildfire protocol message index + * + * @var int + */ + protected $messageIndex = 1; + + /** + * Options for the library + * + * @var array + */ + protected $options = array('maxObjectDepth' => 10, + 'maxArrayDepth' => 20, + 'useNativeJsonEncode' => true, + 'includeLineNumbers' => true); + + /** + * Filters used to exclude object members when encoding + * + * @var array + */ + protected $objectFilters = array(); + + /** + * A stack of objects used to detect recursion during object encoding + * + * @var object + */ + protected $objectStack = array(); + + /** + * Flag to enable/disable logging + * + * @var boolean + */ + protected $enabled = true; + + /** + * The object constructor + */ + function __construct() { + } + + /** + * When the object gets serialized only include specific object members. + * + * @return array + */ + public function __sleep() { + return array('options','objectFilters','enabled'); + } + + /** + * Gets singleton instance of FirePHP + * + * @param boolean $AutoCreate + * @return FirePHP + */ + public static function getInstance($AutoCreate=false) { + if($AutoCreate===true && !self::$instance) { + self::init(); + } + return self::$instance; + } + + /** + * Creates FirePHP object and stores it for singleton access + * + * @return FirePHP + */ + public static function init() { + return self::$instance = new self(); + } + + /** + * Enable and disable logging to Firebug + * + * @param boolean $Enabled TRUE to enable, FALSE to disable + * @return void + */ + public function setEnabled($Enabled) { + $this->enabled = $Enabled; + } + + /** + * Check if logging is enabled + * + * @return boolean TRUE if enabled + */ + public function getEnabled() { + return $this->enabled; + } + + /** + * Specify a filter to be used when encoding an object + * + * Filters are used to exclude object members. + * + * @param string $Class The class name of the object + * @param array $Filter An array of members to exclude + * @return void + */ + public function setObjectFilter($Class, $Filter) { + $this->objectFilters[$Class] = $Filter; + } + + /** + * Set some options for the library + * + * Options: + * - maxObjectDepth: The maximum depth to traverse objects (default: 10) + * - maxArrayDepth: The maximum depth to traverse arrays (default: 20) + * - useNativeJsonEncode: If true will use json_encode() (default: true) + * - includeLineNumbers: If true will include line numbers and filenames (default: true) + * + * @param array $Options The options to be set + * @return void + */ + public function setOptions($Options) { + $this->options = array_merge($this->options,$Options); + } + + /** + * Get options from the library + * + * @return array The currently set options + */ + public function getOptions() { + return $this->options; + } + + /** + * Register FirePHP as your error handler + * + * Will throw exceptions for each php error. + * + * @return mixed Returns a string containing the previously defined error handler (if any) + */ + public function registerErrorHandler($throwErrorExceptions=true) + { + //NOTE: The following errors will not be caught by this error handler: + // E_ERROR, E_PARSE, E_CORE_ERROR, + // E_CORE_WARNING, E_COMPILE_ERROR, + // E_COMPILE_WARNING, E_STRICT + + $this->throwErrorExceptions = $throwErrorExceptions; + + return set_error_handler(array($this,'errorHandler')); + } + + /** + * FirePHP's error handler + * + * Throws exception for each php error that will occur. + * + * @param int $errno + * @param string $errstr + * @param string $errfile + * @param int $errline + * @param array $errcontext + */ + public function errorHandler($errno, $errstr, $errfile, $errline, $errcontext) + { + // Don't throw exception if error reporting is switched off + if (error_reporting() == 0) { + return; + } + // Only throw exceptions for errors we are asking for + if (error_reporting() & $errno) { + + $exception = new ErrorException($errstr, 0, $errno, $errfile, $errline); + if($this->throwErrorExceptions) { + throw $exception; + } else { + $this->fb($exception); + } + } + } + + /** + * Register FirePHP as your exception handler + * + * @return mixed Returns the name of the previously defined exception handler, + * or NULL on error. + * If no previous handler was defined, NULL is also returned. + */ + public function registerExceptionHandler() + { + return set_exception_handler(array($this,'exceptionHandler')); + } + + /** + * FirePHP's exception handler + * + * Logs all exceptions to your firebug console and then stops the script. + * + * @param Exception $Exception + * @throws Exception + */ + function exceptionHandler($Exception) { + + $this->inExceptionHandler = true; + + header('HTTP/1.1 500 Internal Server Error'); + + $this->fb($Exception); + + $this->inExceptionHandler = false; + } + + /** + * Register FirePHP driver as your assert callback + * + * @param boolean $convertAssertionErrorsToExceptions + * @param boolean $throwAssertionExceptions + * @return mixed Returns the original setting or FALSE on errors + */ + public function registerAssertionHandler($convertAssertionErrorsToExceptions=true, $throwAssertionExceptions=false) + { + $this->convertAssertionErrorsToExceptions = $convertAssertionErrorsToExceptions; + $this->throwAssertionExceptions = $throwAssertionExceptions; + + if($throwAssertionExceptions && !$convertAssertionErrorsToExceptions) { + throw $this->newException('Cannot throw assertion exceptions as assertion errors are not being converted to exceptions!'); + } + + return assert_options(ASSERT_CALLBACK, array($this, 'assertionHandler')); + } + + /** + * FirePHP's assertion handler + * + * Logs all assertions to your firebug console and then stops the script. + * + * @param string $file File source of assertion + * @param int $line Line source of assertion + * @param mixed $code Assertion code + */ + public function assertionHandler($file, $line, $code) + { + + if($this->convertAssertionErrorsToExceptions) { + + $exception = new ErrorException('Assertion Failed - Code[ '.$code.' ]', 0, null, $file, $line); + + if($this->throwAssertionExceptions) { + throw $exception; + } else { + $this->fb($exception); + } + + } else { + + $this->fb($code, 'Assertion Failed', FirePHP::ERROR, array('File'=>$file,'Line'=>$line)); + + } + } + + /** + * Set custom processor url for FirePHP + * + * @param string $URL + */ + public function setProcessorUrl($URL) + { + $this->setHeader('X-FirePHP-ProcessorURL', $URL); + } + + /** + * Set custom renderer url for FirePHP + * + * @param string $URL + */ + public function setRendererUrl($URL) + { + $this->setHeader('X-FirePHP-RendererURL', $URL); + } + + /** + * Start a group for following messages. + * + * Options: + * Collapsed: [true|false] + * Color: [#RRGGBB|ColorName] + * + * @param string $Name + * @param array $Options OPTIONAL Instructions on how to log the group + * @return true + * @throws Exception + */ + public function group($Name, $Options=null) { + + if(!$Name) { + throw $this->newException('You must specify a label for the group!'); + } + + if($Options) { + if(!is_array($Options)) { + throw $this->newException('Options must be defined as an array!'); + } + if(array_key_exists('Collapsed', $Options)) { + $Options['Collapsed'] = ($Options['Collapsed'])?'true':'false'; + } + } + + return $this->fb(null, $Name, FirePHP::GROUP_START, $Options); + } + + /** + * Ends a group you have started before + * + * @return true + * @throws Exception + */ + public function groupEnd() { + return $this->fb(null, null, FirePHP::GROUP_END); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::LOG + * @param mixes $Object + * @param string $Label + * @return true + * @throws Exception + */ + public function log($Object, $Label=null) { + return $this->fb($Object, $Label, FirePHP::LOG); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::INFO + * @param mixes $Object + * @param string $Label + * @return true + * @throws Exception + */ + public function info($Object, $Label=null) { + return $this->fb($Object, $Label, FirePHP::INFO); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::WARN + * @param mixes $Object + * @param string $Label + * @return true + * @throws Exception + */ + public function warn($Object, $Label=null) { + return $this->fb($Object, $Label, FirePHP::WARN); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::ERROR + * @param mixes $Object + * @param string $Label + * @return true + * @throws Exception + */ + public function error($Object, $Label=null) { + return $this->fb($Object, $Label, FirePHP::ERROR); + } + + /** + * Dumps key and variable to firebug server panel + * + * @see FirePHP::DUMP + * @param string $Key + * @param mixed $Variable + * @return true + * @throws Exception + */ + public function dump($Key, $Variable) { + return $this->fb($Variable, $Key, FirePHP::DUMP); + } + + /** + * Log a trace in the firebug console + * + * @see FirePHP::TRACE + * @param string $Label + * @return true + * @throws Exception + */ + public function trace($Label) { + return $this->fb($Label, FirePHP::TRACE); + } + + /** + * Log a table in the firebug console + * + * @see FirePHP::TABLE + * @param string $Label + * @param string $Table + * @return true + * @throws Exception + */ + public function table($Label, $Table) { + return $this->fb($Table, $Label, FirePHP::TABLE); + } + + /** + * Check if FirePHP is installed on client + * + * @return boolean + */ + public function detectClientExtension() { + /* Check if FirePHP is installed on client */ + if(!@preg_match_all('/\sFirePHP\/([\.|\d]*)\s?/si',$this->getUserAgent(),$m) || + !version_compare($m[1][0],'0.0.6','>=')) { + return false; + } + return true; + } + + /** + * Log varible to Firebug + * + * @see http://www.firephp.org/Wiki/Reference/Fb + * @param mixed $Object The variable to be logged + * @return true Return TRUE if message was added to headers, FALSE otherwise + * @throws Exception + */ + public function fb($Object) { + + if(!$this->enabled) { + return false; + } + + if (headers_sent($filename, $linenum)) { + // If we are logging from within the exception handler we cannot throw another exception + if($this->inExceptionHandler) { + // Simply echo the error out to the page + echo '<div style="border: 2px solid red; font-family: Arial; font-size: 12px; background-color: lightgray; padding: 5px;"><span style="color: red; font-weight: bold;">FirePHP ERROR:</span> Headers already sent in <b>'.$filename.'</b> on line <b>'.$linenum.'</b>. Cannot send log data to FirePHP. You must have Output Buffering enabled via ob_start() or output_buffering ini directive.</div>'; + } else { + throw $this->newException('Headers already sent in '.$filename.' on line '.$linenum.'. Cannot send log data to FirePHP. You must have Output Buffering enabled via ob_start() or output_buffering ini directive.'); + } + } + + $Type = null; + $Label = null; + $Options = array(); + + if(func_num_args()==1) { + } else + if(func_num_args()==2) { + switch(func_get_arg(1)) { + case self::LOG: + case self::INFO: + case self::WARN: + case self::ERROR: + case self::DUMP: + case self::TRACE: + case self::EXCEPTION: + case self::TABLE: + case self::GROUP_START: + case self::GROUP_END: + $Type = func_get_arg(1); + break; + default: + $Label = func_get_arg(1); + break; + } + } else + if(func_num_args()==3) { + $Type = func_get_arg(2); + $Label = func_get_arg(1); + } else + if(func_num_args()==4) { + $Type = func_get_arg(2); + $Label = func_get_arg(1); + $Options = func_get_arg(3); + } else { + throw $this->newException('Wrong number of arguments to fb() function!'); + } + + + if(!$this->detectClientExtension()) { + return false; + } + + $meta = array(); + $skipFinalObjectEncode = false; + + if($Object instanceof Exception) { + + $meta['file'] = $this->_escapeTraceFile($Object->getFile()); + $meta['line'] = $Object->getLine(); + + $trace = $Object->getTrace(); + if($Object instanceof ErrorException + && isset($trace[0]['function']) + && $trace[0]['function']=='errorHandler' + && isset($trace[0]['class']) + && $trace[0]['class']=='FirePHP') { + + $severity = false; + switch($Object->getSeverity()) { + case E_WARNING: $severity = 'E_WARNING'; break; + case E_NOTICE: $severity = 'E_NOTICE'; break; + case E_USER_ERROR: $severity = 'E_USER_ERROR'; break; + case E_USER_WARNING: $severity = 'E_USER_WARNING'; break; + case E_USER_NOTICE: $severity = 'E_USER_NOTICE'; break; + case E_STRICT: $severity = 'E_STRICT'; break; + case E_RECOVERABLE_ERROR: $severity = 'E_RECOVERABLE_ERROR'; break; + case E_DEPRECATED: $severity = 'E_DEPRECATED'; break; + case E_USER_DEPRECATED: $severity = 'E_USER_DEPRECATED'; break; + } + + $Object = array('Class'=>get_class($Object), + 'Message'=>$severity.': '.$Object->getMessage(), + 'File'=>$this->_escapeTraceFile($Object->getFile()), + 'Line'=>$Object->getLine(), + 'Type'=>'trigger', + 'Trace'=>$this->_escapeTrace(array_splice($trace,2))); + $skipFinalObjectEncode = true; + } else { + $Object = array('Class'=>get_class($Object), + 'Message'=>$Object->getMessage(), + 'File'=>$this->_escapeTraceFile($Object->getFile()), + 'Line'=>$Object->getLine(), + 'Type'=>'throw', + 'Trace'=>$this->_escapeTrace($trace)); + $skipFinalObjectEncode = true; + } + $Type = self::EXCEPTION; + + } else + if($Type==self::TRACE) { + + $trace = debug_backtrace(); + if(!$trace) return false; + for( $i=0 ; $i<sizeof($trace) ; $i++ ) { + + if(isset($trace[$i]['class']) + && isset($trace[$i]['file']) + && ($trace[$i]['class']=='FirePHP' + || $trace[$i]['class']=='FB') + && (substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php' + || substr($this->_standardizePath($trace[$i]['file']),-29,29)=='FirePHPCore/FirePHP.class.php')) { + /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */ + } else + if(isset($trace[$i]['class']) + && isset($trace[$i+1]['file']) + && $trace[$i]['class']=='FirePHP' + && substr($this->_standardizePath($trace[$i+1]['file']),-18,18)=='FirePHPCore/fb.php') { + /* Skip fb() */ + } else + if($trace[$i]['function']=='fb' + || $trace[$i]['function']=='trace' + || $trace[$i]['function']=='send') { + $Object = array('Class'=>isset($trace[$i]['class'])?$trace[$i]['class']:'', + 'Type'=>isset($trace[$i]['type'])?$trace[$i]['type']:'', + 'Function'=>isset($trace[$i]['function'])?$trace[$i]['function']:'', + 'Message'=>$trace[$i]['args'][0], + 'File'=>isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'', + 'Line'=>isset($trace[$i]['line'])?$trace[$i]['line']:'', + 'Args'=>isset($trace[$i]['args'])?$this->encodeObject($trace[$i]['args']):'', + 'Trace'=>$this->_escapeTrace(array_splice($trace,$i+1))); + + $skipFinalObjectEncode = true; + $meta['file'] = isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):''; + $meta['line'] = isset($trace[$i]['line'])?$trace[$i]['line']:''; + break; + } + } + + } else + if($Type==self::TABLE) { + + if(isset($Object[0]) && is_string($Object[0])) { + $Object[1] = $this->encodeTable($Object[1]); + } else { + $Object = $this->encodeTable($Object); + } + + $skipFinalObjectEncode = true; + + } else + if($Type==self::GROUP_START) { + + if(!$Label) { + throw $this->newException('You must specify a label for the group!'); + } + + } else { + if($Type===null) { + $Type = self::LOG; + } + } + + if($this->options['includeLineNumbers']) { + if(!isset($meta['file']) || !isset($meta['line'])) { + + $trace = debug_backtrace(); + for( $i=0 ; $trace && $i<sizeof($trace) ; $i++ ) { + + if(isset($trace[$i]['class']) + && isset($trace[$i]['file']) + && ($trace[$i]['class']=='FirePHP' + || $trace[$i]['class']=='FB') + && (substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php' + || substr($this->_standardizePath($trace[$i]['file']),-29,29)=='FirePHPCore/FirePHP.class.php')) { + /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */ + } else + if(isset($trace[$i]['class']) + && isset($trace[$i+1]['file']) + && $trace[$i]['class']=='FirePHP' + && substr($this->_standardizePath($trace[$i+1]['file']),-18,18)=='FirePHPCore/fb.php') { + /* Skip fb() */ + } else + if(isset($trace[$i]['file']) + && substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php') { + /* Skip FB::fb() */ + } else { + $meta['file'] = isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):''; + $meta['line'] = isset($trace[$i]['line'])?$trace[$i]['line']:''; + break; + } + } + + } + } else { + unset($meta['file']); + unset($meta['line']); + } + + $this->setHeader('X-Wf-Protocol-1','http://meta.wildfirehq.org/Protocol/JsonStream/0.2'); + $this->setHeader('X-Wf-1-Plugin-1','http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/'.self::VERSION); + + $structure_index = 1; + if($Type==self::DUMP) { + $structure_index = 2; + $this->setHeader('X-Wf-1-Structure-2','http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1'); + } else { + $this->setHeader('X-Wf-1-Structure-1','http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1'); + } + + if($Type==self::DUMP) { + $msg = '{"'.$Label.'":'.$this->jsonEncode($Object, $skipFinalObjectEncode).'}'; + } else { + $msg_meta = $Options; + $msg_meta['Type'] = $Type; + if($Label!==null) { + $msg_meta['Label'] = $Label; + } + if(isset($meta['file']) && !isset($msg_meta['File'])) { + $msg_meta['File'] = $meta['file']; + } + if(isset($meta['line']) && !isset($msg_meta['Line'])) { + $msg_meta['Line'] = $meta['line']; + } + $msg = '['.$this->jsonEncode($msg_meta).','.$this->jsonEncode($Object, $skipFinalObjectEncode).']'; + } + + $parts = explode("\n",chunk_split($msg, 5000, "\n")); + + for( $i=0 ; $i<count($parts) ; $i++) { + + $part = $parts[$i]; + if ($part) { + + if(count($parts)>2) { + // Message needs to be split into multiple parts + $this->setHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex, + (($i==0)?strlen($msg):'') + . '|' . $part . '|' + . (($i<count($parts)-2)?'\\':'')); + } else { + $this->setHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex, + strlen($part) . '|' . $part . '|'); + } + + $this->messageIndex++; + + if ($this->messageIndex > 99999) { + throw $this->newException('Maximum number (99,999) of messages reached!'); + } + } + } + + $this->setHeader('X-Wf-1-Index',$this->messageIndex-1); + + return true; + } + + /** + * Standardizes path for windows systems. + * + * @param string $Path + * @return string + */ + protected function _standardizePath($Path) { + return preg_replace('/\\\\+/','/',$Path); + } + + /** + * Escape trace path for windows systems + * + * @param array $Trace + * @return array + */ + protected function _escapeTrace($Trace) { + if(!$Trace) return $Trace; + for( $i=0 ; $i<sizeof($Trace) ; $i++ ) { + if(isset($Trace[$i]['file'])) { + $Trace[$i]['file'] = $this->_escapeTraceFile($Trace[$i]['file']); + } + if(isset($Trace[$i]['args'])) { + $Trace[$i]['args'] = $this->encodeObject($Trace[$i]['args']); + } + } + return $Trace; + } + + /** + * Escape file information of trace for windows systems + * + * @param string $File + * @return string + */ + protected function _escapeTraceFile($File) { + /* Check if we have a windows filepath */ + if(strpos($File,'\\')) { + /* First strip down to single \ */ + + $file = preg_replace('/\\\\+/','\\',$File); + + return $file; + } + return $File; + } + + /** + * Send header + * + * @param string $Name + * @param string_type $Value + */ + protected function setHeader($Name, $Value) { + return header($Name.': '.$Value); + } + + /** + * Get user agent + * + * @return string|false + */ + protected function getUserAgent() { + if(!isset($_SERVER['HTTP_USER_AGENT'])) return false; + return $_SERVER['HTTP_USER_AGENT']; + } + + /** + * Returns a new exception + * + * @param string $Message + * @return Exception + */ + protected function newException($Message) { + return new Exception($Message); + } + + /** + * Encode an object into a JSON string + * + * Uses PHP's jeson_encode() if available + * + * @param object $Object The object to be encoded + * @return string The JSON string + */ + public function jsonEncode($Object, $skipObjectEncode=false) + { + if(!$skipObjectEncode) { + $Object = $this->encodeObject($Object); + } + + if(function_exists('json_encode') + && $this->options['useNativeJsonEncode']!=false) { + + return json_encode($Object); + } else { + return $this->json_encode($Object); + } + } + + /** + * Encodes a table by encoding each row and column with encodeObject() + * + * @param array $Table The table to be encoded + * @return array + */ + protected function encodeTable($Table) { + + if(!$Table) return $Table; + + $new_table = array(); + foreach($Table as $row) { + + if(is_array($row)) { + $new_row = array(); + + foreach($row as $item) { + $new_row[] = $this->encodeObject($item); + } + + $new_table[] = $new_row; + } + } + + return $new_table; + } + + /** + * Encodes an object including members with + * protected and private visibility + * + * @param Object $Object The object to be encoded + * @param int $Depth The current traversal depth + * @return array All members of the object + */ + protected function encodeObject($Object, $ObjectDepth = 1, $ArrayDepth = 1) + { + $return = array(); + + if (is_resource($Object)) { + + return '** '.(string)$Object.' **'; + + } else + if (is_object($Object)) { + + if ($ObjectDepth > $this->options['maxObjectDepth']) { + return '** Max Object Depth ('.$this->options['maxObjectDepth'].') **'; + } + + foreach ($this->objectStack as $refVal) { + if ($refVal === $Object) { + return '** Recursion ('.get_class($Object).') **'; + } + } + array_push($this->objectStack, $Object); + + $return['__className'] = $class = get_class($Object); + + $reflectionClass = new ReflectionClass($class); + $properties = array(); + foreach( $reflectionClass->getProperties() as $property) { + $properties[$property->getName()] = $property; + } + + $members = (array)$Object; + + foreach( $properties as $raw_name => $property ) { + + $name = $raw_name; + if($property->isStatic()) { + $name = 'static:'.$name; + } + if($property->isPublic()) { + $name = 'public:'.$name; + } else + if($property->isPrivate()) { + $name = 'private:'.$name; + $raw_name = "\0".$class."\0".$raw_name; + } else + if($property->isProtected()) { + $name = 'protected:'.$name; + $raw_name = "\0".'*'."\0".$raw_name; + } + + if(!(isset($this->objectFilters[$class]) + && is_array($this->objectFilters[$class]) + && in_array($raw_name,$this->objectFilters[$class]))) { + + if(array_key_exists($raw_name,$members) + && !$property->isStatic()) { + + $return[$name] = $this->encodeObject($members[$raw_name], $ObjectDepth + 1, 1); + + } else { + if(method_exists($property,'setAccessible')) { + $property->setAccessible(true); + $return[$name] = $this->encodeObject($property->getValue($Object), $ObjectDepth + 1, 1); + } else + if($property->isPublic()) { + $return[$name] = $this->encodeObject($property->getValue($Object), $ObjectDepth + 1, 1); + } else { + $return[$name] = '** Need PHP 5.3 to get value **'; + } + } + } else { + $return[$name] = '** Excluded by Filter **'; + } + } + + // Include all members that are not defined in the class + // but exist in the object + foreach( $members as $raw_name => $value ) { + + $name = $raw_name; + + if ($name{0} == "\0") { + $parts = explode("\0", $name); + $name = $parts[2]; + } + + if(!isset($properties[$name])) { + $name = 'undeclared:'.$name; + + if(!(isset($this->objectFilters[$class]) + && is_array($this->objectFilters[$class]) + && in_array($raw_name,$this->objectFilters[$class]))) { + + $return[$name] = $this->encodeObject($value, $ObjectDepth + 1, 1); + } else { + $return[$name] = '** Excluded by Filter **'; + } + } + } + + array_pop($this->objectStack); + + } elseif (is_array($Object)) { + + if ($ArrayDepth > $this->options['maxArrayDepth']) { + return '** Max Array Depth ('.$this->options['maxArrayDepth'].') **'; + } + + foreach ($Object as $key => $val) { + + // Encoding the $GLOBALS PHP array causes an infinite loop + // if the recursion is not reset here as it contains + // a reference to itself. This is the only way I have come up + // with to stop infinite recursion in this case. + if($key=='GLOBALS' + && is_array($val) + && array_key_exists('GLOBALS',$val)) { + $val['GLOBALS'] = '** Recursion (GLOBALS) **'; + } + + $return[$key] = $this->encodeObject($val, 1, $ArrayDepth + 1); + } + } else { + if(self::is_utf8($Object)) { + return $Object; + } else { + return utf8_encode($Object); + } + } + return $return; + } + + /** + * Returns true if $string is valid UTF-8 and false otherwise. + * + * @param mixed $str String to be tested + * @return boolean + */ + protected static function is_utf8($str) { + $c=0; $b=0; + $bits=0; + $len=strlen($str); + for($i=0; $i<$len; $i++){ + $c=ord($str[$i]); + if($c > 128){ + if(($c >= 254)) return false; + elseif($c >= 252) $bits=6; + elseif($c >= 248) $bits=5; + elseif($c >= 240) $bits=4; + elseif($c >= 224) $bits=3; + elseif($c >= 192) $bits=2; + else return false; + if(($i+$bits) > $len) return false; + while($bits > 1){ + $i++; + $b=ord($str[$i]); + if($b < 128 || $b > 191) return false; + $bits--; + } + } + } + return true; + } + + /** + * Converts to and from JSON format. + * + * JSON (JavaScript Object Notation) is a lightweight data-interchange + * format. It is easy for humans to read and write. It is easy for machines + * to parse and generate. It is based on a subset of the JavaScript + * Programming Language, Standard ECMA-262 3rd Edition - December 1999. + * This feature can also be found in Python. JSON is a text format that is + * completely language independent but uses conventions that are familiar + * to programmers of the C-family of languages, including C, C++, C#, Java, + * JavaScript, Perl, TCL, and many others. These properties make JSON an + * ideal data-interchange language. + * + * This package provides a simple encoder and decoder for JSON notation. It + * is intended for use with client-side Javascript applications that make + * use of HTTPRequest to perform server communication functions - data can + * be encoded into JSON notation for use in a client-side javascript, or + * decoded from incoming Javascript requests. JSON format is native to + * Javascript, and can be directly eval()'ed with no further parsing + * overhead + * + * All strings should be in ASCII or UTF-8 format! + * + * LICENSE: Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: Redistributions of source code must retain the + * above copyright notice, this list of conditions and the following + * disclaimer. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * @category + * @package Services_JSON + * @author Michal Migurski <mike-json@teczno.com> + * @author Matt Knapp <mdknapp[at]gmail[dot]com> + * @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com> + * @author Christoph Dorn <christoph@christophdorn.com> + * @copyright 2005 Michal Migurski + * @version CVS: $Id$ + * @license http://www.opensource.org/licenses/bsd-license.php + * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198 + */ + + + /** + * Keep a list of objects as we descend into the array so we can detect recursion. + */ + private $json_objectStack = array(); + + + /** + * convert a string from one UTF-8 char to one UTF-16 char + * + * Normally should be handled by mb_convert_encoding, but + * provides a slower PHP-only method for installations + * that lack the multibye string extension. + * + * @param string $utf8 UTF-8 character + * @return string UTF-16 character + * @access private + */ + private function json_utf82utf16($utf8) + { + // oh please oh please oh please oh please oh please + if(function_exists('mb_convert_encoding')) { + return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); + } + + switch(strlen($utf8)) { + case 1: + // this case should never be reached, because we are in ASCII range + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return $utf8; + + case 2: + // return a UTF-16 character from a 2-byte UTF-8 char + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0x07 & (ord($utf8{0}) >> 2)) + . chr((0xC0 & (ord($utf8{0}) << 6)) + | (0x3F & ord($utf8{1}))); + + case 3: + // return a UTF-16 character from a 3-byte UTF-8 char + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr((0xF0 & (ord($utf8{0}) << 4)) + | (0x0F & (ord($utf8{1}) >> 2))) + . chr((0xC0 & (ord($utf8{1}) << 6)) + | (0x7F & ord($utf8{2}))); + } + + // ignoring UTF-32 for now, sorry + return ''; + } + + /** + * encodes an arbitrary variable into JSON format + * + * @param mixed $var any number, boolean, string, array, or object to be encoded. + * see argument 1 to Services_JSON() above for array-parsing behavior. + * if var is a strng, note that encode() always expects it + * to be in ASCII or UTF-8 format! + * + * @return mixed JSON string representation of input var or an error if a problem occurs + * @access public + */ + private function json_encode($var) + { + + if(is_object($var)) { + if(in_array($var,$this->json_objectStack)) { + return '"** Recursion **"'; + } + } + + switch (gettype($var)) { + case 'boolean': + return $var ? 'true' : 'false'; + + case 'NULL': + return 'null'; + + case 'integer': + return (int) $var; + + case 'double': + case 'float': + return (float) $var; + + case 'string': + // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT + $ascii = ''; + $strlen_var = strlen($var); + + /* + * Iterate over every character in the string, + * escaping with a slash or encoding to UTF-8 where necessary + */ + for ($c = 0; $c < $strlen_var; ++$c) { + + $ord_var_c = ord($var{$c}); + + switch (true) { + case $ord_var_c == 0x08: + $ascii .= '\b'; + break; + case $ord_var_c == 0x09: + $ascii .= '\t'; + break; + case $ord_var_c == 0x0A: + $ascii .= '\n'; + break; + case $ord_var_c == 0x0C: + $ascii .= '\f'; + break; + case $ord_var_c == 0x0D: + $ascii .= '\r'; + break; + + case $ord_var_c == 0x22: + case $ord_var_c == 0x2F: + case $ord_var_c == 0x5C: + // double quote, slash, slosh + $ascii .= '\\'.$var{$c}; + break; + + case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): + // characters U-00000000 - U-0000007F (same as ASCII) + $ascii .= $var{$c}; + break; + + case (($ord_var_c & 0xE0) == 0xC0): + // characters U-00000080 - U-000007FF, mask 110XXXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, ord($var{$c + 1})); + $c += 1; + $utf16 = $this->json_utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF0) == 0xE0): + // characters U-00000800 - U-0000FFFF, mask 1110XXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2})); + $c += 2; + $utf16 = $this->json_utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF8) == 0xF0): + // characters U-00010000 - U-001FFFFF, mask 11110XXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3})); + $c += 3; + $utf16 = $this->json_utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFC) == 0xF8): + // characters U-00200000 - U-03FFFFFF, mask 111110XX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3}), + ord($var{$c + 4})); + $c += 4; + $utf16 = $this->json_utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFE) == 0xFC): + // characters U-04000000 - U-7FFFFFFF, mask 1111110X + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3}), + ord($var{$c + 4}), + ord($var{$c + 5})); + $c += 5; + $utf16 = $this->json_utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + } + } + + return '"'.$ascii.'"'; + + case 'array': + /* + * As per JSON spec if any array key is not an integer + * we must treat the the whole array as an object. We + * also try to catch a sparsely populated associative + * array with numeric keys here because some JS engines + * will create an array with empty indexes up to + * max_index which can cause memory issues and because + * the keys, which may be relevant, will be remapped + * otherwise. + * + * As per the ECMA and JSON specification an object may + * have any string as a property. Unfortunately due to + * a hole in the ECMA specification if the key is a + * ECMA reserved word or starts with a digit the + * parameter is only accessible using ECMAScript's + * bracket notation. + */ + + // treat as a JSON object + if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) { + + $this->json_objectStack[] = $var; + + $properties = array_map(array($this, 'json_name_value'), + array_keys($var), + array_values($var)); + + array_pop($this->json_objectStack); + + foreach($properties as $property) { + if($property instanceof Exception) { + return $property; + } + } + + return '{' . join(',', $properties) . '}'; + } + + $this->json_objectStack[] = $var; + + // treat it like a regular array + $elements = array_map(array($this, 'json_encode'), $var); + + array_pop($this->json_objectStack); + + foreach($elements as $element) { + if($element instanceof Exception) { + return $element; + } + } + + return '[' . join(',', $elements) . ']'; + + case 'object': + $vars = self::encodeObject($var); + + $this->json_objectStack[] = $var; + + $properties = array_map(array($this, 'json_name_value'), + array_keys($vars), + array_values($vars)); + + array_pop($this->json_objectStack); + + foreach($properties as $property) { + if($property instanceof Exception) { + return $property; + } + } + + return '{' . join(',', $properties) . '}'; + + default: + return null; + } + } + + /** + * array-walking function for use in generating JSON-formatted name-value pairs + * + * @param string $name name of key to use + * @param mixed $value reference to an array element to be encoded + * + * @return string JSON-formatted name-value pair, like '"name":value' + * @access private + */ + private function json_name_value($name, $value) + { + // Encoding the $GLOBALS PHP array causes an infinite loop + // if the recursion is not reset here as it contains + // a reference to itself. This is the only way I have come up + // with to stop infinite recursion in this case. + if($name=='GLOBALS' + && is_array($value) + && array_key_exists('GLOBALS',$value)) { + $value['GLOBALS'] = '** Recursion **'; + } + + $encoded_value = $this->json_encode($value); + + if($encoded_value instanceof Exception) { + return $encoded_value; + } + + return $this->json_encode(strval($name)) . ':' . $encoded_value; + } +} diff --git a/framework/3rdParty/FirePHPCore/FirePHP.class.php4 b/framework/3rdParty/FirePHPCore/FirePHP.class.php4 new file mode 100644 index 00000000..3e20120d --- /dev/null +++ b/framework/3rdParty/FirePHPCore/FirePHP.class.php4 @@ -0,0 +1,1291 @@ +<?php +/** + * *** BEGIN LICENSE BLOCK ***** + * + * This file is part of FirePHP (http://www.firephp.org/). + * + * Software License Agreement (New BSD License) + * + * Copyright (c) 2006-2009, Christoph Dorn + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of Christoph Dorn nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ***** END LICENSE BLOCK ***** + * + * This verion of FirePHPCore is for use with PHP4. If you do not require PHP4 + * compatibility, it is suggested you use FirePHPCore.class.php instead. + * + * @copyright Copyright (C) 2007-2009 Christoph Dorn + * @author Christoph Dorn <christoph@christophdorn.com> + * @author Michael Day <manveru.alma@gmail.com> + * @license http://www.opensource.org/licenses/bsd-license.php + * @package FirePHP + */ + +/** + * FirePHP version + * + * @var string + */ +define('FirePHP_VERSION', '0.3'); + +/** + * Firebug LOG level + * + * Logs a message to firebug console + * + * @var string + */ +define('FirePHP_LOG', 'LOG'); + +/** + * Firebug INFO level + * + * Logs a message to firebug console and displays an info icon before the message + * + * @var string + */ +define('FirePHP_INFO', 'INFO'); + +/** + * Firebug WARN level + * + * Logs a message to firebug console, displays a warning icon before the message and colors the line turquoise + * + * @var string + */ +define('FirePHP_WARN', 'WARN'); + +/** + * Firebug ERROR level + * + * Logs a message to firebug console, displays an error icon before the message and colors the line yellow. Also increments the firebug error count. + * + * @var string + */ +define('FirePHP_ERROR', 'ERROR'); + +/** + * Dumps a variable to firebug's server panel + * + * @var string + */ +define('FirePHP_DUMP', 'DUMP'); + +/** + * Displays a stack trace in firebug console + * + * @var string + */ +define('FirePHP_TRACE', 'TRACE'); + +/** + * Displays a table in firebug console + * + * @var string + */ +define('FirePHP_TABLE', 'TABLE'); + +/** + * Starts a group in firebug console + * + * @var string + */ +define('FirePHP_GROUP_START', 'GROUP_START'); + +/** + * Ends a group in firebug console + * + * @var string + */ +define('FirePHP_GROUP_END', 'GROUP_END'); + +/** + * Sends the given data to the FirePHP Firefox Extension. + * The data can be displayed in the Firebug Console or in the + * "Server" request tab. + * + * For more information see: http://www.firephp.org/ + * + * @copyright Copyright (C) 2007-2009 Christoph Dorn + * @author Christoph Dorn <christoph@christophdorn.com> + * @author Michael Day <manveru.alma@gmail.com> + * @license http://www.opensource.org/licenses/bsd-license.php + * @package FirePHP + */ +class FirePHP { + /** + * Wildfire protocol message index + * + * @var int + */ + var $messageIndex = 1; + + /** + * Options for the library + * + * @var array + */ + var $options = array('maxObjectDepth' => 10, + 'maxArrayDepth' => 20, + 'useNativeJsonEncode' => true, + 'includeLineNumbers' => true); + + /** + * Filters used to exclude object members when encoding + * + * @var array + */ + var $objectFilters = array(); + + /** + * A stack of objects used to detect recursion during object encoding + * + * @var object + */ + var $objectStack = array(); + + /** + * Flag to enable/disable logging + * + * @var boolean + */ + var $enabled = true; + + /** + * The object constructor + */ + function FirePHP() { + } + + + /** + * When the object gets serialized only include specific object members. + * + * @return array + */ + function __sleep() { + return array('options','objectFilters','enabled'); + } + + /** + * Gets singleton instance of FirePHP + * + * @param boolean $AutoCreate + * @return FirePHP + */ + function &getInstance($AutoCreate=false) { + global $FirePHP_Instance; + + if($AutoCreate===true && !$FirePHP_Instance) { + $FirePHP_Instance = new FirePHP(); + } + + return $FirePHP_Instance; + } + + /** + * Enable and disable logging to Firebug + * + * @param boolean $Enabled TRUE to enable, FALSE to disable + * @return void + */ + function setEnabled($Enabled) { + $this->enabled = $Enabled; + } + + /** + * Check if logging is enabled + * + * @return boolean TRUE if enabled + */ + function getEnabled() { + return $this->enabled; + } + + /** + * Specify a filter to be used when encoding an object + * + * Filters are used to exclude object members. + * + * @param string $Class The class name of the object + * @param array $Filter An array of members to exclude + * @return void + */ + function setObjectFilter($Class, $Filter) { + $this->objectFilters[$Class] = $Filter; + } + + /** + * Set some options for the library + * + * Options: + * - maxObjectDepth: The maximum depth to traverse objects (default: 10) + * - maxArrayDepth: The maximum depth to traverse arrays (default: 20) + * - useNativeJsonEncode: If true will use json_encode() (default: true) + * - includeLineNumbers: If true will include line numbers and filenames (default: true) + * + * @param array $Options The options to be set + * @return void + */ + function setOptions($Options) { + $this->options = array_merge($this->options,$Options); + } + + /** + * Get options from the library + * + * @return array The currently set options + */ + function getOptions() { + return $this->options; + } + + /** + * Register FirePHP as your error handler + * + * Will use FirePHP to log each php error. + * + * @return mixed Returns a string containing the previously defined error handler (if any) + */ + function registerErrorHandler() + { + //NOTE: The following errors will not be caught by this error handler: + // E_ERROR, E_PARSE, E_CORE_ERROR, + // E_CORE_WARNING, E_COMPILE_ERROR, + // E_COMPILE_WARNING, E_STRICT + + return set_error_handler(array($this,'errorHandler')); + } + + /** + * FirePHP's error handler + * + * Logs each php error that will occur. + * + * @param int $errno + * @param string $errstr + * @param string $errfile + * @param int $errline + * @param array $errcontext + */ + function errorHandler($errno, $errstr, $errfile, $errline, $errcontext) + { + global $FirePHP_Instance; + // Don't log error if error reporting is switched off + if (error_reporting() == 0) { + return; + } + // Only log error for errors we are asking for + if (error_reporting() & $errno) { + $FirePHP_Instance->group($errstr); + $FirePHP_Instance->error("{$errfile}, line $errline"); + $FirePHP_Instance->groupEnd(); + } + } + + /** + * Register FirePHP driver as your assert callback + * + * @return mixed Returns the original setting + */ + function registerAssertionHandler() + { + return assert_options(ASSERT_CALLBACK, array($this, 'assertionHandler')); + } + + /** + * FirePHP's assertion handler + * + * Logs all assertions to your firebug console and then stops the script. + * + * @param string $file File source of assertion + * @param int $line Line source of assertion + * @param mixed $code Assertion code + */ + function assertionHandler($file, $line, $code) + { + $this->fb($code, 'Assertion Failed', FirePHP_ERROR, array('File'=>$file,'Line'=>$line)); + } + + /** + * Set custom processor url for FirePHP + * + * @param string $URL + */ + function setProcessorUrl($URL) + { + $this->setHeader('X-FirePHP-ProcessorURL', $URL); + } + + /** + * Set custom renderer url for FirePHP + * + * @param string $URL + */ + function setRendererUrl($URL) + { + $this->setHeader('X-FirePHP-RendererURL', $URL); + } + + /** + * Start a group for following messages. + * + * Options: + * Collapsed: [true|false] + * Color: [#RRGGBB|ColorName] + * + * @param string $Name + * @param array $Options OPTIONAL Instructions on how to log the group + * @return true + * @throws Exception + */ + function group($Name, $Options=null) { + + if(!$Name) { + trigger_error('You must specify a label for the group!'); + } + + if($Options) { + if(!is_array($Options)) { + trigger_error('Options must be defined as an array!'); + } + if(array_key_exists('Collapsed', $Options)) { + $Options['Collapsed'] = ($Options['Collapsed'])?'true':'false'; + } + } + + return $this->fb(null, $Name, FirePHP_GROUP_START, $Options); + } + + /** + * Ends a group you have started before + * + * @return true + * @throws Exception + */ + function groupEnd() { + return $this->fb(null, null, FirePHP_GROUP_END); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::LOG + * @param mixes $Object + * @param string $Label + * @return true + * @throws Exception + */ + function log($Object, $Label=null) { + return $this->fb($Object, $Label, FirePHP_LOG); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::INFO + * @param mixes $Object + * @param string $Label + * @return true + * @throws Exception + */ + function info($Object, $Label=null) { + return $this->fb($Object, $Label, FirePHP_INFO); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::WARN + * @param mixes $Object + * @param string $Label + * @return true + * @throws Exception + */ + function warn($Object, $Label=null) { + return $this->fb($Object, $Label, FirePHP_WARN); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::ERROR + * @param mixes $Object + * @param string $Label + * @return true + * @throws Exception + */ + function error($Object, $Label=null) { + return $this->fb($Object, $Label, FirePHP_ERROR); + } + + /** + * Dumps key and variable to firebug server panel + * + * @see FirePHP::DUMP + * @param string $Key + * @param mixed $Variable + * @return true + * @throws Exception + */ + function dump($Key, $Variable) { + return $this->fb($Variable, $Key, FirePHP_DUMP); + } + + /** + * Log a trace in the firebug console + * + * @see FirePHP::TRACE + * @param string $Label + * @return true + * @throws Exception + */ + function trace($Label) { + return $this->fb($Label, FirePHP_TRACE); + } + + /** + * Log a table in the firebug console + * + * @see FirePHP::TABLE + * @param string $Label + * @param string $Table + * @return true + * @throws Exception + */ + function table($Label, $Table) { + return $this->fb($Table, $Label, FirePHP_TABLE); + } + + /** + * Check if FirePHP is installed on client + * + * @return boolean + */ + function detectClientExtension() { + /* Check if FirePHP is installed on client */ + if(!@preg_match_all('/\sFirePHP\/([\.|\d]*)\s?/si',$this->getUserAgent(),$m) || + !version_compare($m[1][0],'0.0.6','>=')) { + return false; + } + return true; + } + + /** + * Log varible to Firebug + * + * @see http://www.firephp.org/Wiki/Reference/Fb + * @param mixed $Object The variable to be logged + * @return true Return TRUE if message was added to headers, FALSE otherwise + * @throws Exception + */ + function fb($Object) { + + if(!$this->enabled) { + return false; + } + + if (headers_sent($filename, $linenum)) { + trigger_error('Headers already sent in '.$filename.' on line '.$linenum.'. Cannot send log data to FirePHP. You must have Output Buffering enabled via ob_start() or output_buffering ini directive.'); + } + + $Type = null; + $Label = null; + $Options = array(); + + if(func_num_args()==1) { + } else + if(func_num_args()==2) { + switch(func_get_arg(1)) { + case FirePHP_LOG: + case FirePHP_INFO: + case FirePHP_WARN: + case FirePHP_ERROR: + case FirePHP_DUMP: + case FirePHP_TRACE: + case FirePHP_TABLE: + case FirePHP_GROUP_START: + case FirePHP_GROUP_END: + $Type = func_get_arg(1); + break; + default: + $Label = func_get_arg(1); + break; + } + } else + if(func_num_args()==3) { + $Type = func_get_arg(2); + $Label = func_get_arg(1); + } else + if(func_num_args()==4) { + $Type = func_get_arg(2); + $Label = func_get_arg(1); + $Options = func_get_arg(3); + } else { + trigger_error('Wrong number of arguments to fb() function!'); + } + + + if(!$this->detectClientExtension()) { + return false; + } + + $meta = array(); + $skipFinalObjectEncode = false; + + if($Type==FirePHP_TRACE) { + + $trace = debug_backtrace(); + if(!$trace) return false; + for( $i=0 ; $i<sizeof($trace) ; $i++ ) { + + if(isset($trace[$i]['class']) + && isset($trace[$i]['file']) + && ($trace[$i]['class']=='FirePHP' + || $trace[$i]['class']=='FB') + && (substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php' + || substr($this->_standardizePath($trace[$i]['file']),-29,29)=='FirePHPCore/FirePHP.class.php')) { + /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */ + } else + if(isset($trace[$i]['class']) + && isset($trace[$i+1]['file']) + && $trace[$i]['class']=='FirePHP' + && substr($this->_standardizePath($trace[$i+1]['file']),-18,18)=='FirePHPCore/fb.php') { + /* Skip fb() */ + } else + if($trace[$i]['function']=='fb' + || $trace[$i]['function']=='trace' + || $trace[$i]['function']=='send') { + $Object = array('Class'=>isset($trace[$i]['class'])?$trace[$i]['class']:'', + 'Type'=>isset($trace[$i]['type'])?$trace[$i]['type']:'', + 'Function'=>isset($trace[$i]['function'])?$trace[$i]['function']:'', + 'Message'=>$trace[$i]['args'][0], + 'File'=>isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'', + 'Line'=>isset($trace[$i]['line'])?$trace[$i]['line']:'', + 'Args'=>isset($trace[$i]['args'])?$this->encodeObject($trace[$i]['args']):'', + 'Trace'=>$this->_escapeTrace(array_splice($trace,$i+1))); + + $skipFinalObjectEncode = true; + $meta['file'] = isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):''; + $meta['line'] = isset($trace[$i]['line'])?$trace[$i]['line']:''; + break; + } + } + + } else + if($Type==FirePHP_TABLE) { + + if(isset($Object[0]) && is_string($Object[0])) { + $Object[1] = $this->encodeTable($Object[1]); + } else { + $Object = $this->encodeTable($Object); + } + + $skipFinalObjectEncode = true; + + } else + if($Type==FirePHP_GROUP_START) { + + if(!$Label) { + trigger_error('You must specify a label for the group!'); + } + } else { + if($Type===null) { + $Type = FirePHP_LOG; + } + } + + if($this->options['includeLineNumbers']) { + if(!isset($meta['file']) || !isset($meta['line'])) { + + $trace = debug_backtrace(); + for( $i=0 ; $trace && $i<sizeof($trace) ; $i++ ) { + + if(isset($trace[$i]['class']) + && isset($trace[$i]['file']) + && ($trace[$i]['class']=='FirePHP' + || $trace[$i]['class']=='FB') + && (substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php' + || substr($this->_standardizePath($trace[$i]['file']),-29,29)=='FirePHPCore/FirePHP.class.php')) { + /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */ + } else + if(isset($trace[$i]['class']) + && isset($trace[$i+1]['file']) + && $trace[$i]['class']=='FirePHP' + && substr($this->_standardizePath($trace[$i+1]['file']),-18,18)=='FirePHPCore/fb.php') { + /* Skip fb() */ + } else + if(isset($trace[$i]['file']) + && substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php') { + /* Skip FB::fb() */ + } else { + $meta['file'] = isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):''; + $meta['line'] = isset($trace[$i]['line'])?$trace[$i]['line']:''; + break; + } + } + + } + } else { + unset($meta['file']); + unset($meta['line']); + } + + $this->setHeader('X-Wf-Protocol-1','http://meta.wildfirehq.org/Protocol/JsonStream/0.2'); + $this->setHeader('X-Wf-1-Plugin-1','http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/'.FirePHP_VERSION); + + $structure_index = 1; + if($Type==FirePHP_DUMP) { + $structure_index = 2; + $this->setHeader('X-Wf-1-Structure-2','http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1'); + } else { + $this->setHeader('X-Wf-1-Structure-1','http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1'); + } + + if($Type==FirePHP_DUMP) { + $msg = '{"'.$Label.'":'.$this->jsonEncode($Object, $skipFinalObjectEncode).'}'; + } else { + $msg_meta = $Options; + $msg_meta['Type'] = $Type; + if($Label!==null) { + $msg_meta['Label'] = $Label; + } + if(isset($meta['file']) && !isset($msg_meta['File'])) { + $msg_meta['File'] = $meta['file']; + } + if(isset($meta['line']) && !isset($msg_meta['Line'])) { + $msg_meta['Line'] = $meta['line']; + } + $msg = '['.$this->jsonEncode($msg_meta).','.$this->jsonEncode($Object, $skipFinalObjectEncode).']'; + } + + $parts = explode("\n",chunk_split($msg, 5000, "\n")); + + for( $i=0 ; $i<count($parts) ; $i++) { + + $part = $parts[$i]; + if ($part) { + + if(count($parts)>2) { + // Message needs to be split into multiple parts + $this->setHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex, + (($i==0)?strlen($msg):'') + . '|' . $part . '|' + . (($i<count($parts)-2)?'\\':'')); + } else { + $this->setHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex, + strlen($part) . '|' . $part . '|'); + } + + $this->messageIndex++; + + if ($this->messageIndex > 99999) { + trigger_error('Maximum number (99,999) of messages reached!'); + } + } + } + + $this->setHeader('X-Wf-1-Index',$this->messageIndex-1); + + return true; + } + + + /** + * Standardizes path for windows systems. + * + * @param string $Path + * @return string + */ + function _standardizePath($Path) { + return preg_replace('/\\\\+/','/',$Path); + } + + /** + * Escape trace path for windows systems + * + * @param array $Trace + * @return array + */ + function _escapeTrace($Trace) { + if(!$Trace) return $Trace; + for( $i=0 ; $i<sizeof($Trace) ; $i++ ) { + if(isset($Trace[$i]['file'])) { + $Trace[$i]['file'] = $this->_escapeTraceFile($Trace[$i]['file']); + } + if(isset($Trace[$i]['args'])) { + $Trace[$i]['args'] = $this->encodeObject($Trace[$i]['args']); + } + } + return $Trace; + } + + /** + * Escape file information of trace for windows systems + * + * @param string $File + * @return string + */ + function _escapeTraceFile($File) { + /* Check if we have a windows filepath */ + if(strpos($File,'\\')) { + /* First strip down to single \ */ + + $file = preg_replace('/\\\\+/','\\',$File); + + return $file; + } + return $File; + } + + /** + * Send header + * + * @param string $Name + * @param string_type $Value + */ + function setHeader($Name, $Value) { + return header($Name.': '.$Value); + } + + /** + * Get user agent + * + * @return string|false + */ + function getUserAgent() { + if(!isset($_SERVER['HTTP_USER_AGENT'])) return false; + return $_SERVER['HTTP_USER_AGENT']; + } + + /** + * Encode an object into a JSON string + * + * Uses PHP's jeson_encode() if available + * + * @param object $Object The object to be encoded + * @return string The JSON string + */ + function jsonEncode($Object, $skipObjectEncode=false) + { + if(!$skipObjectEncode) { + $Object = $this->encodeObject($Object); + } + + if(function_exists('json_encode') + && $this->options['useNativeJsonEncode']!=false) { + + return json_encode($Object); + } else { + return $this->json_encode($Object); + } + } + + /** + * Encodes a table by encoding each row and column with encodeObject() + * + * @param array $Table The table to be encoded + * @return array + */ + function encodeTable($Table) { + + if(!$Table) return $Table; + + $new_table = array(); + foreach($Table as $row) { + + if(is_array($row)) { + $new_row = array(); + + foreach($row as $item) { + $new_row[] = $this->encodeObject($item); + } + + $new_table[] = $new_row; + } + } + + return $new_table; + } + + /** + * Encodes an object + * + * @param Object $Object The object to be encoded + * @param int $Depth The current traversal depth + * @return array All members of the object + */ + function encodeObject($Object, $ObjectDepth = 1, $ArrayDepth = 1) + { + $return = array(); + + if (is_resource($Object)) { + + return '** '.(string)$Object.' **'; + + } else + if (is_object($Object)) { + + if ($ObjectDepth > $this->options['maxObjectDepth']) { + return '** Max Object Depth ('.$this->options['maxObjectDepth'].') **'; + } + + foreach ($this->objectStack as $refVal) { + if ($refVal === $Object) { + return '** Recursion ('.get_class($Object).') **'; + } + } + array_push($this->objectStack, $Object); + + $return['__className'] = $class = get_class($Object); + + $members = (array)$Object; + + // Include all members that are not defined in the class + // but exist in the object + foreach( $members as $raw_name => $value ) { + + $name = $raw_name; + + if ($name{0} == "\0") { + $parts = explode("\0", $name); + $name = $parts[2]; + } + + if(!isset($properties[$name])) { + $name = 'undeclared:'.$name; + + if(!(isset($this->objectFilters[$class]) + && is_array($this->objectFilters[$class]) + && in_array($raw_name,$this->objectFilters[$class]))) { + + $return[$name] = $this->encodeObject($value, $ObjectDepth + 1, 1); + } else { + $return[$name] = '** Excluded by Filter **'; + } + } + } + + array_pop($this->objectStack); + + } elseif (is_array($Object)) { + + if ($ArrayDepth > $this->options['maxArrayDepth']) { + return '** Max Array Depth ('.$this->options['maxArrayDepth'].') **'; + } + + foreach ($Object as $key => $val) { + + // Encoding the $GLOBALS PHP array causes an infinite loop + // if the recursion is not reset here as it contains + // a reference to itself. This is the only way I have come up + // with to stop infinite recursion in this case. + if($key=='GLOBALS' + && is_array($val) + && array_key_exists('GLOBALS',$val)) { + $val['GLOBALS'] = '** Recursion (GLOBALS) **'; + } + + $return[$key] = $this->encodeObject($val, 1, $ArrayDepth + 1); + } + } else { + if($this->is_utf8($Object)) { + return $Object; + } else { + return utf8_encode($Object); + } + } + return $return; + + } + + /** + * Returns true if $string is valid UTF-8 and false otherwise. + * + * @param mixed $str String to be tested + * @return boolean + */ + function is_utf8($str) { + $c=0; $b=0; + $bits=0; + $len=strlen($str); + for($i=0; $i<$len; $i++){ + $c=ord($str[$i]); + if($c > 128){ + if(($c >= 254)) return false; + elseif($c >= 252) $bits=6; + elseif($c >= 248) $bits=5; + elseif($c >= 240) $bits=4; + elseif($c >= 224) $bits=3; + elseif($c >= 192) $bits=2; + else return false; + if(($i+$bits) > $len) return false; + while($bits > 1){ + $i++; + $b=ord($str[$i]); + if($b < 128 || $b > 191) return false; + $bits--; + } + } + } + return true; + } + + /** + * Converts to and from JSON format. + * + * JSON (JavaScript Object Notation) is a lightweight data-interchange + * format. It is easy for humans to read and write. It is easy for machines + * to parse and generate. It is based on a subset of the JavaScript + * Programming Language, Standard ECMA-262 3rd Edition - December 1999. + * This feature can also be found in Python. JSON is a text format that is + * completely language independent but uses conventions that are familiar + * to programmers of the C-family of languages, including C, C++, C#, Java, + * JavaScript, Perl, TCL, and many others. These properties make JSON an + * ideal data-interchange language. + * + * This package provides a simple encoder and decoder for JSON notation. It + * is intended for use with client-side Javascript applications that make + * use of HTTPRequest to perform server communication functions - data can + * be encoded into JSON notation for use in a client-side javascript, or + * decoded from incoming Javascript requests. JSON format is native to + * Javascript, and can be directly eval()'ed with no further parsing + * overhead + * + * All strings should be in ASCII or UTF-8 format! + * + * LICENSE: Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: Redistributions of source code must retain the + * above copyright notice, this list of conditions and the following + * disclaimer. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * @category + * @package Services_JSON + * @author Michal Migurski <mike-json@teczno.com> + * @author Matt Knapp <mdknapp[at]gmail[dot]com> + * @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com> + * @author Christoph Dorn <christoph@christophdorn.com> + * @copyright 2005 Michal Migurski + * @version CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $ + * @license http://www.opensource.org/licenses/bsd-license.php + * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198 + */ + + + /** + * Keep a list of objects as we descend into the array so we can detect recursion. + */ + var $json_objectStack = array(); + + + /** + * convert a string from one UTF-8 char to one UTF-16 char + * + * Normally should be handled by mb_convert_encoding, but + * provides a slower PHP-only method for installations + * that lack the multibye string extension. + * + * @param string $utf8 UTF-8 character + * @return string UTF-16 character + * @access private + */ + function json_utf82utf16($utf8) + { + // oh please oh please oh please oh please oh please + if(function_exists('mb_convert_encoding')) { + return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); + } + + switch(strlen($utf8)) { + case 1: + // this case should never be reached, because we are in ASCII range + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return $utf8; + + case 2: + // return a UTF-16 character from a 2-byte UTF-8 char + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0x07 & (ord($utf8{0}) >> 2)) + . chr((0xC0 & (ord($utf8{0}) << 6)) + | (0x3F & ord($utf8{1}))); + + case 3: + // return a UTF-16 character from a 3-byte UTF-8 char + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr((0xF0 & (ord($utf8{0}) << 4)) + | (0x0F & (ord($utf8{1}) >> 2))) + . chr((0xC0 & (ord($utf8{1}) << 6)) + | (0x7F & ord($utf8{2}))); + } + + // ignoring UTF-32 for now, sorry + return ''; + } + + /** + * encodes an arbitrary variable into JSON format + * + * @param mixed $var any number, boolean, string, array, or object to be encoded. + * see argument 1 to Services_JSON() above for array-parsing behavior. + * if var is a strng, note that encode() always expects it + * to be in ASCII or UTF-8 format! + * + * @return mixed JSON string representation of input var or an error if a problem occurs + * @access public + */ + function json_encode($var) + { + + if(is_object($var)) { + if(in_array($var,$this->json_objectStack)) { + return '"** Recursion **"'; + } + } + + switch (gettype($var)) { + case 'boolean': + return $var ? 'true' : 'false'; + + case 'NULL': + return 'null'; + + case 'integer': + return (int) $var; + + case 'double': + case 'float': + return (float) $var; + + case 'string': + // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT + $ascii = ''; + $strlen_var = strlen($var); + + /* + * Iterate over every character in the string, + * escaping with a slash or encoding to UTF-8 where necessary + */ + for ($c = 0; $c < $strlen_var; ++$c) { + + $ord_var_c = ord($var{$c}); + + switch (true) { + case $ord_var_c == 0x08: + $ascii .= '\b'; + break; + case $ord_var_c == 0x09: + $ascii .= '\t'; + break; + case $ord_var_c == 0x0A: + $ascii .= '\n'; + break; + case $ord_var_c == 0x0C: + $ascii .= '\f'; + break; + case $ord_var_c == 0x0D: + $ascii .= '\r'; + break; + + case $ord_var_c == 0x22: + case $ord_var_c == 0x2F: + case $ord_var_c == 0x5C: + // double quote, slash, slosh + $ascii .= '\\'.$var{$c}; + break; + + case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): + // characters U-00000000 - U-0000007F (same as ASCII) + $ascii .= $var{$c}; + break; + + case (($ord_var_c & 0xE0) == 0xC0): + // characters U-00000080 - U-000007FF, mask 110XXXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, ord($var{$c + 1})); + $c += 1; + $utf16 = $this->json_utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF0) == 0xE0): + // characters U-00000800 - U-0000FFFF, mask 1110XXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2})); + $c += 2; + $utf16 = $this->json_utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF8) == 0xF0): + // characters U-00010000 - U-001FFFFF, mask 11110XXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3})); + $c += 3; + $utf16 = $this->json_utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFC) == 0xF8): + // characters U-00200000 - U-03FFFFFF, mask 111110XX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3}), + ord($var{$c + 4})); + $c += 4; + $utf16 = $this->json_utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFE) == 0xFC): + // characters U-04000000 - U-7FFFFFFF, mask 1111110X + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3}), + ord($var{$c + 4}), + ord($var{$c + 5})); + $c += 5; + $utf16 = $this->json_utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + } + } + + return '"'.$ascii.'"'; + + case 'array': + /* + * As per JSON spec if any array key is not an integer + * we must treat the the whole array as an object. We + * also try to catch a sparsely populated associative + * array with numeric keys here because some JS engines + * will create an array with empty indexes up to + * max_index which can cause memory issues and because + * the keys, which may be relevant, will be remapped + * otherwise. + * + * As per the ECMA and JSON specification an object may + * have any string as a property. Unfortunately due to + * a hole in the ECMA specification if the key is a + * ECMA reserved word or starts with a digit the + * parameter is only accessible using ECMAScript's + * bracket notation. + */ + + // treat as a JSON object + if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) { + + $this->json_objectStack[] = $var; + + $properties = array_map(array($this, 'json_name_value'), + array_keys($var), + array_values($var)); + + array_pop($this->json_objectStack); + + return '{' . join(',', $properties) . '}'; + } + + $this->json_objectStack[] = $var; + + // treat it like a regular array + $elements = array_map(array($this, 'json_encode'), $var); + + array_pop($this->json_objectStack); + + return '[' . join(',', $elements) . ']'; + + case 'object': + $vars = FirePHP::encodeObject($var); + + $this->json_objectStack[] = $var; + + $properties = array_map(array($this, 'json_name_value'), + array_keys($vars), + array_values($vars)); + + array_pop($this->json_objectStack); + + return '{' . join(',', $properties) . '}'; + + default: + return null; + } + } + + /** + * array-walking function for use in generating JSON-formatted name-value pairs + * + * @param string $name name of key to use + * @param mixed $value reference to an array element to be encoded + * + * @return string JSON-formatted name-value pair, like '"name":value' + * @access private + */ + function json_name_value($name, $value) + { + // Encoding the $GLOBALS PHP array causes an infinite loop + // if the recursion is not reset here as it contains + // a reference to itself. This is the only way I have come up + // with to stop infinite recursion in this case. + if($name=='GLOBALS' + && is_array($value) + && array_key_exists('GLOBALS',$value)) { + $value['GLOBALS'] = '** Recursion **'; + } + + $encoded_value = $this->json_encode($value); + + return $this->json_encode(strval($name)) . ':' . $encoded_value; + } +} + diff --git a/framework/3rdParty/FirePHPCore/LICENSE b/framework/3rdParty/FirePHPCore/LICENSE new file mode 100644 index 00000000..3e390f9d --- /dev/null +++ b/framework/3rdParty/FirePHPCore/LICENSE @@ -0,0 +1,29 @@ +Software License Agreement (New BSD License) + +Copyright (c) 2006-2009, Christoph Dorn +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name of Christoph Dorn nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/framework/3rdParty/FirePHPCore/fb.php b/framework/3rdParty/FirePHPCore/fb.php new file mode 100644 index 00000000..9d1857cb --- /dev/null +++ b/framework/3rdParty/FirePHPCore/fb.php @@ -0,0 +1,261 @@ +<?php + +/* ***** BEGIN LICENSE BLOCK ***** + * + * This file is part of FirePHP (http://www.firephp.org/). + * + * Software License Agreement (New BSD License) + * + * Copyright (c) 2006-2009, Christoph Dorn + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of Christoph Dorn nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ***** END LICENSE BLOCK ***** + * + * @copyright Copyright (C) 2007-2009 Christoph Dorn + * @author Christoph Dorn <christoph@christophdorn.com> + * @license http://www.opensource.org/licenses/bsd-license.php + * @package FirePHP + */ + +require_once dirname(__FILE__).'/FirePHP.class.php'; + +/** + * Sends the given data to the FirePHP Firefox Extension. + * The data can be displayed in the Firebug Console or in the + * "Server" request tab. + * + * @see http://www.firephp.org/Wiki/Reference/Fb + * @param mixed $Object + * @return true + * @throws Exception + */ +function fb() +{ + $instance = FirePHP::getInstance(true); + + $args = func_get_args(); + return call_user_func_array(array($instance,'fb'),$args); +} + + +class FB +{ + /** + * Enable and disable logging to Firebug + * + * @see FirePHP->setEnabled() + * @param boolean $Enabled TRUE to enable, FALSE to disable + * @return void + */ + public static function setEnabled($Enabled) { + $instance = FirePHP::getInstance(true); + $instance->setEnabled($Enabled); + } + + /** + * Check if logging is enabled + * + * @see FirePHP->getEnabled() + * @return boolean TRUE if enabled + */ + public static function getEnabled() { + $instance = FirePHP::getInstance(true); + return $instance->getEnabled(); + } + + /** + * Specify a filter to be used when encoding an object + * + * Filters are used to exclude object members. + * + * @see FirePHP->setObjectFilter() + * @param string $Class The class name of the object + * @param array $Filter An array or members to exclude + * @return void + */ + public static function setObjectFilter($Class, $Filter) { + $instance = FirePHP::getInstance(true); + $instance->setObjectFilter($Class, $Filter); + } + + /** + * Set some options for the library + * + * @see FirePHP->setOptions() + * @param array $Options The options to be set + * @return void + */ + public static function setOptions($Options) { + $instance = FirePHP::getInstance(true); + $instance->setOptions($Options); + } + + /** + * Get options for the library + * + * @see FirePHP->getOptions() + * @return array The options + */ + public static function getOptions() { + $instance = FirePHP::getInstance(true); + return $instance->getOptions(); + } + + /** + * Log object to firebug + * + * @see http://www.firephp.org/Wiki/Reference/Fb + * @param mixed $Object + * @return true + * @throws Exception + */ + public static function send() + { + $instance = FirePHP::getInstance(true); + $args = func_get_args(); + return call_user_func_array(array($instance,'fb'),$args); + } + + /** + * Start a group for following messages + * + * Options: + * Collapsed: [true|false] + * Color: [#RRGGBB|ColorName] + * + * @param string $Name + * @param array $Options OPTIONAL Instructions on how to log the group + * @return true + */ + public static function group($Name, $Options=null) { + $instance = FirePHP::getInstance(true); + return $instance->group($Name, $Options); + } + + /** + * Ends a group you have started before + * + * @return true + * @throws Exception + */ + public static function groupEnd() { + return self::send(null, null, FirePHP::GROUP_END); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::LOG + * @param mixes $Object + * @param string $Label + * @return true + * @throws Exception + */ + public static function log($Object, $Label=null) { + return self::send($Object, $Label, FirePHP::LOG); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::INFO + * @param mixes $Object + * @param string $Label + * @return true + * @throws Exception + */ + public static function info($Object, $Label=null) { + return self::send($Object, $Label, FirePHP::INFO); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::WARN + * @param mixes $Object + * @param string $Label + * @return true + * @throws Exception + */ + public static function warn($Object, $Label=null) { + return self::send($Object, $Label, FirePHP::WARN); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::ERROR + * @param mixes $Object + * @param string $Label + * @return true + * @throws Exception + */ + public static function error($Object, $Label=null) { + return self::send($Object, $Label, FirePHP::ERROR); + } + + /** + * Dumps key and variable to firebug server panel + * + * @see FirePHP::DUMP + * @param string $Key + * @param mixed $Variable + * @return true + * @throws Exception + */ + public static function dump($Key, $Variable) { + return self::send($Variable, $Key, FirePHP::DUMP); + } + + /** + * Log a trace in the firebug console + * + * @see FirePHP::TRACE + * @param string $Label + * @return true + * @throws Exception + */ + public static function trace($Label) { + return self::send($Label, FirePHP::TRACE); + } + + /** + * Log a table in the firebug console + * + * @see FirePHP::TABLE + * @param string $Label + * @param string $Table + * @return true + * @throws Exception + */ + public static function table($Label, $Table) { + return self::send($Table, $Label, FirePHP::TABLE); + } + +} + diff --git a/framework/3rdParty/FirePHPCore/fb.php4 b/framework/3rdParty/FirePHPCore/fb.php4 new file mode 100644 index 00000000..5b69e348 --- /dev/null +++ b/framework/3rdParty/FirePHPCore/fb.php4 @@ -0,0 +1,251 @@ +<?php + +/* ***** BEGIN LICENSE BLOCK ***** + * + * This file is part of FirePHP (http://www.firephp.org/). + * + * Software License Agreement (New BSD License) + * + * Copyright (c) 2006-2009, Christoph Dorn + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of Christoph Dorn nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ***** END LICENSE BLOCK ***** + * + * @copyright Copyright (C) 2007-2009 Christoph Dorn + * @author Christoph Dorn <christoph@christophdorn.com> + * @author Michael Day <manveru.alma@gmail.com> + * @license http://www.opensource.org/licenses/bsd-license.php + * @package FirePHP + */ + +require_once dirname(__FILE__).'/FirePHP.class.php4'; + +/** + * Sends the given data to the FirePHP Firefox Extension. + * The data can be displayed in the Firebug Console or in the + * "Server" request tab. + * + * @see http://www.firephp.org/Wiki/Reference/Fb + * @param mixed $Object + * @return true + * @throws Exception + */ +function fb() +{ + $instance =& FirePHP::getInstance(true); + + $args = func_get_args(); + return call_user_func_array(array(&$instance,'fb'),$args); +} + + +class FB +{ + /** + * Enable and disable logging to Firebug + * + * @see FirePHP->setEnabled() + * @param boolean $Enabled TRUE to enable, FALSE to disable + * @return void + */ + function setEnabled($Enabled) { + $instance =& FirePHP::getInstance(true); + $instance->setEnabled($Enabled); + } + + /** + * Check if logging is enabled + * + * @see FirePHP->getEnabled() + * @return boolean TRUE if enabled + */ + function getEnabled() { + $instance =& FirePHP::getInstance(true); + return $instance->getEnabled(); + } + + /** + * Specify a filter to be used when encoding an object + * + * Filters are used to exclude object members. + * + * @see FirePHP->setObjectFilter() + * @param string $Class The class name of the object + * @param array $Filter An array or members to exclude + * @return void + */ + function setObjectFilter($Class, $Filter) { + $instance =& FirePHP::getInstance(true); + $instance->setObjectFilter($Class, $Filter); + } + + /** + * Set some options for the library + * + * @see FirePHP->setOptions() + * @param array $Options The options to be set + * @return void + */ + function setOptions($Options) { + $instance =& FirePHP::getInstance(true); + $instance->setOptions($Options); + } + + /** + * Get options for the library + * + * @see FirePHP->getOptions() + * @return array The options + */ + function getOptions() { + $instance =& FirePHP::getInstance(true); + return $instance->getOptions(); + } + + /** + * Log object to firebug + * + * @see http://www.firephp.org/Wiki/Reference/Fb + * @param mixed $Object + * @return true + */ + function send() + { + $instance =& FirePHP::getInstance(true); + $args = func_get_args(); + return call_user_func_array(array(&$instance,'fb'),$args); + } + + /** + * Start a group for following messages + * + * Options: + * Collapsed: [true|false] + * Color: [#RRGGBB|ColorName] + * + * @param string $Name + * @param array $Options OPTIONAL Instructions on how to log the group + * @return true + */ + function group($Name, $Options=null) { + $instance =& FirePHP::getInstance(true); + return $instance->group($Name, $Options); + } + + /** + * Ends a group you have started before + * + * @return true + */ + function groupEnd() { + return FB::send(null, null, FirePHP_GROUP_END); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::LOG + * @param mixes $Object + * @param string $Label + * @return true + */ + function log($Object, $Label=null) { + return FB::send($Object, $Label, FirePHP_LOG); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::INFO + * @param mixes $Object + * @param string $Label + * @return true + */ + function info($Object, $Label=null) { + return FB::send($Object, $Label, FirePHP_INFO); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::WARN + * @param mixes $Object + * @param string $Label + * @return true + */ + function warn($Object, $Label=null) { + return FB::send($Object, $Label, FirePHP_WARN); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::ERROR + * @param mixes $Object + * @param string $Label + * @return true + */ + function error($Object, $Label=null) { + return FB::send($Object, $Label, FirePHP_ERROR); + } + + /** + * Dumps key and variable to firebug server panel + * + * @see FirePHP::DUMP + * @param string $Key + * @param mixed $Variable + * @return true + */ + function dump($Key, $Variable) { + return FB::send($Variable, $Key, FirePHP_DUMP); + } + + /** + * Log a trace in the firebug console + * + * @see FirePHP::TRACE + * @param string $Label + * @return true + */ + function trace($Label) { + return FB::send($Label, FirePHP_TRACE); + } + + /** + * Log a table in the firebug console + * + * @see FirePHP::TABLE + * @param string $Label + * @param string $Table + * @return true + */ + function table($Label, $Table) { + return FB::send($Table, $Label, FirePHP_TABLE); + } +} diff --git a/framework/3rdParty/readme.html b/framework/3rdParty/readme.html index ee6a49e7..bf9c243f 100644 --- a/framework/3rdParty/readme.html +++ b/framework/3rdParty/readme.html @@ -145,7 +145,13 @@ projects. <td>An interactive PHP Shell</td>
<td>Available throught the prado-cli.php script.</td>
</tr>
-
+<tr>
+ <td><a href="FirePHPCore">3rdParty/FirePHPCore</a></td>
+ <td><a href="http://www.firephp.org/">FirePHPCore</a></td>
+ <td><a href="FirePHPCore/LICENSE">BSD</a></td>
+ <td>FirePHP enables you to log to your Firebug Console using a simple PHP method call.</td>
+ <td>Available throught TFirePhpLogRoute</td>
+</tr>
</table>
</body>
diff --git a/framework/Caching/TDbCache.php b/framework/Caching/TDbCache.php index 2c2c863d..376ff249 100644 --- a/framework/Caching/TDbCache.php +++ b/framework/Caching/TDbCache.php @@ -31,14 +31,23 @@ Prado::using('System.Data.TDbConnection'); * The cached data is stored in a table in the specified database.
* By default, the name of the table is called 'pradocache'. If the table does not
* exist in the database, it will be automatically created with the following structure:
- * (itemkey CHAR(128) PRIMARY KEY, value BLOB, expire INT)
+ * <code>
+ * CREATE TABLE pradocache (itemkey CHAR(128), value BLOB, expire INT)
+ * CREATE INDEX IX_itemkey ON pradocache (itemkey)
+ * CREATE INDEX IX_expire ON pradocache (expire)
+ * </code>
*
* Note, some DBMS might not support BLOB type. In this case, replace 'BLOB' with a suitable
* binary data type (e.g. LONGBLOB in MySQL, BYTEA in PostgreSQL.)
*
+ * Important: Make sure that the indices are non-unique!
+ *
* If you want to change the cache table name, or if you want to create the table by yourself,
* you may set {@link setCacheTableName CacheTableName} and {@link setAutoCreateCacheTable AutoCreateCacheTableName} properties.
*
+ * {@link setFlushInterval FlushInterval} control how often expired items will be removed from cache.
+ * If you prefer to remove expired items manualy e.g. via cronjob you can disable automatic deletion by setting FlushInterval to '0'.
+ *
* The following basic cache operations are implemented:
* - {@link get} : retrieve the value with a key (if any) from cache
* - {@link set} : store the value with a key into cache
@@ -92,6 +101,18 @@ class TDbCache extends TCache */
private $_cacheTable='pradocache';
/**
+ * @var integer Interval expired items will be removed from cache
+ */
+ private $_flushInterval=60;
+ /**
+ * @var boolean
+ */
+ private $_cacheInitialized = false;
+ /**
+ * @var boolean
+ */
+ private $_createCheck= false;
+ /**
* @var boolean whether the cache DB table should be created automatically
*/
private $_autoCreate=true;
@@ -111,23 +132,70 @@ class TDbCache extends TCache /**
* Initializes this module.
- * This method is required by the IModule interface. It checks if the DbFile
- * property is set, and creates a SQLiteDatabase instance for it.
- * The database or the cache table does not exist, they will be created.
- * Expired values are also deleted.
+ * This method is required by the IModule interface.
+ * attach {@link doInitializeCache} to TApplication.OnLoadStateComplete event
+ * attach {@link doFlushCacheExpired} to TApplication.OnSaveState event
+ *
* @param TXmlElement configuration for this module, can be null
- * @throws TConfigurationException if sqlite extension is not installed,
- * DbFile is set invalid, or any error happens during creating database or cache table.
*/
public function init($config)
{
+ $this -> getApplication() -> attachEventHandler('OnLoadStateComplete', array($this, 'doInitializeCache'));
+ $this -> getApplication() -> attachEventHandler('OnSaveState', array($this, 'doFlushCacheExpired'));
+ parent::init($config);
+ }
+
+ /**
+ * Event listener for TApplication.OnSaveState
+ * @return void
+ * @since 3.1.5
+ * @see flushCacheExpired
+ */
+ public function doFlushCacheExpired()
+ {
+ $this->flushCacheExpired(false);
+ }
+
+ /**
+ * Event listener for TApplication.OnLoadStateComplete
+ *
+ * @return void
+ * @since 3.1.5
+ * @see initializeCache
+ */
+ public function doInitializeCache()
+ {
+ $this->initializeCache();
+ }
+
+ /**
+ * Initialize TDbCache
+ *
+ * If {@link setAutoCreateCacheTable AutoCreateCacheTableName} is 'true' check existence of cache table
+ * and create table if does not exist.
+ *
+ * @return void
+ * @throws TConfigurationException if any error happens during creating database or cache table.
+ * @since 3.1.5
+ */
+ protected function initializeCache()
+ {
+ if($this->_cacheInitialized) return;
+
$db=$this->getDbConnection();
$db->setActive(true);
-
- $sql='DELETE FROM '.$this->_cacheTable.' WHERE expire<>0 AND expire<'.time();
try
{
- $db->createCommand($sql)->execute();
+ $key = 'TDbCache:' . $this->_cacheTable . ':created';
+ $this -> _createCheck = $this -> getApplication() -> getGlobalState($key, 0);
+
+ if($this->_autoCreate && !$this -> _createCheck) {
+ $sql='SELECT 1 FROM '.$this->_cacheTable.' WHERE 0';
+ $db->createCommand($sql)->queryScalar();
+
+ $this -> _createCheck = true;
+ $this -> getApplication() -> setGlobalState($key, time());
+ }
}
catch(Exception $e)
{
@@ -142,14 +210,69 @@ class TDbCache extends TCache else
$blob='BLOB';
- $sql='CREATE TABLE '.$this->_cacheTable." (itemkey CHAR(128) PRIMARY KEY, value $blob, expire INT)";
+ $sql='CREATE TABLE '.$this->_cacheTable." (itemkey CHAR(128), value $blob, expire INT)";
+ $db->createCommand($sql)->execute();
+
+ $sql='CREATE INDEX IX_itemkey ON ' . $this->_cacheTable . ' (itemkey)';
$db->createCommand($sql)->execute();
+
+ $sql='CREATE INDEX IX_expire ON ' . $this->_cacheTable . ' (expire)';
+ $db->createCommand($sql)->execute();
+
+ $this -> _createCheck = true;
+ $this -> getApplication() -> setGlobalState($key, time());
}
else
throw new TConfigurationException('db_cachetable_inexistent',$this->_cacheTable);
}
+ $this->_cacheInitialized = true;
+ }
- parent::init($config);
+ /**
+ * Flush expired values from cache depending on {@link setFlushInterval FlushInterval}
+ * @param boolean override {@link setFlushInterval FlushInterval} and force deletion of expired items
+ * @return void
+ * @since 3.1.5
+ */
+ public function flushCacheExpired($force=false)
+ {
+ $interval = $this -> getFlushInterval();
+ if(!$force && $interval === 0) return;
+
+ $key = 'TDbCache:' . $this->_cacheTable . ':flushed';
+ $now = time();
+ $next = $interval + (integer)$this -> getApplication() -> getGlobalState($key, 0);
+
+ if($force || $next <= $now)
+ {
+ if(!$this->_cacheInitialized) $this->initializeCache();
+ $sql='DELETE FROM '.$this->_cacheTable.' WHERE expire<>0 AND expire<'.$now;
+ $this->getDbConnection()->createCommand($sql)->execute();
+ $this -> getApplication() -> setGlobalState($key, $now);
+ }
+ }
+
+ /**
+ * @return integer Interval in sec expired items will be removed from cache. Default to 60
+ * @since 3.1.5
+ */
+ public function getFlushInterval()
+ {
+ return $this->_flushInterval;
+ }
+
+ /**
+ * Sets interval expired items will be removed from cache
+ *
+ * To disable automatic deletion of expired items,
+ * e.g. for external flushing via cron you can set value to '0'
+ *
+ * @param integer Interval in sec
+ * @since 3.1.5
+ */
+ public function setFlushInterval($value)
+ {
+ $this->_flushInterval = (integer) $value;
}
/**
@@ -284,9 +407,17 @@ class TDbCache extends TCache * Note, if {@link setAutoCreateCacheTable AutoCreateCacheTable} is false
* and you want to create the DB table manually by yourself,
* you need to make sure the DB table is of the following structure:
- * (itemkey CHAR(128) PRIMARY KEY, value BLOB, expire INT)
+ * <code>
+ * CREATE TABLE pradocache (itemkey CHAR(128), value BLOB, expire INT)
+ * CREATE INDEX IX_itemkey ON pradocache (itemkey)
+ * CREATE INDEX IX_expire ON pradocache (expire)
+ * </code>
+ *
* Note, some DBMS might not support BLOB type. In this case, replace 'BLOB' with a suitable
* binary data type (e.g. LONGBLOB in MySQL, BYTEA in PostgreSQL.)
+ *
+ * Important: Make sure that the indices are non-unique!
+ *
* @param string the name of the DB table to store cache content
* @see setAutoCreateCacheTable
*/
@@ -321,7 +452,8 @@ class TDbCache extends TCache */
protected function getValue($key)
{
- $sql='SELECT value FROM '.$this->_cacheTable.' WHERE itemkey=\''.$key.'\' AND (expire=0 OR expire>'.time().')';
+ if(!$this->_cacheInitialized) $this->initializeCache();
+ $sql='SELECT value FROM '.$this->_cacheTable.' WHERE itemkey=\''.$key.'\' AND (expire=0 OR expire>'.time().') ORDER BY expire DESC';
return $this->_db->createCommand($sql)->queryScalar();
}
@@ -351,6 +483,7 @@ class TDbCache extends TCache */
protected function addValue($key,$value,$expire)
{
+ if(!$this->_cacheInitialized) $this->initializeCache();
$expire=($expire<=0)?0:time()+$expire;
$sql="INSERT INTO {$this->_cacheTable} (itemkey,value,expire) VALUES(:key,:value,$expire)";
try
@@ -375,6 +508,7 @@ class TDbCache extends TCache */
protected function deleteValue($key)
{
+ if(!$this->_cacheInitialized) $this->initializeCache();
$command=$this->_db->createCommand("DELETE FROM {$this->_cacheTable} WHERE itemkey=:key");
$command->bindValue(':key',$key,PDO::PARAM_STR);
$command->execute();
@@ -387,9 +521,8 @@ class TDbCache extends TCache */
public function flush()
{
+ if(!$this->_cacheInitialized) $this->initializeCache();
$this->_db->createCommand("DELETE FROM {$this->_cacheTable}")->execute();
return true;
}
}
-
-?>
diff --git a/framework/Collections/TQueue.php b/framework/Collections/TQueue.php index d8b692d1..1eacfb0a 100644 --- a/framework/Collections/TQueue.php +++ b/framework/Collections/TQueue.php @@ -141,7 +141,7 @@ class TQueue extends TComponent implements IteratorAggregate,Countable public function enqueue($item)
{
++$this->_c;
- array_push($this->_d,$item);
+ $this->_d[] = $item;
}
/**
diff --git a/framework/Collections/TStack.php b/framework/Collections/TStack.php index 0d3890b4..3c8d5fc6 100644 --- a/framework/Collections/TStack.php +++ b/framework/Collections/TStack.php @@ -140,7 +140,7 @@ class TStack extends TComponent implements IteratorAggregate,Countable public function push($item)
{
++$this->_c;
- array_push($this->_d,$item);
+ $this->_d[] = $item;
}
/**
diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php b/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php index a352cb07..a3daf35c 100644 --- a/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php +++ b/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php @@ -84,7 +84,7 @@ abstract class TActiveRecordRelation $obj->collectForeignObjects($results);
}
else if($results instanceof TActiveRecordRelation)
- array_push($stack,$this); //call it later
+ $stack[] = $this; //call it later
return $results;
}
@@ -109,14 +109,17 @@ abstract class TActiveRecordRelation protected function findForeignKeys($from, $matchesRecord, $loose=false)
{
$gateway = $matchesRecord->getRecordGateway();
- $matchingTableName = $gateway->getRecordTableInfo($matchesRecord)->getTableName();
+ $recordTableInfo = $gateway->getRecordTableInfo($matchesRecord);
+ $matchingTableName = strtolower($recordTableInfo->getTableName());
+ $matchingFullTableName = strtolower($recordTableInfo->getTableFullName());
$tableInfo=$from;
if($from instanceof TActiveRecord)
$tableInfo = $gateway->getRecordTableInfo($from);
//find first non-empty FK
foreach($tableInfo->getForeignKeys() as $fkeys)
{
- if(strtolower($fkeys['table'])===strtolower($matchingTableName))
+ $fkTable = strtolower($fkeys['table']);
+ if($fkTable===$matchingTableName || $fkTable===$matchingFullTableName)
{
$hasFkField = !$loose && $this->getContext()->hasFkField();
$key = $hasFkField ? $this->getFkFields($fkeys['keys']) : $fkeys['keys'];
diff --git a/framework/Data/ActiveRecord/TActiveRecord.php b/framework/Data/ActiveRecord/TActiveRecord.php index fa134a9f..af171bbd 100644 --- a/framework/Data/ActiveRecord/TActiveRecord.php +++ b/framework/Data/ActiveRecord/TActiveRecord.php @@ -1,6 +1,6 @@ <?php /** - * TActiveRecord and TActiveRecordEventParameter class file. + * TActiveRecord, TActiveRecordEventParameter, TActiveRecordInvalidFinderResult class file. * * @author Wei Zhuo <weizhuo[at]gmail[dot]com> * @link http://www.pradosoft.com/ @@ -189,6 +189,15 @@ abstract class TActiveRecord extends TComponent */ protected $_connection; // use protected so that serialization is fine + + /** + * Defaults to 'null' + * + * @var TActiveRecordInvalidFinderResult + * @since 3.1.5 + */ + protected $_invalidFinderResult = null; // use protected so that serialization is fine + /** * Prevent __call() method creating __sleep() when serializing. */ @@ -527,9 +536,8 @@ abstract class TActiveRecord extends TComponent protected function populateObjects($reader) { $result=array(); - $class = get_class($this); foreach($reader as $data) - $result[] = self::createRecord($class, $data); + $result[] = $this->populateObject($data); return $result; } @@ -830,7 +838,12 @@ abstract class TActiveRecord extends TComponent else if($delete=strncasecmp($method,'deleteallby',11)===0) $condition = $method[11]==='_' ? substr($method,12) : substr($method,11); else - return null;//throw new TActiveRecordException('ar_invalid_finder_method',$method); + { + if($this->getInvalidFinderResult() == TActiveRecordInvalidFinderResult::Exception) + throw new TActiveRecordException('ar_invalid_finder_method',$method); + else + return null; + } $criteria = $this->getRecordGateway()->getCommand($this)->createCriteriaFromString($method, $condition, $args); if($delete) @@ -840,6 +853,34 @@ abstract class TActiveRecord extends TComponent } /** + * @return TActiveRecordInvalidFinderResult Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}'. + * @see TActiveRecordManager::getInvalidFinderResult + * @since 3.1.5 + */ + public function getInvalidFinderResult() + { + if($this->_invalidFinderResult !== null) + return $this->_invalidFinderResult; + + return self::getRecordManager()->getInvalidFinderResult(); + } + + /** + * Define the way an active record finder react if an invalid magic-finder invoked + * + * @param TActiveRecordInvalidFinderResult|null + * @see TActiveRecordManager::setInvalidFinderResult + * @since 3.1.5 + */ + public function setInvalidFinderResult($value) + { + if($value === null) + $this->_invalidFinderResult = null; + else + $this->_invalidFinderResult = TPropertyValue::ensureEnum($value, 'TActiveRecordInvalidFinderResult'); + } + + /** * Create a new TSqlCriteria object from a string $criteria. The $args * are additional parameters and are used in place of the $parameters * if $parameters is not an array and $args is an arrary. @@ -1022,3 +1063,25 @@ class TActiveRecordChangeEventParameter extends TEventParameter } } +/** + * TActiveRecordInvalidFinderResult class. + * TActiveRecordInvalidFinderResult defines the enumerable type for possible results + * if an invalid {@link TActiveRecord::__call magic-finder} invoked. + * + * The following enumerable values are defined: + * - Null: return null (default) + * - Exception: throws a TActiveRecordException + * + * @author Yves Berkholz <godzilla80@gmx.net> + * @version $Id$ + * @package System.Data.ActiveRecord + * @see TActiveRecordManager::setInvalidFinderResult + * @see TActiveRecordConfig::setInvalidFinderResult + * @see TActiveRecord::setInvalidFinderResult + * @since 3.1.5 + */ +class TActiveRecordInvalidFinderResult extends TEnumerable +{ + const Null = 'Null'; + const Exception = 'Exception'; +} diff --git a/framework/Data/ActiveRecord/TActiveRecordConfig.php b/framework/Data/ActiveRecord/TActiveRecordConfig.php index 63f05aef..51278fc9 100644 --- a/framework/Data/ActiveRecord/TActiveRecordConfig.php +++ b/framework/Data/ActiveRecord/TActiveRecordConfig.php @@ -11,6 +11,7 @@ */
Prado::using('System.Data.TDataSourceConfig');
+Prado::using('System.Data.ActiveRecord.TActiveRecordManager');
/**
* TActiveRecordConfig module configuration class.
@@ -74,17 +75,25 @@ class TActiveRecordConfig extends TDataSourceConfig private $_enableCache=false;
/**
+ * Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}'
+ *
+ * @var TActiveRecordInvalidFinderResult
+ * @since 3.1.5
+ */
+ private $_invalidFinderResult = TActiveRecordInvalidFinderResult::Null;
+
+ /**
* Initialize the active record manager.
* @param TXmlDocument xml configuration.
*/
public function init($xml)
{
parent::init($xml);
- Prado::using('System.Data.ActiveRecord.TActiveRecordManager');
$manager = TActiveRecordManager::getInstance();
if($this->getEnableCache())
$manager->setCache($this->getApplication()->getCache());
$manager->setDbConnection($this->getDbConnection());
+ $manager->setInvalidFinderResult($this->getInvalidFinderResult());
}
/**
@@ -103,5 +112,26 @@ class TActiveRecordConfig extends TDataSourceConfig {
return $this->_enableCache;
}
+
+ /**
+ * @return TActiveRecordInvalidFinderResult Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}'.
+ * @see setInvalidFinderResult
+ * @since 3.1.5
+ */
+ public function getInvalidFinderResult()
+ {
+ return $this->_invalidFinderResult;
+ }
+
+ /**
+ * Define the way an active record finder react if an invalid magic-finder invoked
+ *
+ * @param TActiveRecordInvalidFinderResult
+ * @see getInvalidFinderResult
+ * @since 3.1.5
+ */
+ public function setInvalidFinderResult($value)
+ {
+ $this->_invalidFinderResult = TPropertyValue::ensureEnum($value, 'TActiveRecordInvalidFinderResult');
+ }
}
- diff --git a/framework/Data/ActiveRecord/TActiveRecordGateway.php b/framework/Data/ActiveRecord/TActiveRecordGateway.php index 6cce9eb9..e588b976 100644 --- a/framework/Data/ActiveRecord/TActiveRecordGateway.php +++ b/framework/Data/ActiveRecord/TActiveRecordGateway.php @@ -349,7 +349,7 @@ class TActiveRecordGateway extends TComponent $tableInfo->getTableFullName(), $name); } if($column->getIsPrimaryKey()) - $primary[] = $value; + $primary[$name] = $value; else $values[$name] = $value; } diff --git a/framework/Data/ActiveRecord/TActiveRecordManager.php b/framework/Data/ActiveRecord/TActiveRecordManager.php index 9912e7ff..cba746ec 100644 --- a/framework/Data/ActiveRecord/TActiveRecordManager.php +++ b/framework/Data/ActiveRecord/TActiveRecordManager.php @@ -44,6 +44,14 @@ class TActiveRecordManager extends TComponent private $_cache;
/**
+ * Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}'
+ *
+ * @var TActiveRecordInvalidFinderResult
+ * @since 3.1.5
+ */
+ private $_invalidFinderResult = TActiveRecordInvalidFinderResult::Null;
+
+ /**
* @return ICache application cache.
*/
public function getCache()
@@ -106,6 +114,25 @@ class TActiveRecordManager extends TComponent {
return new TActiveRecordGateway($this);
}
-}
+ /**
+ * @return TActiveRecordInvalidFinderResult Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}'.
+ * @since 3.1.5
+ * @see setInvalidFinderResult
+ */
+ public function getInvalidFinderResult()
+ {
+ return $this->_invalidFinderResult;
+ }
+ /**
+ * Define the way an active record finder react if an invalid magic-finder invoked
+ * @param TActiveRecordInvalidFinderResult
+ * @since 3.1.5
+ * @see getInvalidFinderResult
+ */
+ public function setInvalidFinderResult($value)
+ {
+ $this->_invalidFinderResult = TPropertyValue::ensureEnum($value, 'TActiveRecordInvalidFinderResult');
+ }
+}
diff --git a/framework/Data/Common/IbmDb2/TIbmColumnMetaData.php b/framework/Data/Common/IbmDb2/TIbmColumnMetaData.php deleted file mode 100644 index 3b7001ab..00000000 --- a/framework/Data/Common/IbmDb2/TIbmColumnMetaData.php +++ /dev/null @@ -1,156 +0,0 @@ -<?php
-/**
- * TIbmColumnMetaData class file.
- *
- * @author Cesar Ramos <cramos[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2008 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id: TIbmColumnMetaData.php 1807 2007-03-31 06:42:15Z wei $
- * @package System.Data.ActiveRecord.Vendor
- */
-
-/**
- * TIbmColumnMetaData class.
- *
- * Column details for IBM DB2 database. Using php_pdo_ibm.dll extension.
- *
- * @author Cesar Ramos <cramos[at]gmail[dot]com>
- * @version $Id: TIbmColumnMetaData.php 1807 2007-03-31 06:42:15Z wei $
- * @package System.Data.ActiveRecord.Vendor
- * @since 3.1
- */
-class TIbmColumnMetaData extends TComponent
-{
- private $_name;
- private $_type;
- private $_length;
- private $_autoIncrement;
- private $_default;
- private $_notNull=true;
-
- private $_isPrimary=null;
-
- private $_property;
-
- /**
- * Initialize column meta data.
- *
- * @param string column name.
- * @param string column data type.
- * @param string column data length.
- * @param boolean column can not be null.
- * @param string serial name.
- * @param string default value.
- */
- public function __construct($name,$type,$length,$notNull,$autoIncrement,$default,$primary)
- {
- $this->_property=$name;
- $this->_name=$name;
- $this->_type=$type;
- $this->_length=$length;
- $this->_notNull=$notNull;
- $this->_autoIncrement=$autoIncrement;
- $this->_default=$default;
- $this->_isPrimary=$primary;
- }
-
- /**
- * @return string quoted column name.
- */
- public function getName()
- {
- return $this->_name;
- }
-
- /**
- * @return integer length.
- */
- public function getLength()
- {
- return $this->_length;
- }
-
- /**
- * @return string active record property name
- */
- public function getProperty()
- {
- return $this->_property;
- }
-
- /**
- * @return boolean true if column is a sequence, false otherwise.
- */
- public function hasSequence()
- {
- return $this->_autoIncrement;
- }
-
- /**
- * @return null no sequence name.
- */
- public function getSequenceName()
- {
- return null;
- }
-
- /**
- * @return boolean true if the column is a primary key, or part of a composite primary key.
- */
- public function getIsPrimaryKey()
- {
- return $this->_isPrimary;
- }
-
- /**
- * @return string column type
- */
- public function getType()
- {
- return $this->_type;
- }
-
-
- /**
- * @return boolean false if column can be null, true otherwise.
- */
- public function getNotNull()
- {
- return $this->_notNull;
- }
-
- /**
- * @return boolean true if column has default value, false otherwise.
- */
- public function hasDefault()
- {
- return $this->_default !== null;
- }
-
- /**
- * @return string default column value.
- */
- public function getDefaultValue()
- {
- return $this->_default;
- }
-
- /**
- * @return string PHP primative type derived from the column type.
- */
- public function getPHPType()
- {
- switch(strtolower($this->_type))
- {
- case 'smallint': case 'integer':
- return 'integer';
- case 'real': case 'float': case 'double': case 'decimal': case 'bigint':
- return 'float';
- default:
- return 'string';
- }
- }
-
-}
-
diff --git a/framework/Data/Common/IbmDb2/TIbmMetaData.php b/framework/Data/Common/IbmDb2/TIbmMetaData.php deleted file mode 100644 index 496bb12f..00000000 --- a/framework/Data/Common/IbmDb2/TIbmMetaData.php +++ /dev/null @@ -1,112 +0,0 @@ -<?php
-
-/**
- * TIbmMetaData class file.
- *
- * @author Cesar Ramos <cramos[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2008 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id: TIbmMetaData.php 1807 2007-03-31 06:42:15Z wei $
- * @package System.Data.ActiveRecord.Vendor
- */
-Prado::using('System.Data.ActiveRecord.Vendor.TDbMetaDataCommon');
-
-/**
- * TIbmMetaData class.
- *
- * Column details for IBM DB2 database. Using php_pdo_ibm.dll extension.
- *
- * Does not support LIMIT and OFFSET criterias.
- *
- * @author Cesar Ramos <cramos[at]gmail[dot]com>
- * @version $Id: TIbmMetaData.php 1807 2007-03-31 06:42:15Z wei $
- * @package System.Data.ActiveRecord.Vendor
- * @since 3.1
- */
-class TIbmMetaData extends TDbMetaDataCommon
-{
- /**
- * Build the SQL search string from the criteria object for IBM DB2 database.
- * @param TDbConnection database connection.
- * @param TActiveRecordCriteria search criteria.
- * @return string SQL search.
- */
- protected function getSqlFromCriteria($conn, $criteria)
- {
- if($criteria===null) return '';
- $sql = '';
- if(($condition = $criteria->getCondition())!==null)
- $sql .= ' WHERE '.$condition;
- $orders=array();
- foreach($criteria->getOrdersBy() as $by=>$ordering)
- $orders[] = $this->getOrdering($by, $ordering);
- if(count($orders) > 0)
- $sql .= ' ORDER BY '.implode(', ', $orders);
- //if(($limit = $criteria->getLimit())!==null)
- //{
- // $sql .= ' FETCH FIRST '.intval($limit).' ROWS ONLY';
- //}
- return strlen($sql) > 0 ? $sql : '';
- }
-
- /**
- * Lowercase the data keys, IBM DB2 returns uppercase column names
- * @param mixed record row
- * @return array record row
- */
- public function postQueryRow($row)
- {
- if(!is_array($row)) return $row;
- $result=array();
- foreach($row as $k=>$v)
- $result[strtolower($k)]=$v;
- return $result;
- }
-
- /**
- * Lowercase the data keys, IBM DB2 returns uppercase column names
- * @param mixed record row
- * @return array record row
- */
- public function postQuery($rows)
- {
- $data = array();
- foreach($rows as $k=>$v)
- $data[$k] = $this->postQueryRow($v);
- return $data;
- }
-
- public function getSearchRegExpCriteria($fields, $keywords)
- {
- if(strlen(trim($keywords)) == 0) return '';
- $words = array();
- preg_match_all('/([a-zA-Z0-9-+]+)/', $keywords, $words);
- $result = array();
- foreach($fields as $field)
- {
- $column = $this->getColumn($field);
- if($this->isSearchableColumn($column))
- $result[] = $this->getLikeCriteriaStr($column->getName(), $words[0]);
- }
- $a = '('.implode(' OR ', $result).')';
- error_log($a);
- return '('.implode(' OR ', $result).')';
- }
-
- protected function isSearchableColumn($column)
- {
- $type = strtolower($column->getType());
- return $type === 'char' || $type === 'varchar';
- }
-
- protected function getLikeCriteriaStr($column, $words)
- {
- $result=array();
- foreach($words as $word)
- $result[] = "{$column} LIKE '%{$word}%'";
- return '('.implode(' AND ', $result).')';
- }
-
-
-}
diff --git a/framework/Data/Common/IbmDb2/TIbmMetaDataInspector.php b/framework/Data/Common/IbmDb2/TIbmMetaDataInspector.php deleted file mode 100644 index a37fad6e..00000000 --- a/framework/Data/Common/IbmDb2/TIbmMetaDataInspector.php +++ /dev/null @@ -1,112 +0,0 @@ -<?php
-/**
- * TIbmMetaDataInspector class file.
- *
- * @author Cesar Ramos <cramos[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2008 PradoSoft - * @license http://www.pradosoft.com/license/
- * @version $Id: TIbmMetaDataInspector.php 1807 2007-03-31 06:42:15Z wei $
- * @package System.Data.ActiveRecord.Vendor
- */
-Prado::using('System.Data.ActiveRecord.Vendor.TDbMetaDataInspector');
-Prado::using('System.Data.ActiveRecord.Vendor.TIbmColumnMetaData');
-Prado::using('System.Data.ActiveRecord.Vendor.TIbmMetaData');
-
-/**
- * TIbmMetaDataInspector class.
- *
- * Column details for IBM DB2 database. Using php_pdo_ibm.dll extension.
- *
- * @author Cesar Ramos <cramos[at]gmail[dot]com>
- * @version $Id: TIbmMetaDataInspector.php 1807 2007-03-31 06:42:15Z wei $
- * @package System.Data.ActiveRecord.Vendor
- * @since 3.1
- */
-class TIbmMetaDataInspector extends TDbMetaDataInspector
-{
- private $_schema;
-
- /**
- * @param string default schema.
- */
- public function setSchema($schema)
- {
- $this->_schema=$schema;
- }
-
- /**
- * @return string default schema.
- */
- public function getSchema()
- {
- return $this->_schema;
- }
- /**
- * Get the column definitions for given table.
- * @param string table name.
- * @return array column name value pairs of column meta data.
- */
- protected function getColumnDefinitions($table)
- {
- if(count($parts= explode('.', $table)) > 1)
- {
- $tablename = $parts[1];
- $schema = $parts[0];
- }
- else
- {
- $tablename = $parts[0];
- $schema = $this->getSchema();
- }
- $sql="SELECT * FROM SYSCAT.COLUMNS WHERE TABNAME='".strtoupper($tablename)."'";
- if ($schema)
- $sql=$sql." AND TABSCHEMA='".strtoupper($schema)."'";
-
- $conn = $this->getDbConnection();
- $conn->setActive(true);
- $command = $conn->createCommand($sql);
- $command->prepare();
- $result=$command->query($sql);
- foreach ($result as $col)
- $cols[strtolower($col['COLNAME'])] = $this->getColumnMetaData($col);
- return $cols;
- }
-
- protected function getColumnMetaData($col)
- {
- $name = strtolower($col['COLNAME']);
- $type = $col['TYPENAME'];
- $length = $col['LENGTH'];
- $notNull = $col['NULLS']==='N'?1:0;
- $autoIncrement=$col['IDENTITY']==='Y'?1:0;
- $default = $col['DEFAULT'];
- $primaryKey = $col['KEYSEQ']?1:0;
- return new TIbmColumnMetaData($name,$type,$length,$notNull,$autoIncrement,$default,$primaryKey);
- }
-
- /**
- * Not implemented, IBM does not always have foreign key constraints.
- */
- protected function getConstraintKeys($table)
- {
- return array('primary'=>array(), 'foreign'=>array());
- }
-
- /**
- * Create a new instance of meta data.
- * @param string table name
- * @param array column meta data
- * @param array primary key meta data
- * @param array foreign key meta data.
- * @return TDbMetaData table meta data.
- */
- protected function createMetaData($table, $columns, $primary, $foreign)
- {
- $pks = array();
- foreach($columns as $name=>$column)
- if($column->getIsPrimaryKey())
- $pks[] = $name;
- return new TIbmMetaData($table,$columns,$pks);
- }
-}
diff --git a/framework/Data/Common/Mysql/TMysqlMetaData.php b/framework/Data/Common/Mysql/TMysqlMetaData.php index 75f7a7cf..ae552cf3 100644 --- a/framework/Data/Common/Mysql/TMysqlMetaData.php +++ b/framework/Data/Common/Mysql/TMysqlMetaData.php @@ -310,7 +310,7 @@ EOD; $sql = "SHOW CREATE TABLE `{$tableName}`";
$command = $this->getDbConnection()->createCommand($sql);
$result = $command->queryRow();
- return $result['Create Table'];
+ return isset($result['Create Table']) ? $result['Create Table'] : (isset($result['Create View']) ? $result['Create View'] : '');
}
/**
diff --git a/framework/Data/Common/Oracle/TOracleCommandBuilder.php b/framework/Data/Common/Oracle/TOracleCommandBuilder.php index 8c60751a..a22c0f08 100644 --- a/framework/Data/Common/Oracle/TOracleCommandBuilder.php +++ b/framework/Data/Common/Oracle/TOracleCommandBuilder.php @@ -86,20 +86,14 @@ class TOracleCommandBuilder extends TDbCommandBuilder { $niniDoSelect = strpos($sql, 'SELECT') + 6; $nfimDoSelect = (strpos($sql, 'FROM') !== false ? strpos($sql, 'FROM') : $nfimDaSQL); - $niniDoWhere = strpos($sql, 'WHERE') + 5; - - $WhereConstraint = substr($sql, $niniDoWhere, $nfimDoWhere - $niniDoWhere); - - $WhereInSubSelect = ""; - if (trim($WhereConstraint) !== "") { - $WhereInSubSelect = "WHERE " . $WhereConstraint; - } + $WhereInSubSelect=""; + if(strpos($sql, 'WHERE')!==false) + $WhereInSubSelect = "WHERE " .substr($sql, strpos($sql, 'WHERE')+5, $nfimDoWhere - $niniDoWhere); $sORDERBY = ''; if (stripos($sql, 'ORDER') !== false) { $p = stripos($sql, 'ORDER'); - $sORDERBY = substr($sql, $p +8, 10000); - + $sORDERBY = substr($sql, $p +8); } $fields = substr($sql, 0, $nfimDoSelect); @@ -142,23 +136,18 @@ class TOracleCommandBuilder extends TDbCommandBuilder { ") WHERE {$pradoNUMLIN} >= {$offset} "; ************************* */ - $toReg = $offset + $limit -1; + $offset=(int)$offset; + $toReg = $offset + $limit ; $fullTableName = $this->getTableInfo()->getTableFullName(); - if (empty ($sORDERBY)) { - $newSql = " SELECT $fields FROM " . - "( " . - " SELECT ROW_NUMBER() OVER ( ORDER BY ROWNUM ) as {$pradoNUMLIN} {$aliasedFields} " . - " FROM {$fullTableName} {$fieldsALIAS}" . - ") nn " . - " WHERE nn.{$pradoNUMLIN} >= {$offset} AND nn.{$pradoNUMLIN} <= {$toReg} "; - } else { - $newSql = " SELECT $fields FROM " . - "( " . - " SELECT ROW_NUMBER() OVER ( ORDER BY {$sORDERBY} ) as {$pradoNUMLIN} {$aliasedFields} " . - " FROM {$fullTableName} {$fieldsALIAS} $WhereInSubSelect" . - ") nn " . - " WHERE nn.{$pradoNUMLIN} >= {$offset} AND nn.{$pradoNUMLIN} <= {$toReg} "; - } + if (empty ($sORDERBY)) + $sORDERBY="ROWNUM"; + + $newSql = " SELECT $fields FROM " . + "( " . + " SELECT ROW_NUMBER() OVER ( ORDER BY {$sORDERBY} ) -1 as {$pradoNUMLIN} {$aliasedFields} " . + " FROM {$fullTableName} {$fieldsALIAS} $WhereInSubSelect" . + ") nn " . + " WHERE nn.{$pradoNUMLIN} >= {$offset} AND nn.{$pradoNUMLIN} < {$toReg} "; //echo $newSql."\n<br>\n"; return $newSql; } diff --git a/framework/Data/Common/Oracle/TOracleMetaData.php b/framework/Data/Common/Oracle/TOracleMetaData.php index bb8e9b7b..5fdf1d5c 100644 --- a/framework/Data/Common/Oracle/TOracleMetaData.php +++ b/framework/Data/Common/Oracle/TOracleMetaData.php @@ -10,7 +10,10 @@ * @package System.Data.Common.Oracle */ - +/** + * Load the base TDbMetaData class. + */ +Prado::using('System.Data.Common.TDbMetaData'); Prado::using('System.Data.Common.Oracle.TOracleTableInfo'); Prado::using('System.Data.Common.Oracle.TOracleTableColumn'); @@ -22,28 +25,11 @@ Prado::using('System.Data.Common.Oracle.TOracleTableColumn'); * @package System.Data.Common.Oracle * @since 3.1 */ -class TOracleMetaData extends TComponent +class TOracleMetaData extends TDbMetaData { - private $_tableInfoCache=array(); - private $_connection; private $_defaultSchema = 'system'; - /** - * @param TDbConnection database connection. - */ - public function __construct($conn) - { - $this->_connection=$conn; - } - - /** - * @return TDbConnection database connection. - */ - public function getDbConnection() - { - return $this->_connection; - } - + /** * @return string TDbTableInfo class name. */ @@ -69,23 +55,6 @@ class TOracleMetaData extends TComponent } /** - * Obtains table meta data information for the current connection and given table name. - * @param string table or view name - * @return TDbTableInfo table information. - */ - public function getTableInfo($tableName=null) - { - $key = $tableName===null?$this->getDbConnection()->getConnectionString():$tableName; - if(!isset($this->_tableInfoCache[$key])) - { - $class = $this->getTableInfoClass(); - $tableInfo = $tableName===null ? new $class : $this->createTableInfo($tableName); - $this->_tableInfoCache[$key] = $tableInfo; - } - return $this->_tableInfoCache[$key]; - } - - /** * @param string table name with optional schema name prefix, uses default schema name prefix is not provided. * @return array tuple as ($schemaName,$tableName) */ diff --git a/framework/Data/Common/Pgsql/TPgsqlMetaData.php b/framework/Data/Common/Pgsql/TPgsqlMetaData.php index a2243531..3ab872a5 100644 --- a/framework/Data/Common/Pgsql/TPgsqlMetaData.php +++ b/framework/Data/Common/Pgsql/TPgsqlMetaData.php @@ -330,7 +330,7 @@ EOD; */
protected function getPrimaryKeys($tableName, $schemaName, $columnIndex)
{
- $index = join(', ', split(' ', $columnIndex));
+ $index = join(', ', explode(' ', $columnIndex));
$sql =
<<<EOD
SELECT attnum, attname FROM pg_catalog.pg_attribute WHERE
@@ -344,7 +344,7 @@ EOD; $command = $this->getDbConnection()->createCommand($sql);
$command->bindValue(':table', $tableName);
$command->bindValue(':schema', $schemaName);
-// $command->bindValue(':columnIndex', join(', ', split(' ', $columnIndex)));
+// $command->bindValue(':columnIndex', join(', ', explode(' ', $columnIndex)));
$primary = array();
foreach($command->query() as $row)
{
diff --git a/framework/Data/Common/Pgsql/TPgsqlTableInfo.php b/framework/Data/Common/Pgsql/TPgsqlTableInfo.php index 2447c141..1a634c19 100644 --- a/framework/Data/Common/Pgsql/TPgsqlTableInfo.php +++ b/framework/Data/Common/Pgsql/TPgsqlTableInfo.php @@ -42,7 +42,7 @@ class TPgsqlTableInfo extends TDbTableInfo if(($schema=$this->getSchemaName())!==null)
return $schema.'.'.$this->getTableName();
else
- $this->getTableName();
+ return $this->getTableName();
}
/**
diff --git a/framework/Data/DataGateway/TDataGatewayCommand.php b/framework/Data/DataGateway/TDataGatewayCommand.php index 35e4dcbe..7425e6c4 100644 --- a/framework/Data/DataGateway/TDataGatewayCommand.php +++ b/framework/Data/DataGateway/TDataGatewayCommand.php @@ -276,7 +276,7 @@ class TDataGatewayCommand extends TComponent {
$column = $this->getTableInfo()->getColumn($key)->getColumnName();
$criteria[] = $column.' = :'.$key;
- $bindings[$key] = $values[$i++];
+ $bindings[$key] = isset($values[$key])?$values[$key]:$values[$i++];
}
return array(implode(' AND ', $criteria), $bindings);
}
diff --git a/framework/Data/SqlMap/Configuration/TParameterMap.php b/framework/Data/SqlMap/Configuration/TParameterMap.php index f4fbbe1c..4b5ee144 100644 --- a/framework/Data/SqlMap/Configuration/TParameterMap.php +++ b/framework/Data/SqlMap/Configuration/TParameterMap.php @@ -4,7 +4,7 @@ *
* @author Wei Zhuo <weizhuo[at]gmail[dot]com>
* @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2008 PradoSoft + * @copyright Copyright © 2005-2008 PradoSoft
* @license http://www.pradosoft.com/license/
* @version $Id$
* @package System.Data.SqlMap.Configuration
@@ -140,12 +140,12 @@ class TParameterMap extends TComponent {
$value = $this->getObjectValue($parameterValue,$property);
- if(!is_null($handler=$this->createTypeHandler($property, $registry)))
+ if(($handler=$this->createTypeHandler($property, $registry))!==null)
$value = $handler->getParameter($value);
$value = $this->nullifyDefaultValue($property,$value);
- if(!is_null($type = $property->getType()))
+ if(($type = $property->getType())!==null)
$value = $registry->convertToType($type, $value);
return $value;
@@ -197,7 +197,7 @@ class TParameterMap extends TComponent */
protected function nullifyDefaultValue($property,$value)
{
- if(!is_null($nullValue = $property->getNullValue()))
+ if(($nullValue = $property->getNullValue())!==null)
{
if($nullValue === $value)
$value = null;
diff --git a/framework/Data/SqlMap/Configuration/TResultMap.php b/framework/Data/SqlMap/Configuration/TResultMap.php index d59d9522..e85dc1aa 100644 --- a/framework/Data/SqlMap/Configuration/TResultMap.php +++ b/framework/Data/SqlMap/Configuration/TResultMap.php @@ -4,7 +4,7 @@ *
* @author Wei Zhuo <weizhuo[at]gmail[dot]com>
* @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2008 PradoSoft + * @copyright Copyright © 2005-2008 PradoSoft
* @license http://www.pradosoft.com/license/
* @version $Id$
* @package System.Data.SqlMap.Configuration
@@ -162,7 +162,7 @@ class TResultMap extends TComponent $handler = $registry->getTypeHandler($this->getClass());
try
{
- if(!is_null($handler))
+ if($handler!==null)
return $handler->createNewInstance();
else
return $registry->createInstanceOf($this->getClass());
@@ -184,12 +184,12 @@ class TResultMap extends TComponent public function resolveSubMap($registry,$row)
{
$subMap = $this;
- if(!is_null($disc = $this->getDiscriminator()))
+ if(($disc = $this->getDiscriminator())!==null)
{
$value = $disc->getMapping()->getPropertyValue($registry,$row);
$subMap = $disc->getSubMap((string)$value);
- if(is_null($subMap))
+ if($subMap===null)
$subMap = $this;
else if($subMap !== $this)
$subMap = $subMap->resolveSubMap($registry,$row);
diff --git a/framework/Data/SqlMap/Configuration/TResultProperty.php b/framework/Data/SqlMap/Configuration/TResultProperty.php index 8e20d5e4..7316ef0b 100644 --- a/framework/Data/SqlMap/Configuration/TResultProperty.php +++ b/framework/Data/SqlMap/Configuration/TResultProperty.php @@ -4,7 +4,7 @@ *
* @author Wei Zhuo <weizhuo[at]gmail[dot]com>
* @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2008 PradoSoft + * @copyright Copyright © 2005-2008 PradoSoft
* @license http://www.pradosoft.com/license/
* @version $Id$
* @package System.Data.SqlMap.Configuration
@@ -242,7 +242,7 @@ class TResultProperty extends TComponent $value = $this->getTypedValue($registry,$row[$index]);
else if(isset($row[$name]))
$value = $this->getTypedValue($registry,$row[$name]);
- if(is_null($value) && !is_null($this->getNullValue()))
+ if(($value===null) && ($this->getNullValue()!==null))
$value = $this->getTypedValue($registry,$this->getNullValue());
return $value;
}
@@ -302,7 +302,7 @@ class TResultProperty extends TComponent */
public function instanceOfListType($target)
{
- if(is_null($this->getType()))
+ if($this->getType()===null)
return TPropertyAccess::get($target,$this->getProperty()) instanceof TList;
return $this->getPropertyValueType() == self::LIST_TYPE;
}
@@ -315,7 +315,7 @@ class TResultProperty extends TComponent */
public function instanceOfArrayType($target)
{
- if(is_null($this->getType()))
+ if($this->getType()===null)
{
$prop = TPropertyAccess::get($target,$this->getProperty());
if(is_object($prop))
diff --git a/framework/Data/SqlMap/Configuration/TSqlMapCacheModel.php b/framework/Data/SqlMap/Configuration/TSqlMapCacheModel.php index d7984dc4..d85148eb 100644 --- a/framework/Data/SqlMap/Configuration/TSqlMapCacheModel.php +++ b/framework/Data/SqlMap/Configuration/TSqlMapCacheModel.php @@ -4,7 +4,7 @@ *
* @author Wei Zhuo <weizhuo[at]gmail[dot]com>
* @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2008 PradoSoft + * @copyright Copyright © 2005-2008 PradoSoft
* @license http://www.pradosoft.com/license/
* @version $Id$
* @package System.Data.SqlMap.Configuration
@@ -38,8 +38,9 @@ class TSqlMapCacheModel extends TComponent private $_hits = 0;
private $_requests = 0;
private $_id;
- private $_implementation='basic';
+ private $_implementation=TSqlMapCacheTypes::Basic;
private $_properties = array();
+ private $_flushInterval = 0;
/**
* @return string unique cache model identifier.
@@ -74,12 +75,28 @@ class TSqlMapCacheModel extends TComponent }
/**
+ * @param integer the number of seconds in which the cached value will expire. 0 means never expire.
+ */
+ public function setFlushInterval($value)
+ {
+ $this->_flushInterval=TPropertyValue::ensureInteger($value);
+ }
+
+ /**
+ * @return integer cache duration.
+ */
+ public function getFlushInterval()
+ {
+ return $this->_flushInterval;
+ }
+
+ /**
* Initialize the cache implementation, sets the actual cache contain if supplied.
* @param ISqLMapCache cache implementation instance.
*/
public function initialize($cache=null)
{
- if(is_null($cache))
+ if($cache===null)
$this->_cache= Prado::createComponent($this->getImplementationClass());
else
$this->_cache=$cache;
@@ -127,7 +144,7 @@ class TSqlMapCacheModel extends TComponent //if flush ?
$value = $this->_cache->get($key);
$this->_requests++;
- if(!is_null($value))
+ if($value!==null)
$this->_hits++;
return $value;
}
@@ -141,8 +158,8 @@ class TSqlMapCacheModel extends TComponent if($key instanceof TSqlMapCacheKey)
$key = $key->getHash();
- if(!is_null($value))
- $this->_cache->set($key, $value);
+ if($value!==null)
+ $this->_cache->set($key, $value, $this->_flushInterval);
}
/**
diff --git a/framework/Data/SqlMap/Configuration/TSqlMapStatement.php b/framework/Data/SqlMap/Configuration/TSqlMapStatement.php index 3afcc75f..7e1783a3 100644 --- a/framework/Data/SqlMap/Configuration/TSqlMapStatement.php +++ b/framework/Data/SqlMap/Configuration/TSqlMapStatement.php @@ -5,7 +5,7 @@ *
* @author Wei Zhuo <weizhuo[at]gmail[dot]com>
* @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2008 PradoSoft + * @copyright Copyright © 2005-2008 PradoSoft
* @license http://www.pradosoft.com/license/
* @version $Id$
* @package System.Data.SqlMap.Configuration
@@ -275,7 +275,7 @@ class TSqlMapStatement extends TComponent protected function createInstanceOf($registry,$type,$row=null)
{
$handler = $registry->getTypeHandler($type);
- if(!is_null($handler))
+ if($handler!==null)
return $handler->createNewInstance($row);
else
return $registry->createInstanceOf($type);
diff --git a/framework/Data/SqlMap/Configuration/TSqlMapXmlConfiguration.php b/framework/Data/SqlMap/Configuration/TSqlMapXmlConfiguration.php index f2d13966..462b356f 100644 --- a/framework/Data/SqlMap/Configuration/TSqlMapXmlConfiguration.php +++ b/framework/Data/SqlMap/Configuration/TSqlMapXmlConfiguration.php @@ -4,14 +4,14 @@ *
* @author Wei Zhuo <weizhuo[at]gmail[dot]com>
* @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2008 PradoSoft + * @copyright Copyright © 2005-2008 PradoSoft
* @license http://www.pradosoft.com/license/
* @version $Id$
* @package System.Data.SqlMap.Configuration
*/
Prado::using('System.Data.SqlMap.Configuration.TSqlMapStatement');
- +
/**
* TSqlMapXmlConfig class file.
*
@@ -89,6 +89,9 @@ abstract class TSqlMapXmlConfigBuilder */
protected function loadXmlDocument($filename,TSqlMapXmlConfiguration $config)
{
+ if( strpos($filename, '${') !== false)
+ $filename = $config->replaceProperties($filename);
+
if(!is_file($filename))
throw new TSqlMapConfigurationException(
'sqlmap_unable_to_find_config', $filename);
@@ -228,6 +231,9 @@ class TSqlMapXmlConfiguration extends TSqlMapXmlConfigBuilder {
if(strlen($resource = (string)$node['resource']) > 0)
{
+ if( strpos($resource, '${') !== false)
+ $resource = $this->replaceProperties($resource);
+
$mapping = new TSqlMapXmlMappingConfiguration($this);
$filename = $this->getAbsoluteFilePath($this->_configFile, $resource);
$mapping->configure($filename);
@@ -255,7 +261,7 @@ class TSqlMapXmlConfiguration extends TSqlMapXmlConfigBuilder $resultMap, $this->_configFile, $entry->getID());
}
}
- if(!is_null($entry->getDiscriminator()))
+ if($entry->getDiscriminator()!==null)
$entry->getDiscriminator()->initialize($this->_manager);
}
}
@@ -309,16 +315,16 @@ class TSqlMapXmlMappingConfiguration extends TSqlMapXmlConfigBuilder private $_FlushOnExecuteStatements=array();
- /** - * Regular expressions for escaping simple/inline parameter symbols - */ - const SIMPLE_MARK='$'; - const INLINE_SYMBOL='#'; - const ESCAPED_SIMPLE_MARK_REGEXP='/\$\$/'; - const ESCAPED_INLINE_SYMBOL_REGEXP='/\#\#/'; - const SIMPLE_PLACEHOLDER='`!!`'; - const INLINE_PLACEHOLDER='`!!!`'; - + /**
+ * Regular expressions for escaping simple/inline parameter symbols
+ */
+ const SIMPLE_MARK='$';
+ const INLINE_SYMBOL='#';
+ const ESCAPED_SIMPLE_MARK_REGEXP='/\$\$/';
+ const ESCAPED_INLINE_SYMBOL_REGEXP='/\#\#/';
+ const SIMPLE_PLACEHOLDER='`!!`';
+ const INLINE_PLACEHOLDER='`!!!`';
+
/**
* @param TSqlMapXmlConfiguration parent xml configuration.
*/
@@ -343,6 +349,15 @@ class TSqlMapXmlMappingConfiguration extends TSqlMapXmlConfigBuilder $document = $this->loadXmlDocument($filename,$this->_xmlConfig);
$this->_document=$document;
+ static $bCacheDependencies;
+ if($bCacheDependencies === null)
+ $bCacheDependencies = Prado::getApplication()->getMode() !== TApplicationMode::Performance;
+
+ if($bCacheDependencies)
+ $this->_manager->getCacheDependencies()
+ ->getDependencies()
+ ->add(new TFileCacheDependency($filename));
+
foreach($document->xpath('//resultMap') as $node)
$this->loadResultMap($node);
@@ -434,7 +449,7 @@ class TSqlMapXmlMappingConfiguration extends TSqlMapXmlConfigBuilder foreach($node->xpath('subMap') as $subMapNode)
{
- if(is_null($discriminator))
+ if($discriminator===null)
throw new TSqlMapConfigurationException(
'sqlmap_undefined_discriminator', $node, $this->_configFile,$subMapNode);
$subMap = new TSubMap;
@@ -442,7 +457,7 @@ class TSqlMapXmlMappingConfiguration extends TSqlMapXmlConfigBuilder $discriminator->addSubMap($subMap);
}
- if(!is_null($discriminator))
+ if($discriminator!==null)
$resultMap->setDiscriminator($discriminator);
return $resultMap;
@@ -542,7 +557,7 @@ class TSqlMapXmlMappingConfiguration extends TSqlMapXmlConfigBuilder $scope['file'] = $this->_configFile;
$scope['node'] = $node;
- $sqlStatement=preg_replace(self::ESCAPED_INLINE_SYMBOL_REGEXP,self::INLINE_PLACEHOLDER,$sqlStatement); + $sqlStatement=preg_replace(self::ESCAPED_INLINE_SYMBOL_REGEXP,self::INLINE_PLACEHOLDER,$sqlStatement);
if($statement->parameterMap() === null)
{
// Build a Parametermap with the inline parameters.
@@ -559,7 +574,7 @@ class TSqlMapXmlMappingConfiguration extends TSqlMapXmlConfigBuilder }
$sqlStatement = $sqlText['sql'];
}
- $sqlStatement=preg_replace('/'.self::INLINE_PLACEHOLDER.'/',self::INLINE_SYMBOL,$sqlStatement); + $sqlStatement=preg_replace('/'.self::INLINE_PLACEHOLDER.'/',self::INLINE_SYMBOL,$sqlStatement);
$this->prepareSql($statement, $sqlStatement, $node);
}
@@ -574,7 +589,7 @@ class TSqlMapXmlMappingConfiguration extends TSqlMapXmlConfigBuilder protected function prepareSql($statement,$sqlStatement, $node)
{
$simpleDynamic = new TSimpleDynamicParser;
- $sqlStatement=preg_replace(self::ESCAPED_SIMPLE_MARK_REGEXP,self::SIMPLE_PLACEHOLDER,$sqlStatement); + $sqlStatement=preg_replace(self::ESCAPED_SIMPLE_MARK_REGEXP,self::SIMPLE_PLACEHOLDER,$sqlStatement);
$dynamics = $simpleDynamic->parse($sqlStatement);
if(count($dynamics['parameters']) > 0)
{
@@ -583,7 +598,7 @@ class TSqlMapXmlMappingConfiguration extends TSqlMapXmlConfigBuilder }
else
$sql = new TStaticSql();
- $sqlStatement=preg_replace('/'.self::SIMPLE_PLACEHOLDER.'/',self::SIMPLE_MARK,$sqlStatement); + $sqlStatement=preg_replace('/'.self::SIMPLE_PLACEHOLDER.'/',self::SIMPLE_MARK,$sqlStatement);
$sql->buildPreparedStatement($statement, $sqlStatement);
$statement->setSqlText($sql);
}
@@ -697,6 +712,8 @@ class TSqlMapXmlMappingConfiguration extends TSqlMapXmlConfigBuilder }
$cache = Prado::createComponent($cacheModel->getImplementationClass());
$this->setObjectPropFromNode($cache,$node,$properties);
+ $this->loadFlushInterval($cacheModel,$node);
+
$cacheModel->initialize($cache);
$this->_manager->addCacheModel($cacheModel);
foreach($node->xpath('flushOnExecute') as $flush)
@@ -704,6 +721,40 @@ class TSqlMapXmlMappingConfiguration extends TSqlMapXmlConfigBuilder }
/**
+ * Load the flush interval
+ * @param TSqlMapCacheModel cache model
+ * @param SimpleXmlElement cache node
+ */
+ protected function loadFlushInterval($cacheModel, $node)
+ {
+ $flushInterval = $node->xpath('flushInterval');
+ if($flushInterval === null || count($flushInterval) === 0) return;
+ $duration = 0;
+ foreach($flushInterval[0]->attributes() as $name=>$value)
+ {
+ switch(strToLower($name))
+ {
+ case 'seconds':
+ $duration += (integer)$value;
+ break;
+ case 'minutes':
+ $duration += 60 * (integer)$value;
+ break;
+ case 'hours':
+ $duration += 3600 * (integer)$value;
+ break;
+ case 'days':
+ $duration += 86400 * (integer)$value;
+ break;
+ case 'duration':
+ $duration = (integer)$value;
+ break 2; // switch, foreach
+ }
+ }
+ $cacheModel->setFlushInterval($duration);
+ }
+
+ /**
* Load the flush on cache properties.
* @param TSqlMapCacheModel cache model
* @param SimpleXmlElement parent node.
@@ -737,4 +788,4 @@ class TSqlMapXmlMappingConfiguration extends TSqlMapXmlConfigBuilder }
}
}
- +
diff --git a/framework/Data/SqlMap/DataMapper/TPropertyAccess.php b/framework/Data/SqlMap/DataMapper/TPropertyAccess.php index a27cb50f..5dbd00eb 100644 --- a/framework/Data/SqlMap/DataMapper/TPropertyAccess.php +++ b/framework/Data/SqlMap/DataMapper/TPropertyAccess.php @@ -71,6 +71,8 @@ class TPropertyAccess $object = $object->{$getter}();
else if(in_array($prop, array_keys(get_object_vars($object))))
$object = $object->{$prop};
+ elseif(method_exists($object, '__get') && is_callable(array($object, '__get')))
+ $object = $object->{$prop};
else
throw new TInvalidPropertyException('sqlmap_invalid_property',$path);
}
@@ -106,6 +108,8 @@ class TPropertyAccess $object = $object->{$getter}();
else if(in_array($prop, array_keys(get_object_vars($object))))
$object = $object->{$prop};
+ elseif(method_exists($object, '__get') && is_callable(array($object, '__get')))
+ $object = $object->{$prop};
else
return false;
}
diff --git a/framework/Data/SqlMap/DataMapper/TSqlMapPagedList.php b/framework/Data/SqlMap/DataMapper/TSqlMapPagedList.php index 86171c1e..68b0c638 100644 --- a/framework/Data/SqlMap/DataMapper/TSqlMapPagedList.php +++ b/framework/Data/SqlMap/DataMapper/TSqlMapPagedList.php @@ -4,7 +4,7 @@ *
* @author Wei Zhuo <weizhuo[at]gmail[dot]com>
* @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2008 PradoSoft + * @copyright Copyright © 2005-2008 PradoSoft
* @license http://www.pradosoft.com/license/
* @version $Id$
* @package System.Data.SqlMap
@@ -178,7 +178,7 @@ class TSqlMapPagedList extends TPagedList */
public function getIsNextPageAvailable()
{
- return !is_null($this->_nextPageList);
+ return $this->_nextPageList!==null;
}
/**
@@ -186,7 +186,7 @@ class TSqlMapPagedList extends TPagedList */
public function getIsPreviousPageAvailable()
{
- return !is_null($this->_prevPageList);
+ return $this->_prevPageList!==null;
}
/**
@@ -194,7 +194,7 @@ class TSqlMapPagedList extends TPagedList */
public function getIsLastPage()
{
- return is_null($this->_nextPageList) || $this->_nextPageList->getCount() < 1;
+ return ($this->_nextPageList===null) || $this->_nextPageList->getCount() < 1;
}
/**
diff --git a/framework/Data/SqlMap/Statements/TCachingStatement.php b/framework/Data/SqlMap/Statements/TCachingStatement.php index c8a748c1..fcaeb974 100644 --- a/framework/Data/SqlMap/Statements/TCachingStatement.php +++ b/framework/Data/SqlMap/Statements/TCachingStatement.php @@ -4,7 +4,7 @@ *
* @author Wei Zhuo <weizhuo[at]gmail[dot]com>
* @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2008 PradoSoft + * @copyright Copyright © 2005-2008 PradoSoft
* @license http://www.pradosoft.com/license/
* @version $Id$
* @package System.Data.SqlMap.Statements
@@ -47,7 +47,7 @@ class TCachingStatement implements IMappedStatement $sql = $this->createCommand($connection, $parameter, $skip, $max);
$key = $this->getCacheKey(array(clone($sql), $keyProperty, $valueProperty,$skip, $max));
$map = $this->getStatement()->getCache()->get($key);
- if(is_null($map))
+ if($map===null)
{
$map = $this->_mappedStatement->runQueryForMap(
$connection, $parameter, $sql, $keyProperty, $valueProperty, $delegate);
@@ -71,7 +71,7 @@ class TCachingStatement implements IMappedStatement $sql = $this->createCommand($connection, $parameter, $skip, $max);
$key = $this->getCacheKey(array(clone($sql), $parameter, $skip, $max));
$list = $this->getStatement()->getCache()->get($key);
- if(is_null($list))
+ if($list===null)
{
$list = $this->_mappedStatement->runQueryForList(
$connection, $parameter, $sql, $result, $delegate);
@@ -85,7 +85,7 @@ class TCachingStatement implements IMappedStatement $sql = $this->createCommand($connection, $parameter);
$key = $this->getCacheKey(array(clone($sql), $parameter));
$object = $this->getStatement()->getCache()->get($key);
- if(is_null($object))
+ if($object===null)
{
$object = $this->_mappedStatement->runQueryForObject($connection, $sql, $result);
$this->getStatement()->getCache()->set($key, $object);
diff --git a/framework/Data/SqlMap/Statements/TMappedStatement.php b/framework/Data/SqlMap/Statements/TMappedStatement.php index 6a9130fe..c4bb53dd 100644 --- a/framework/Data/SqlMap/Statements/TMappedStatement.php +++ b/framework/Data/SqlMap/Statements/TMappedStatement.php @@ -231,7 +231,7 @@ class TMappedStatement extends TComponent implements IMappedStatement $connection->setActive(true);
$reader = $sql->query();
//$reader = $this->executeSQLQueryLimit($connection, $sql, $max, $skip);
- if(!is_null($delegate))
+ if($delegate!==null)
{
foreach($reader as $row)
{
@@ -304,14 +304,14 @@ class TMappedStatement extends TComponent implements IMappedStatement //$recordSet = $this->executeSQLQuery($connection, $sql);
$connection->setActive(true);
$reader = $command->query();
- if(!is_null($delegate))
+ if($delegate!==null)
{
//while($row = $recordSet->fetchRow())
foreach($reader as $row)
{
$obj = $this->applyResultMap($row);
$key = TPropertyAccess::get($obj, $keyProperty);
- $value = is_null($valueProperty) ? $obj :
+ $value = ($valueProperty===null) ? $obj :
TPropertyAccess::get($obj, $valueProperty);
$param = new TResultSetMapItemParameter($key, $value, $parameter, $map);
$this->raiseRowDelegate($delegate, $param);
@@ -324,7 +324,7 @@ class TMappedStatement extends TComponent implements IMappedStatement {
$obj = $this->applyResultMap($row);
$key = TPropertyAccess::get($obj, $keyProperty);
- $map[$key] = is_null($valueProperty) ? $obj :
+ $map[$key] = ($valueProperty===null) ? $obj :
TPropertyAccess::get($obj, $valueProperty);
}
}
@@ -426,7 +426,7 @@ class TMappedStatement extends TComponent implements IMappedStatement // var_dump($command,$parameter);
$result = $command->execute();
- if(is_null($generatedKey))
+ if($generatedKey===null)
$generatedKey = $this->getPostGeneratedSelectKey($connection, $parameter);
$this->executePostSelect($connection);
@@ -445,7 +445,7 @@ class TMappedStatement extends TComponent implements IMappedStatement if($this->_statement instanceof TSqlMapInsert)
{
$selectKey = $this->_statement->getSelectKey();
- if(!is_null($selectKey) && !$selectKey->getIsAfter())
+ if(($selectKey!==null) && !$selectKey->getIsAfter())
return $this->executeSelectKey($connection, $parameter, $selectKey);
}
}
@@ -461,7 +461,7 @@ class TMappedStatement extends TComponent implements IMappedStatement if($this->_statement instanceof TSqlMapInsert)
{
$selectKey = $this->_statement->getSelectKey();
- if(!is_null($selectKey) && $selectKey->getIsAfter())
+ if(($selectKey!==null) && $selectKey->getIsAfter())
return $this->executeSelectKey($connection, $parameter, $selectKey);
}
}
@@ -575,7 +575,7 @@ class TMappedStatement extends TComponent implements IMappedStatement */
protected function fillResultClass($resultClass, $row, $resultObject)
{
- if(is_null($resultObject))
+ if($resultObject===null)
{
$registry = $this->getManager()->getTypeHandlers();
$resultObject = $this->_statement->createInstanceOfResultClass($registry,$row);
@@ -643,7 +643,7 @@ class TMappedStatement extends TComponent implements IMappedStatement $registry = $this->getManager()->getTypeHandlers();
$resultMap = $resultMap->resolveSubMap($registry,$row);
- if(is_null($resultObject))
+ if($resultObject===null)
$resultObject = $resultMap->createInstanceOfResult($registry);
if(is_object($resultObject))
@@ -734,10 +734,10 @@ class TMappedStatement extends TComponent implements IMappedStatement */
protected function fillDefaultResultMap($resultMap, $row, $resultObject)
{
- if(is_null($resultObject))
+ if($resultObject===null)
$resultObject='';
- if(!is_null($resultMap))
+ if($resultMap!==null)
$result = $this->fillArrayResultMap($resultMap, $row, $resultObject);
else
$result = $row;
@@ -762,8 +762,8 @@ class TMappedStatement extends TComponent implements IMappedStatement $registry=$this->getManager()->getTypeHandlers();
foreach($resultMap->getColumns() as $column)
{
- if(is_null($column->getType())
- && !is_null($resultObject) && !is_object($resultObject))
+ if(($column->getType()===null)
+ && ($resultObject!==null) && !is_object($resultObject))
$column->setType(gettype($resultObject));
$result[$column->getProperty()] = $column->getPropertyValue($registry,$row);
}
@@ -800,7 +800,7 @@ class TMappedStatement extends TComponent implements IMappedStatement {
$resultObject = $property->getPropertyValue($registry,$row);
}
- else if(strlen($select) == 0 && is_null($nested))
+ else if(strlen($select) == 0 && ($nested===null))
{
$value = $property->getPropertyValue($registry,$row);
@@ -810,7 +810,7 @@ class TMappedStatement extends TComponent implements IMappedStatement else
$resultObject = $value;
}
- else if(!is_null($nested))
+ else if($nested!==null)
{
if($property->instanceOfListType($resultObject) || $property->instanceOfArrayType($resultObject))
{
@@ -869,7 +869,7 @@ class TMappedStatement extends TComponent implements IMappedStatement $postSelect->setMethod(self::QUERY_FOR_OBJECT);
if(!$property->getLazyLoad())
- array_push($this->_selectQueque, $postSelect);
+ $this->_selectQueque[] = $postSelect;
}
/**
@@ -995,8 +995,8 @@ class TSqlMapObjectCollectionTree */
public function add($parent, $node, $object='')
{
- if(isset($this->_entries[$parent]) && !is_null($this->_entries[$parent])
- && isset($this->_entries[$node]) && !is_null($this->_entries[$node]))
+ if(isset($this->_entries[$parent]) && ($this->_entries[$parent]!==null)
+ && isset($this->_entries[$node]) && ($this->_entries[$node]!==null))
{
$this->_entries[$node] = $object;
return;
diff --git a/framework/Data/SqlMap/Statements/TPreparedCommand.php b/framework/Data/SqlMap/Statements/TPreparedCommand.php index 99bb6eff..66ff2a70 100644 --- a/framework/Data/SqlMap/Statements/TPreparedCommand.php +++ b/framework/Data/SqlMap/Statements/TPreparedCommand.php @@ -25,9 +25,15 @@ class TPreparedCommand {
public function create(TSqlMapManager $manager, $connection, $statement, $parameterObject,$skip=null,$max=null)
{
- $prepared = $statement->getSQLText()->getPreparedStatement($parameterObject);
+ $sqlText = $statement->getSQLText();
+
+ $prepared = $sqlText->getPreparedStatement($parameterObject);
$connection->setActive(true);
$sql = $prepared->getPreparedSql();
+
+ if($sqlText instanceof TSimpleDynamicSql)
+ $sql = $sqlText->replaceDynamicParameter($sql, $parameterObject);
+
if($max!==null || $skip!==null)
{
$builder = TDbMetaData::getInstance($connection)->createCommandBuilder();
@@ -35,6 +41,7 @@ class TPreparedCommand }
$command = $connection->createCommand($sql);
$this->applyParameterMap($manager, $command, $prepared, $statement, $parameterObject);
+
return $command;
}
diff --git a/framework/Data/SqlMap/Statements/TPreparedStatementFactory.php b/framework/Data/SqlMap/Statements/TPreparedStatementFactory.php index 44603408..a2a9df03 100644 --- a/framework/Data/SqlMap/Statements/TPreparedStatementFactory.php +++ b/framework/Data/SqlMap/Statements/TPreparedStatementFactory.php @@ -4,7 +4,7 @@ *
* @author Wei Zhuo <weizhuo[at]gmail[dot]com>
* @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2008 PradoSoft + * @copyright Copyright © 2005-2008 PradoSoft
* @license http://www.pradosoft.com/license/
* @version $Id$
* @package System.Data.SqlMap.Statements
@@ -35,7 +35,7 @@ class TPreparedStatementFactory {
$this->_preparedStatement = new TPreparedStatement();
$this->_preparedStatement->setPreparedSql($this->_commandText);
- if(!is_null($this->_statement->parameterMap()))
+ if($this->_statement->parameterMap()!==null)
$this->createParametersForTextCommand();
return $this->_preparedStatement;
}
diff --git a/framework/Data/SqlMap/Statements/TSimpleDynamicSql.php b/framework/Data/SqlMap/Statements/TSimpleDynamicSql.php index 910fd659..3e8969ba 100644 --- a/framework/Data/SqlMap/Statements/TSimpleDynamicSql.php +++ b/framework/Data/SqlMap/Statements/TSimpleDynamicSql.php @@ -27,23 +27,15 @@ class TSimpleDynamicSql extends TStaticSql $this->_mappings = $mappings;
}
- public function getPreparedStatement($parameter=null)
+ public function replaceDynamicParameter($sql, $parameter)
{
- $statement = parent::getPreparedStatement($parameter);
- if($parameter !== null)
- $this->mapDynamicParameter($statement, $parameter);
- return $statement;
- }
-
- protected function mapDynamicParameter($statement, $parameter)
- {
- $sql = $statement->getPreparedSql();
foreach($this->_mappings as $property)
{
$value = TPropertyAccess::get($parameter, $property);
$sql = preg_replace('/'.TSimpleDynamicParser::DYNAMIC_TOKEN.'/', $value, $sql, 1);
}
- $statement->setPreparedSql($sql);
+
+ return $sql;
}
}
diff --git a/framework/Data/SqlMap/TSqlMapConfig.php b/framework/Data/SqlMap/TSqlMapConfig.php index 98f2a844..c57ab40e 100644 --- a/framework/Data/SqlMap/TSqlMapConfig.php +++ b/framework/Data/SqlMap/TSqlMapConfig.php @@ -49,7 +49,7 @@ class TSqlMapConfig extends TDataSourceConfig $cache = $this->getApplication()->getCache();
if($cache !== null) {
$cache->delete($this->getCacheKey());
- }
+ }
}
/**
@@ -62,7 +62,10 @@ class TSqlMapConfig extends TDataSourceConfig {
$cache = $this->getApplication()->getCache();
if($cache !== null) {
- return $cache->set($this->getCacheKey(), $manager);
+ $dependencies = null;
+ if($this->getApplication()->getMode() !== TApplicationMode::Performance)
+ $dependencies = $manager->getCacheDependencies();
+ return $cache->set($this->getCacheKey(), $manager, 0, $dependencies);
}
}
return false;
@@ -147,6 +150,9 @@ class TSqlMapConfig extends TDataSourceConfig $this->cacheSqlMapManager($manager);
}
}
+ else {
+ $manager->setDbConnection($this->getDbConnection());
+ }
return $manager->getSqlmapGateway();
}
diff --git a/framework/Data/SqlMap/TSqlMapManager.php b/framework/Data/SqlMap/TSqlMapManager.php index 290050d1..432c1c5e 100644 --- a/framework/Data/SqlMap/TSqlMapManager.php +++ b/framework/Data/SqlMap/TSqlMapManager.php @@ -18,6 +18,7 @@ Prado::using('System.Data.SqlMap.Configuration.TSqlMapStatement'); Prado::using('System.Data.SqlMap.Configuration.*');
Prado::using('System.Data.SqlMap.DataMapper.*');
Prado::using('System.Data.SqlMap.Statements.*');
+Prado::using('System.Caching.TCache');
/**
@@ -50,6 +51,7 @@ class TSqlMapManager extends TComponent private $_connection;
private $_gateway;
+ private $_cacheDependencies;
/**
* Constructor, create a new SqlMap manager.
@@ -113,6 +115,18 @@ class TSqlMapManager extends TComponent }
/**
+ * @return TChainedCacheDependency
+ * @since 3.1.5
+ */
+ public function getCacheDependencies()
+ {
+ if($this->_cacheDependencies === null)
+ $this->_cacheDependencies=new TChainedCacheDependency();
+
+ return $this->_cacheDependencies;
+ }
+
+ /**
* Configures the current TSqlMapManager using the given xml configuration file
* defined in {@link ConfigFile setConfigFile()}.
* @return TSqlMapGateway create and configure a new TSqlMapGateway.
diff --git a/framework/Data/TDataSourceConfig.php b/framework/Data/TDataSourceConfig.php index 9e6bb2fc..cf1f963c 100644 --- a/framework/Data/TDataSourceConfig.php +++ b/framework/Data/TDataSourceConfig.php @@ -143,7 +143,7 @@ class TDataSourceConfig extends TModule */
public function setConnectionClass($value)
{
- if(!is_null($this->_conn))
+ if($this->_conn!==null)
throw new TConfigurationException('datasource_dbconnection_exists', $value);
$this->_connClass=$value;
}
@@ -160,7 +160,7 @@ class TDataSourceConfig extends TModule if($conn instanceof TDbConnection)
return $conn;
else if($conn instanceof TDataSourceConfig)
- return $conn->_conn;
+ return $conn->getDbConnection();
else
throw new TConfigurationException('datasource_dbconnection_invalid',$id);
}
diff --git a/framework/Data/TDbConnection.php b/framework/Data/TDbConnection.php index 259ca7b7..26d61883 100644 --- a/framework/Data/TDbConnection.php +++ b/framework/Data/TDbConnection.php @@ -267,7 +267,7 @@ class TDbConnection extends TComponent */
public function getCharset ()
{
- return $this>_charset;
+ return $this->_charset;
}
/**
diff --git a/framework/Exceptions/TException.php b/framework/Exceptions/TException.php index c6a87b4d..073b587d 100644 --- a/framework/Exceptions/TException.php +++ b/framework/Exceptions/TException.php @@ -35,6 +35,7 @@ class TException extends Exception
{
private $_errorCode='';
+ static $_messageCache=array(); /**
* Constructor.
@@ -64,16 +65,20 @@ class TException extends Exception protected function translateErrorMessage($key)
{
$msgFile=$this->getErrorMessageFile();
- if(($entries=@file($msgFile))!==false)
- {
- foreach($entries as $entry)
+ + // Cache messages + if (!isset(self::$_messageCache[$msgFile])) + { + if(($entries=@file($msgFile))!==false)
{
- @list($code,$message)=explode('=',$entry,2);
- if(trim($code)===$key)
- return trim($message);
+ foreach($entries as $entry)
+ {
+ @list($code,$message)=explode('=',$entry,2);
+ self::$_messageCache[$msgFile][trim($code)]=trim($message); + }
}
- }
- return $key;
+ } + return isset(self::$_messageCache[$msgFile][$key]) ? self::$_messageCache[$msgFile][$key] : $key;
}
/**
diff --git a/framework/I18N/TDateFormat.php b/framework/I18N/TDateFormat.php index 914131bd..2a4ef8bc 100644 --- a/framework/I18N/TDateFormat.php +++ b/framework/I18N/TDateFormat.php @@ -119,7 +119,7 @@ class TDateFormat extends TI18NControl implements IDataRenderer //no presets found, use the string as the pattern
//and let the DateFormat handle it.
- if(is_null($pattern))
+ if($pattern===null)
$pattern = $string;
if (!is_array($pattern) && strlen($pattern) == 0)
$pattern = null;
@@ -226,7 +226,7 @@ class TDateFormat extends TI18NControl implements IDataRenderer $app = $this->getApplication()->getGlobalization();
//initialized the default class wide formatter
- if(is_null(self::$formatter))
+ if(self::$formatter===null)
self::$formatter = new DateFormat($app->getCulture());
$culture = $this->getCulture();
diff --git a/framework/I18N/TGlobalization.php b/framework/I18N/TGlobalization.php index 7a7dce00..2201f4e5 100644 --- a/framework/I18N/TGlobalization.php +++ b/framework/I18N/TGlobalization.php @@ -78,8 +78,7 @@ class TGlobalization extends TModule else
{
$t = $config->getElementByTagName('translation');
- if($t)
- $translation = $t->getAttributes();
+ $translation = ($t)?$t->getAttributes():null;
}
if($translation)
$this->setTranslationConfiguration($translation);
@@ -232,7 +231,7 @@ class TGlobalization extends TModule */
public function getCultureVariants($culture=null)
{
- if(is_null($culture)) $culture = $this->getCulture();
+ if($culture===null) $culture = $this->getCulture();
$variants = explode('_', $culture);
$result = array();
for(; count($variants) > 0; array_pop($variants))
diff --git a/framework/I18N/TI18NControl.php b/framework/I18N/TI18NControl.php index ac3246fe..c04477e0 100644 --- a/framework/I18N/TI18NControl.php +++ b/framework/I18N/TI18NControl.php @@ -4,7 +4,7 @@ *
* @author Wei Zhuo <weizhuo[at]gmail[dot]com>
* @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2008 PradoSoft + * @copyright Copyright © 2005-2008 PradoSoft
* @license http://www.pradosoft.com/license/
* @version $Id$
* @package System.I18N
@@ -49,11 +49,11 @@ class TI18NControl extends TControl //fall back to globalization charset
if(empty($charset))
- $charset = is_null($app) ? '' : $app->getCharset();
+ $charset = ($app===null) ? '' : $app->getCharset();
//fall back to default charset
if(empty($charset))
- $charset = (is_null($app)) ? 'UTF-8' : $app->getDefaultCharset();
+ $charset = ($app===null) ? 'UTF-8' : $app->getDefaultCharset();
return $charset;
}
diff --git a/framework/I18N/TNumberFormat.php b/framework/I18N/TNumberFormat.php index 62b43243..1f33e6e4 100644 --- a/framework/I18N/TNumberFormat.php +++ b/framework/I18N/TNumberFormat.php @@ -220,7 +220,7 @@ class TNumberFormat extends TI18NControl implements IDataRenderer $app = $this->getApplication()->getGlobalization();
//initialized the default class wide formatter
- if(is_null(self::$formatter))
+ if(self::$formatter===null)
self::$formatter = new NumberFormat($app->getCulture());
$pattern = strlen($this->getPattern()) > 0
diff --git a/framework/I18N/TTranslate.php b/framework/I18N/TTranslate.php index 2f1e0633..a444aeba 100644 --- a/framework/I18N/TTranslate.php +++ b/framework/I18N/TTranslate.php @@ -4,7 +4,7 @@ *
* @author Wei Zhuo <weizhuo[at]gmail[dot]com>
* @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2008 PradoSoft + * @copyright Copyright © 2005-2008 PradoSoft
* @license http://www.pradosoft.com/license/
* @version $Id$
* @package System.I18N
@@ -236,7 +236,7 @@ class TTranslate extends TI18NControl $app = $this->getApplication()->getGlobalization();
//no translation handler provided
- if(is_null($config = $app->getTranslationConfiguration()))
+ if(($config = $app->getTranslationConfiguration())===null)
return strtr($text, $subs);
$catalogue = $this->getCatalogue();
diff --git a/framework/I18N/core/MessageSource_MySQL.php b/framework/I18N/core/MessageSource_MySQL.php index 317d53e2..080b89bc 100644 --- a/framework/I18N/core/MessageSource_MySQL.php +++ b/framework/I18N/core/MessageSource_MySQL.php @@ -87,7 +87,7 @@ class MessageSource_MySQL extends MessageSource {
/*static $conn;
- if(!is_null($conn))
+ if($conn!==null)
return $conn;
*/
$dsninfo = $this->dns;
diff --git a/framework/PradoBase.php b/framework/PradoBase.php index 11bc7f41..084dd62b 100644 --- a/framework/PradoBase.php +++ b/framework/PradoBase.php @@ -1,619 +1,614 @@ -<?php
-/**
- * PradoBase class file.
- *
- * This is the file that establishes the PRADO component model
- * and error handling mechanism.
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2008 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System
- */
-
-/**
- * Defines the PRADO framework installation path.
- */
-if(!defined('PRADO_DIR'))
- define('PRADO_DIR',dirname(__FILE__));
-/**
- * Defines the default permission for writable directories and files
- */
-if(!defined('PRADO_CHMOD'))
- define('PRADO_CHMOD',0777);
-
-/**
- * PradoBase class.
- *
- * PradoBase implements a few fundamental static methods.
- *
- * To use the static methods, Use Prado as the class name rather than PradoBase.
- * PradoBase is meant to serve as the base class of Prado. The latter might be
- * rewritten for customization.
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @version $Id$
- * @package System
- * @since 3.0
- */
-class PradoBase
-{
- /**
- * File extension for Prado class files.
- */
- const CLASS_FILE_EXT='.php';
- /**
- * @var array list of path aliases
- */
- private static $_aliases=array('System'=>PRADO_DIR);
- /**
- * @var array list of namespaces currently in use
- */
- private static $_usings=array();
- /**
- * @var TApplication the application instance
- */
- private static $_application=null;
- /**
- * @var TLogger logger instance
- */
- private static $_logger=null;
-
- /**
- * @return string the version of Prado framework
- */
- public static function getVersion()
- {
- return '3.2a';
- }
-
- /**
- * Initializes error handlers.
- * This method set error and exception handlers to be functions
- * defined in this class.
- */
- public static function initErrorHandlers()
- {
- /**
- * Sets error handler to be Prado::phpErrorHandler
- */
- set_error_handler(array('PradoBase','phpErrorHandler'),error_reporting());
- /**
- * Sets exception handler to be Prado::exceptionHandler
- */
- set_exception_handler(array('PradoBase','exceptionHandler'));
- }
-
- /**
- * Class autoload loader.
- * This method is provided to be invoked within an __autoload() magic method.
- * @param string class name
- */
- public static function autoload($className)
- {
- include_once($className.self::CLASS_FILE_EXT);
- if(!class_exists($className,false) && !interface_exists($className,false))
- self::fatalError("Class file for '$className' cannot be found.");
- }
-
- /**
- * @param integer the type of "powered logo". Valid values include 0 and 1.
- * @return string a string that can be displayed on your Web page showing powered-by-PRADO information
- */
- public static function poweredByPrado($logoType=0)
- {
- $logoName=$logoType==1?'powered2':'powered';
- if(self::$_application!==null)
- {
- $am=self::$_application->getAssetManager();
- $url=$am->publishFilePath(self::getPathOfNamespace('System.'.$logoName,'.gif'));
- }
- else
- $url='http://www.pradosoft.com/images/'.$logoName.'.gif';
- return '<a title="Powered by PRADO" href="http://www.pradosoft.com/" target="_blank"><img src="'.$url.'" style="border-width:0px;" alt="Powered by PRADO" /></a>';
- }
-
- public static function metaGenerator() +<?php +/** + * PradoBase class file. + * + * This is the file that establishes the PRADO component model + * and error handling mechanism. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2008 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System + */ + +/** + * Defines the PRADO framework installation path. + */ +if(!defined('PRADO_DIR')) + define('PRADO_DIR',dirname(__FILE__)); +/** + * Defines the default permission for writable directories and files + */ +if(!defined('PRADO_CHMOD')) + define('PRADO_CHMOD',0777); + +/** + * PradoBase class. + * + * PradoBase implements a few fundamental static methods. + * + * To use the static methods, Use Prado as the class name rather than PradoBase. + * PradoBase is meant to serve as the base class of Prado. The latter might be + * rewritten for customization. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id$ + * @package System + * @since 3.0 + */ +class PradoBase +{ + /** + * File extension for Prado class files. + */ + const CLASS_FILE_EXT='.php'; + /** + * @var array list of path aliases + */ + private static $_aliases=array('System'=>PRADO_DIR); + /** + * @var array list of namespaces currently in use + */ + private static $_usings=array(); + /** + * @var TApplication the application instance + */ + private static $_application=null; + /** + * @var TLogger logger instance + */ + private static $_logger=null; + + /** + * @return string the version of Prado framework + */ + public static function getVersion() + { + return '3.2-dev';
+ } + + /** + * Initializes error handlers. + * This method set error and exception handlers to be functions + * defined in this class. + */ + public static function initErrorHandlers() + { + /** + * Sets error handler to be Prado::phpErrorHandler + */ + set_error_handler(array('PradoBase','phpErrorHandler'),error_reporting()); + /** + * Sets exception handler to be Prado::exceptionHandler + */ + set_exception_handler(array('PradoBase','exceptionHandler')); + } + + /** + * Class autoload loader. + * This method is provided to be invoked within an __autoload() magic method. + * @param string class name + */ + public static function autoload($className) + { + include_once($className.self::CLASS_FILE_EXT); + if(!class_exists($className,false) && !interface_exists($className,false)) + self::fatalError("Class file for '$className' cannot be found."); + } + + /** + * @param integer the type of "powered logo". Valid values include 0 and 1. + * @return string a string that can be displayed on your Web page showing powered-by-PRADO information + */ + public static function poweredByPrado($logoType=0) + { + $logoName=$logoType==1?'powered2':'powered'; + if(self::$_application!==null) + { + $am=self::$_application->getAssetManager(); + $url=$am->publishFilePath(self::getPathOfNamespace('System.'.$logoName,'.gif')); + } + else + $url='http://www.pradosoft.com/images/'.$logoName.'.gif'; + return '<a title="Powered by PRADO" href="http://www.pradosoft.com/" target="_blank"><img src="'.$url.'" style="border-width:0px;" alt="Powered by PRADO" /></a>'; + } + + /** + * PHP error handler. + * This method should be registered as PHP error handler using + * {@link set_error_handler}. The method throws an exception that + * contains the error information. + * @param integer the level of the error raised + * @param string the error message + * @param string the filename that the error was raised in + * @param integer the line number the error was raised at + */ + public static function phpErrorHandler($errno,$errstr,$errfile,$errline) { - return 'PRADO - http://www.pradosoft.com/'; + if(error_reporting()!=0) + throw new TPhpErrorException($errno,$errstr,$errfile,$errline); } - /**
- * PHP error handler.
- * This method should be registered as PHP error handler using
- * {@link set_error_handler}. The method throws an exception that
- * contains the error information.
- * @param integer the level of the error raised
- * @param string the error message
- * @param string the filename that the error was raised in
- * @param integer the line number the error was raised at
- */
- public static function phpErrorHandler($errno,$errstr,$errfile,$errline)
- {
- if(error_reporting()!=0)
- throw new TPhpErrorException($errno,$errstr,$errfile,$errline);
- }
-
- /**
- * Default exception handler.
- * This method should be registered as default exception handler using
- * {@link set_exception_handler}. The method tries to use the errorhandler
- * module of the Prado application to handle the exception.
- * If the application or the module does not exist, it simply echoes the
- * exception.
- * @param Exception exception that is not caught
- */
- public static function exceptionHandler($exception)
- {
- if(self::$_application!==null && ($errorHandler=self::$_application->getErrorHandler())!==null)
- {
- $errorHandler->handleError(null,$exception);
- }
- else
- {
- echo $exception;
- }
- exit(1);
- }
-
- /**
- * Stores the application instance in the class static member.
- * This method helps implement a singleton pattern for TApplication.
- * Repeated invocation of this method or the application constructor
- * will cause the throw of an exception.
- * This method should only be used by framework developers.
- * @param TApplication the application instance
- * @throws TInvalidOperationException if this method is invoked twice or more.
- */
- public static function setApplication($application)
- {
- if(self::$_application!==null)
- throw new TInvalidOperationException('prado_application_singleton_required');
- self::$_application=$application;
- }
-
- /**
- * @return TApplication the application singleton, null if the singleton has not be created yet.
- */
- public static function getApplication()
- {
- return self::$_application;
- }
-
- /**
- * @return string the path of the framework
- */
- public static function getFrameworkPath()
- {
- return PRADO_DIR;
- }
-
- /**
- * Serializes a data.
- * The original PHP serialize function has a bug that may not serialize
- * properly an object.
- * @param mixed data to be serialized
- * @return string the serialized data
- */
- public static function serialize($data)
- {
- $arr[0]=$data;
- return serialize($arr);
- }
-
- /**
- * Unserializes a data.
- * The original PHP unserialize function has a bug that may not unserialize
- * properly an object.
- * @param string data to be unserialized
- * @return mixed unserialized data, null if unserialize failed
- */
- public static function unserialize($str)
- {
- $arr=unserialize($str);
- return isset($arr[0])?$arr[0]:null;
- }
-
- /**
- * Creates a component with the specified type.
- * A component type can be either the component class name
- * or a namespace referring to the path of the component class file.
- * For example, 'TButton', 'System.Web.UI.WebControls.TButton' are both
- * valid component type.
- * This method can also pass parameters to component constructors.
- * All parameters passed to this method except the first one (the component type)
- * will be supplied as component constructor parameters.
- * @param string component type
- * @return TComponent component instance of the specified type
- * @throws TInvalidDataValueException if the component type is unknown
- */
- public static function createComponent($type)
- {
- self::using($type);
- if(($pos=strrpos($type,'.'))!==false)
- $type=substr($type,$pos+1);
- if(($n=func_num_args())>1)
- {
- $args=func_get_args();
- $s='$args[1]';
- for($i=2;$i<$n;++$i)
- $s.=",\$args[$i]";
- eval("\$component=new $type($s);");
- return $component;
- }
- else
- return new $type;
- }
-
- /**
- * Uses a namespace.
- * A namespace ending with an asterisk '*' refers to a directory, otherwise it represents a PHP file.
- * If the namespace corresponds to a directory, the directory will be appended
- * to the include path. If the namespace corresponds to a file, it will be included (include_once).
- * @param string namespace to be used
- * @param boolean whether to check the existence of the class after the class file is included
- * @throws TInvalidDataValueException if the namespace is invalid
- */
- public static function using($namespace,$checkClassExistence=true)
- {
- if(isset(self::$_usings[$namespace]) || class_exists($namespace,false))
- return;
- if(($pos=strrpos($namespace,'.'))===false) // a class name
- {
- try
- {
- include_once($namespace.self::CLASS_FILE_EXT);
- }
- catch(Exception $e)
- {
- if($checkClassExistence && !class_exists($namespace,false))
- throw new TInvalidOperationException('prado_component_unknown',$namespace,$e->getMessage());
- else
- throw $e;
- }
- }
- else if(($path=self::getPathOfNamespace($namespace,self::CLASS_FILE_EXT))!==null)
- {
- $className=substr($namespace,$pos+1);
- if($className==='*') // a directory
- {
- self::$_usings[$namespace]=$path;
- set_include_path(get_include_path().PATH_SEPARATOR.$path);
- }
- else // a file
- {
- self::$_usings[$namespace]=$path;
- if(!$checkClassExistence || !class_exists($className,false))
- {
- try
- {
- include_once($path);
- }
- catch(Exception $e)
- {
- if($checkClassExistence && !class_exists($className,false))
- throw new TInvalidOperationException('prado_component_unknown',$className,$e->getMessage());
- else
- throw $e;
- }
- }
- }
- }
- else
- throw new TInvalidDataValueException('prado_using_invalid',$namespace);
- }
-
- /**
- * Translates a namespace into a file path.
- * The first segment of the namespace is considered as a path alias
- * which is replaced with the actual path. The rest segments are
- * subdirectory names appended to the aliased path.
- * If the namespace ends with an asterisk '*', it represents a directory;
- * Otherwise it represents a file whose extension name is specified by the second parameter (defaults to empty).
- * Note, this method does not ensure the existence of the resulting file path.
- * @param string namespace
- * @param string extension to be appended if the namespace refers to a file
- * @return string file path corresponding to the namespace, null if namespace is invalid
- */
- public static function getPathOfNamespace($namespace,$ext='')
- {
- if(isset(self::$_usings[$namespace]))
- return self::$_usings[$namespace];
- else if(isset(self::$_aliases[$namespace]))
- return self::$_aliases[$namespace];
- else
- {
- $segs=explode('.',$namespace);
- $alias=array_shift($segs);
- if(($file=array_pop($segs))!==null && ($root=self::getPathOfAlias($alias))!==null)
- return rtrim($root.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR ,$segs),'/\\').(($file==='*')?'':DIRECTORY_SEPARATOR.$file.$ext);
- else
- return null;
- }
- }
-
- /**
- * @param string alias to the path
- * @return string the path corresponding to the alias, null if alias not defined.
- */
- public static function getPathOfAlias($alias)
- {
- return isset(self::$_aliases[$alias])?self::$_aliases[$alias]:null;
- }
-
- protected static function getPathAliases()
- {
- return self::$_aliases;
- }
-
- /**
- * @param string alias to the path
- * @param string the path corresponding to the alias
- * @throws TInvalidOperationException if the alias is already defined
- * @throws TInvalidDataValueException if the path is not a valid file path
- */
- public static function setPathOfAlias($alias,$path)
- {
- if(isset(self::$_aliases[$alias]))
- throw new TInvalidOperationException('prado_alias_redefined',$alias);
- else if(($rp=realpath($path))!==false && is_dir($rp))
- {
- if(strpos($alias,'.')===false)
- self::$_aliases[$alias]=$rp;
- else
- throw new TInvalidDataValueException('prado_aliasname_invalid',$alias);
- }
- else
- throw new TInvalidDataValueException('prado_alias_invalid',$alias,$path);
- }
-
- /**
- * Fatal error handler.
- * This method displays an error message together with the current call stack.
- * The application will exit after calling this method.
- * @param string error message
- */
- public static function fatalError($msg)
- {
- echo '<h1>Fatal Error</h1>';
- echo '<p>'.$msg.'</p>';
- if(!function_exists('debug_backtrace'))
- return;
- echo '<h2>Debug Backtrace</h2>';
- echo '<pre>';
- $index=-1;
- foreach(debug_backtrace() as $t)
- {
- $index++;
- if($index==0) // hide the backtrace of this function
- continue;
- echo '#'.$index.' ';
- if(isset($t['file']))
- echo basename($t['file']) . ':' . $t['line'];
- else
- echo '<PHP inner-code>';
- echo ' -- ';
- if(isset($t['class']))
- echo $t['class'] . $t['type'];
- echo $t['function'] . '(';
- if(isset($t['args']) && sizeof($t['args']) > 0)
- {
- $count=0;
- foreach($t['args'] as $item)
- {
- if(is_string($item))
- {
- $str=htmlentities(str_replace("\r\n", "", $item), ENT_QUOTES);
- if (strlen($item) > 70)
- echo "'". substr($str, 0, 70) . "...'";
- else
- echo "'" . $str . "'";
- }
- else if (is_int($item) || is_float($item))
- echo $item;
- else if (is_object($item))
- echo get_class($item);
- else if (is_array($item))
- echo 'array(' . count($item) . ')';
- else if (is_bool($item))
- echo $item ? 'true' : 'false';
- else if ($item === null)
- echo 'NULL';
- else if (is_resource($item))
- echo get_resource_type($item);
- $count++;
- if (count($t['args']) > $count)
- echo ', ';
- }
- }
- echo ")\n";
- }
- echo '</pre>';
- exit(1);
- }
-
- /**
- * Returns a list of user preferred languages.
- * The languages are returned as an array. Each array element
- * represents a single language preference. The languages are ordered
- * according to user preferences. The first language is the most preferred.
- * @return array list of user preferred languages.
- */
- public static function getUserLanguages()
- {
- static $languages=null;
- if($languages===null)
- {
- if(!isset($_SERVER['HTTP_ACCEPT_LANGUAGE']))
- $languages[0]='en';
- else
- {
- $languages=array();
- foreach(explode(',',$_SERVER['HTTP_ACCEPT_LANGUAGE']) as $language)
- {
- $array=split(';q=',trim($language));
- $languages[trim($array[0])]=isset($array[1])?(float)$array[1]:1.0;
- }
- arsort($languages);
- $languages=array_keys($languages);
- if(empty($languages))
- $languages[0]='en';
- }
- }
- return $languages;
- }
-
- /**
- * Returns the most preferred language by the client user.
- * @return string the most preferred language by the client user, defaults to English.
- */
- public static function getPreferredLanguage()
- {
- static $language=null;
- if($language===null)
- {
- $langs=Prado::getUserLanguages();
- $lang=explode('-',$langs[0]);
- if(empty($lang[0]) || !ctype_alpha($lang[0]))
- $language='en';
- else
- $language=$lang[0];
- }
- return $language;
- }
-
- /**
- * Writes a log message.
- * This method wraps {@link log()} by checking the application mode.
- * When the application is in Debug mode, debug backtrace information is appended
- * to the message and the message is logged at DEBUG level.
- * When the application is in Performance mode, this method does nothing.
- * Otherwise, the message is logged at INFO level.
- * @param string message to be logged
- * @param string category of the message
- * @see log, getLogger
- */
- public static function trace($msg,$category='Uncategorized')
- {
- if(self::$_application && self::$_application->getMode()===TApplicationMode::Performance)
- return;
- if(!self::$_application || self::$_application->getMode()===TApplicationMode::Debug)
- {
- $trace=debug_backtrace();
- if(isset($trace[0]['file']) && isset($trace[0]['line']))
- $msg.=" (line {$trace[0]['line']}, {$trace[0]['file']})";
- $level=TLogger::DEBUG;
- }
- else
- $level=TLogger::INFO;
- self::log($msg,$level,$category);
- }
-
- /**
- * Logs a message.
- * Messages logged by this method may be retrieved via {@link TLogger::getLogs}
- * and may be recorded in different media, such as file, email, database, using
- * {@link TLogRouter}.
- * @param string message to be logged
- * @param integer level of the message. Valid values include
- * TLogger::DEBUG, TLogger::INFO, TLogger::NOTICE, TLogger::WARNING,
- * TLogger::ERROR, TLogger::ALERT, TLogger::FATAL.
- * @param string category of the message
- */
- public static function log($msg,$level=TLogger::INFO,$category='Uncategorized')
- {
- if(self::$_logger===null)
- self::$_logger=new TLogger;
- self::$_logger->log($msg,$level,$category);
- }
-
- /**
- * @return TLogger message logger
- */
- public static function getLogger()
- {
- if(self::$_logger===null)
- self::$_logger=new TLogger;
- return self::$_logger;
- }
-
- /**
- * Converts a variable into a string representation.
- * This method achieves the similar functionality as var_dump and print_r
- * but is more robust when handling complex objects such as PRADO controls.
- * @param mixed variable to be dumped
- * @param integer maximum depth that the dumper should go into the variable. Defaults to 10.
- * @param boolean whether to syntax highlight the output. Defaults to false.
- * @return string the string representation of the variable
- */
- public static function varDump($var,$depth=10,$highlight=false)
- {
- Prado::using('System.Util.TVarDumper');
- return TVarDumper::dump($var,$depth,$highlight);
- }
-
- /**
- * Localize a text to the locale/culture specified in the globalization handler.
- * @param string text to be localized.
- * @param array a set of parameters to substitute.
- * @param string a different catalogue to find the localize text.
- * @param string the input AND output charset.
- * @return string localized text.
- * @see TTranslate::formatter()
- * @see TTranslate::init()
- */
- public static function localize($text, $parameters=array(), $catalogue=null, $charset=null)
- {
- Prado::using('System.I18N.Translation');
- $app = Prado::getApplication()->getGlobalization(false);
-
- $params = array();
- foreach($parameters as $key => $value)
- $params['{'.$key.'}'] = $value;
-
- //no translation handler provided
- if($app===null || ($config = $app->getTranslationConfiguration())===null)
- return strtr($text, $params);
-
- if ($catalogue===null)
- $catalogue=isset($config['catalogue'])?$config['catalogue']:'messages';
-
- Translation::init($catalogue);
-
- //globalization charset
- $appCharset = $app===null ? '' : $app->getCharset();
-
- //default charset
- $defaultCharset = ($app===null) ? 'UTF-8' : $app->getDefaultCharset();
-
- //fall back
- if(empty($charset)) $charset = $appCharset;
- if(empty($charset)) $charset = $defaultCharset;
-
- return Translation::formatter($catalogue)->format($text,$params,$catalogue,$charset);
- }
-}
-
-/**
- * TReflectionClass class.
- * This class was originally written to cope with the incompatibility between different PHP versions.
- * It is equivalent to ReflectionClass for PHP version >= 5.1.0
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @version $Id$
- * @package System
- * @since 3.0
- */
-class TReflectionClass extends ReflectionClass
-{
-}
-
-/**
- * Includes the classes essential for PradoBase class
- */
-PradoBase::using('System.TComponent');
-PradoBase::using('System.Exceptions.TException');
-PradoBase::using('System.Util.TLogger');
-
-?>
+ /** + * Default exception handler. + * This method should be registered as default exception handler using + * {@link set_exception_handler}. The method tries to use the errorhandler + * module of the Prado application to handle the exception. + * If the application or the module does not exist, it simply echoes the + * exception. + * @param Exception exception that is not caught + */ + public static function exceptionHandler($exception) + { + if(self::$_application!==null && ($errorHandler=self::$_application->getErrorHandler())!==null) + { + $errorHandler->handleError(null,$exception); + } + else + { + echo $exception; + } + exit(1); + } + + /** + * Stores the application instance in the class static member. + * This method helps implement a singleton pattern for TApplication. + * Repeated invocation of this method or the application constructor + * will cause the throw of an exception. + * This method should only be used by framework developers. + * @param TApplication the application instance + * @throws TInvalidOperationException if this method is invoked twice or more. + */ + public static function setApplication($application) + { + if(self::$_application!==null) + throw new TInvalidOperationException('prado_application_singleton_required'); + self::$_application=$application; + } + + /** + * @return TApplication the application singleton, null if the singleton has not be created yet. + */ + public static function getApplication() + { + return self::$_application; + } + + /** + * @return string the path of the framework + */ + public static function getFrameworkPath() + { + return PRADO_DIR; + } + + /** + * Serializes a data. + * The original PHP serialize function has a bug that may not serialize + * properly an object. + * @param mixed data to be serialized + * @return string the serialized data + */ + public static function serialize($data) + { + $arr[0]=$data; + return serialize($arr); + } + + /** + * Unserializes a data. + * The original PHP unserialize function has a bug that may not unserialize + * properly an object. + * @param string data to be unserialized + * @return mixed unserialized data, null if unserialize failed + */ + public static function unserialize($str) + { + $arr=unserialize($str); + return isset($arr[0])?$arr[0]:null; + } + + /** + * Creates a component with the specified type. + * A component type can be either the component class name + * or a namespace referring to the path of the component class file. + * For example, 'TButton', 'System.Web.UI.WebControls.TButton' are both + * valid component type. + * This method can also pass parameters to component constructors. + * All parameters passed to this method except the first one (the component type) + * will be supplied as component constructor parameters. + * @param string component type + * @return TComponent component instance of the specified type + * @throws TInvalidDataValueException if the component type is unknown + */ + public static function createComponent($type) + { + self::using($type); + if(($pos=strrpos($type,'.'))!==false) + $type=substr($type,$pos+1); + if(($n=func_num_args())>1) + { + $args=func_get_args(); + $s='$args[1]'; + for($i=2;$i<$n;++$i) + $s.=",\$args[$i]"; + eval("\$component=new $type($s);"); + return $component; + } + else + return new $type; + } + + /** + * Uses a namespace. + * A namespace ending with an asterisk '*' refers to a directory, otherwise it represents a PHP file. + * If the namespace corresponds to a directory, the directory will be appended + * to the include path. If the namespace corresponds to a file, it will be included (include_once). + * @param string namespace to be used + * @param boolean whether to check the existence of the class after the class file is included + * @throws TInvalidDataValueException if the namespace is invalid + */ + public static function using($namespace,$checkClassExistence=true) + { + if(isset(self::$_usings[$namespace]) || class_exists($namespace,false)) + return; + if(($pos=strrpos($namespace,'.'))===false) // a class name + { + try + { + include_once($namespace.self::CLASS_FILE_EXT); + } + catch(Exception $e) + { + if($checkClassExistence && !class_exists($namespace,false)) + throw new TInvalidOperationException('prado_component_unknown',$namespace,$e->getMessage()); + else + throw $e; + } + } + else if(($path=self::getPathOfNamespace($namespace,self::CLASS_FILE_EXT))!==null) + { + $className=substr($namespace,$pos+1); + if($className==='*') // a directory + { + self::$_usings[$namespace]=$path; + set_include_path(get_include_path().PATH_SEPARATOR.$path); + } + else // a file + { + self::$_usings[$namespace]=$path; + if(!$checkClassExistence || !class_exists($className,false)) + { + try + { + include_once($path); + } + catch(Exception $e) + { + if($checkClassExistence && !class_exists($className,false)) + throw new TInvalidOperationException('prado_component_unknown',$className,$e->getMessage()); + else + throw $e; + } + } + } + } + else + throw new TInvalidDataValueException('prado_using_invalid',$namespace); + } + + /** + * Translates a namespace into a file path. + * The first segment of the namespace is considered as a path alias + * which is replaced with the actual path. The rest segments are + * subdirectory names appended to the aliased path. + * If the namespace ends with an asterisk '*', it represents a directory; + * Otherwise it represents a file whose extension name is specified by the second parameter (defaults to empty). + * Note, this method does not ensure the existence of the resulting file path. + * @param string namespace + * @param string extension to be appended if the namespace refers to a file + * @return string file path corresponding to the namespace, null if namespace is invalid + */ + public static function getPathOfNamespace($namespace,$ext='') + { + if(isset(self::$_usings[$namespace])) + return self::$_usings[$namespace]; + else if(isset(self::$_aliases[$namespace])) + return self::$_aliases[$namespace]; + else + { + $segs=explode('.',$namespace); + $alias=array_shift($segs); + if(($file=array_pop($segs))!==null && ($root=self::getPathOfAlias($alias))!==null) + return rtrim($root.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR ,$segs),'/\\').(($file==='*')?'':DIRECTORY_SEPARATOR.$file.$ext); + else + return null; + } + } + + /** + * @param string alias to the path + * @return string the path corresponding to the alias, null if alias not defined. + */ + public static function getPathOfAlias($alias) + { + return isset(self::$_aliases[$alias])?self::$_aliases[$alias]:null; + } + + protected static function getPathAliases() + { + return self::$_aliases; + } + + /** + * @param string alias to the path + * @param string the path corresponding to the alias + * @throws TInvalidOperationException if the alias is already defined + * @throws TInvalidDataValueException if the path is not a valid file path + */ + public static function setPathOfAlias($alias,$path) + { + if(isset(self::$_aliases[$alias])) + throw new TInvalidOperationException('prado_alias_redefined',$alias); + else if(($rp=realpath($path))!==false && is_dir($rp)) + { + if(strpos($alias,'.')===false) + self::$_aliases[$alias]=$rp; + else + throw new TInvalidDataValueException('prado_aliasname_invalid',$alias); + } + else + throw new TInvalidDataValueException('prado_alias_invalid',$alias,$path); + } + + /** + * Fatal error handler. + * This method displays an error message together with the current call stack. + * The application will exit after calling this method. + * @param string error message + */ + public static function fatalError($msg) + { + echo '<h1>Fatal Error</h1>'; + echo '<p>'.$msg.'</p>'; + if(!function_exists('debug_backtrace')) + return; + echo '<h2>Debug Backtrace</h2>'; + echo '<pre>'; + $index=-1; + foreach(debug_backtrace() as $t) + { + $index++; + if($index==0) // hide the backtrace of this function + continue; + echo '#'.$index.' '; + if(isset($t['file'])) + echo basename($t['file']) . ':' . $t['line']; + else + echo '<PHP inner-code>'; + echo ' -- '; + if(isset($t['class'])) + echo $t['class'] . $t['type']; + echo $t['function'] . '('; + if(isset($t['args']) && sizeof($t['args']) > 0) + { + $count=0; + foreach($t['args'] as $item) + { + if(is_string($item)) + { + $str=htmlentities(str_replace("\r\n", "", $item), ENT_QUOTES); + if (strlen($item) > 70) + echo "'". substr($str, 0, 70) . "...'"; + else + echo "'" . $str . "'"; + } + else if (is_int($item) || is_float($item)) + echo $item; + else if (is_object($item)) + echo get_class($item); + else if (is_array($item)) + echo 'array(' . count($item) . ')'; + else if (is_bool($item)) + echo $item ? 'true' : 'false'; + else if ($item === null) + echo 'NULL'; + else if (is_resource($item)) + echo get_resource_type($item); + $count++; + if (count($t['args']) > $count) + echo ', '; + } + } + echo ")\n"; + } + echo '</pre>'; + exit(1); + } + + /** + * Returns a list of user preferred languages. + * The languages are returned as an array. Each array element + * represents a single language preference. The languages are ordered + * according to user preferences. The first language is the most preferred. + * @return array list of user preferred languages. + */ + public static function getUserLanguages() + { + static $languages=null; + if($languages===null) + { + if(!isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) + $languages[0]='en'; + else + { + $languages=array(); + foreach(explode(',',$_SERVER['HTTP_ACCEPT_LANGUAGE']) as $language) + { + $array=explode(';q=',trim($language)); + $languages[trim($array[0])]=isset($array[1])?(float)$array[1]:1.0; + } + arsort($languages); + $languages=array_keys($languages); + if(empty($languages)) + $languages[0]='en'; + } + } + return $languages; + } + + /** + * Returns the most preferred language by the client user. + * @return string the most preferred language by the client user, defaults to English. + */ + public static function getPreferredLanguage() + { + static $language=null; + if($language===null) + { + $langs=Prado::getUserLanguages(); + $lang=explode('-',$langs[0]); + if(empty($lang[0]) || !ctype_alpha($lang[0])) + $language='en'; + else + $language=$lang[0]; + } + return $language; + } + + /** + * Writes a log message. + * This method wraps {@link log()} by checking the application mode. + * When the application is in Debug mode, debug backtrace information is appended + * to the message and the message is logged at DEBUG level. + * When the application is in Performance mode, this method does nothing. + * Otherwise, the message is logged at INFO level. + * @param string message to be logged + * @param string category of the message + * @see log, getLogger + */ + public static function trace($msg,$category='Uncategorized') + { + if(self::$_application && self::$_application->getMode()===TApplicationMode::Performance) + return; + if(!self::$_application || self::$_application->getMode()===TApplicationMode::Debug) + { + $trace=debug_backtrace(); + if(isset($trace[0]['file']) && isset($trace[0]['line'])) + $msg.=" (line {$trace[0]['line']}, {$trace[0]['file']})"; + $level=TLogger::DEBUG; + } + else + $level=TLogger::INFO; + self::log($msg,$level,$category); + } + + /** + * Logs a message. + * Messages logged by this method may be retrieved via {@link TLogger::getLogs} + * and may be recorded in different media, such as file, email, database, using + * {@link TLogRouter}. + * @param string message to be logged + * @param integer level of the message. Valid values include + * TLogger::DEBUG, TLogger::INFO, TLogger::NOTICE, TLogger::WARNING, + * TLogger::ERROR, TLogger::ALERT, TLogger::FATAL. + * @param string category of the message + */ + public static function log($msg,$level=TLogger::INFO,$category='Uncategorized') + { + if(self::$_logger===null) + self::$_logger=new TLogger; + self::$_logger->log($msg,$level,$category); + } + + /** + * @return TLogger message logger + */ + public static function getLogger() + { + if(self::$_logger===null) + self::$_logger=new TLogger; + return self::$_logger; + } + + /** + * Converts a variable into a string representation. + * This method achieves the similar functionality as var_dump and print_r + * but is more robust when handling complex objects such as PRADO controls. + * @param mixed variable to be dumped + * @param integer maximum depth that the dumper should go into the variable. Defaults to 10. + * @param boolean whether to syntax highlight the output. Defaults to false. + * @return string the string representation of the variable + */ + public static function varDump($var,$depth=10,$highlight=false) + { + Prado::using('System.Util.TVarDumper'); + return TVarDumper::dump($var,$depth,$highlight); + } + + /** + * Localize a text to the locale/culture specified in the globalization handler. + * @param string text to be localized. + * @param array a set of parameters to substitute. + * @param string a different catalogue to find the localize text. + * @param string the input AND output charset. + * @return string localized text. + * @see TTranslate::formatter() + * @see TTranslate::init() + */ + public static function localize($text, $parameters=array(), $catalogue=null, $charset=null) + { + Prado::using('System.I18N.Translation'); + $app = Prado::getApplication()->getGlobalization(false); + + $params = array(); + foreach($parameters as $key => $value) + $params['{'.$key.'}'] = $value; + + //no translation handler provided + if($app===null || ($config = $app->getTranslationConfiguration())===null) + return strtr($text, $params); + + if ($catalogue===null) + $catalogue=isset($config['catalogue'])?$config['catalogue']:'messages'; + + Translation::init($catalogue); + + //globalization charset + $appCharset = $app===null ? '' : $app->getCharset(); + + //default charset + $defaultCharset = ($app===null) ? 'UTF-8' : $app->getDefaultCharset(); + + //fall back + if(empty($charset)) $charset = $appCharset; + if(empty($charset)) $charset = $defaultCharset; + + return Translation::formatter($catalogue)->format($text,$params,$catalogue,$charset); + } +} + +/** + * TReflectionClass class. + * This class was originally written to cope with the incompatibility between different PHP versions. + * It is equivalent to ReflectionClass for PHP version >= 5.1.0 + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id$ + * @package System + * @since 3.0 + */ +class TReflectionClass extends ReflectionClass +{ +} + +/** + * Includes the classes essential for PradoBase class + */ +PradoBase::using('System.TComponent'); +PradoBase::using('System.Exceptions.TException'); +PradoBase::using('System.Util.TLogger'); + +?> diff --git a/framework/Security/TUserManager.php b/framework/Security/TUserManager.php index 878ca864..1a99bfcb 100644 --- a/framework/Security/TUserManager.php +++ b/framework/Security/TUserManager.php @@ -4,7 +4,7 @@ *
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2008 PradoSoft
+ * @copyright Copyright © 2005-2008 PradoSoft * @license http://www.pradosoft.com/license/
* @version $Id$
* @package System.Security
@@ -181,7 +181,7 @@ class TUserManager extends TModule implements IUserManager * Loads user/role information from an XML node.
* @param TXmlElement the XML node containing the user information
*/
- protected function loadUserDataFromXml($xmlNode)
+ private function loadUserDataFromXml($xmlNode)
{
foreach($xmlNode->getElementsByTagName('user') as $node)
{
@@ -400,4 +400,3 @@ class TUserManagerPasswordMode extends TEnumerable const SHA1='SHA1';
}
-?>
diff --git a/framework/Util/TLogRouter.php b/framework/Util/TLogRouter.php index c4b7157e..cc736fae 100644 --- a/framework/Util/TLogRouter.php +++ b/framework/Util/TLogRouter.php @@ -663,14 +663,14 @@ class TBrowserLogRoute extends TLogRoute protected function renderHeader()
{
$string = <<<EOD
-<table cellspacing="0" cellpadding="2" border="0" width="100%">
+<table cellspacing="0" cellpadding="2" border="0" width="100%" style="table-layout:auto">
<tr>
- <th style="background-color: black; color:white;" colspan="11">
+ <th style="background-color: black; color:white;" colspan="5">
Application Log
</th>
- </tr><tr style="background-color: #ccc;">
- <th> </th>
- <th>Category</th><th>Message</th><th>Time Spent (s)</th><th>Cumulated Time Spent (s)</th>
+ </tr><tr style="background-color: #ccc; color:black">
+ <th style="width: 15px"> </th>
+ <th style="width: auto">Category</th><th style="width: auto">Message</th><th style="width: 120px">Time Spent (s)</th><th style="width: 150px">Cumulated Time Spent (s)</th>
</tr>
EOD;
return $string;
@@ -685,7 +685,7 @@ EOD; $msg = preg_replace('/\(line[^\)]+\)$/','',$log[0]); //remove line number info
$msg = THttpUtility::htmlEncode($msg);
$string = <<<EOD
- <tr style="background-color: {$bgcolor};">
+ <tr style="background-color: {$bgcolor}; color:#000">
<td style="border:1px solid silver;background-color: $color;"> </td>
<td>{$log[2]}</td>
<td>{$msg}</td>
@@ -713,11 +713,11 @@ EOD; protected function renderFooter()
{
- $string = "<tr><td colspan=\"11\" style=\"text-align:center; border-top: 1px solid #ccc; padding:0.2em;\">";
+ $string = "<tr><td colspan=\"5\" style=\"text-align:center; background-color:black; border-top: 1px solid #ccc; padding:0.2em;\">";
foreach(self::$_levelValues as $name => $level)
{
- $string .= "<span style=\"color:white;background-color:".$this->getColorLevel($level);
- $string .= ";margin: 0.5em;\">".strtoupper($name)."</span>";
+ $string .= "<span style=\"color:white; border:1px solid white; background-color:".$this->getColorLevel($level);
+ $string .= ";margin: 0.5em; padding:0.01em;\">".strtoupper($name)."</span>";
}
$string .= "</td></tr></table>";
return $string;
@@ -795,7 +795,7 @@ class TDbLogRoute extends TLogRoute $sql='SELECT * FROM '.$this->_logTable.' WHERE 0';
try
{
- $db->createCommand($sql)->execute();
+ $db->createCommand($sql)->query()->close();
}
catch(Exception $e)
{
@@ -819,26 +819,33 @@ class TDbLogRoute extends TLogRoute $command=$this->getDbConnection()->createCommand($sql);
foreach($logs as $log)
{
- $command->bindValue(':level',$log[0]);
- $command->bindValue(':category',$log[1]);
- $command->bindValue(':logtime',$log[2]);
- $command->bindValue(':message',$log[3]);
+ $command->bindValue(':message',$log[0]);
+ $command->bindValue(':level',$log[1]);
+ $command->bindValue(':category',$log[2]);
+ $command->bindValue(':logtime',$log[3]);
$command->execute();
}
}
/**
* Creates the DB table for storing log messages.
+ * @todo create sequence for PostgreSQL
*/
protected function createDbTable()
{
+ $db = $this->getDbConnection();
+ $driver=$db->getDriverName();
+ $autoidAttributes = '';
+ if($driver==='mysql')
+ $autoidAttributes = 'AUTO_INCREMENT';
+
$sql='CREATE TABLE '.$this->_logTable.' (
- log_id INTEGER NOT NULL PRIMARY KEY,
+ log_id INTEGER NOT NULL PRIMARY KEY ' . $autoidAttributes . ',
level INTEGER,
category VARCHAR(128),
logtime VARCHAR(20),
message VARCHAR(255))';
- $this->getDbConnection()->createCommand($sql)->execute();
+ $db->createCommand($sql)->execute();
}
/**
@@ -1012,4 +1019,101 @@ EOD; }
}
-?>
+
+/**
+ * TFirePhpLogRoute class.
+ *
+ * TFirePhpLogRoute prints log messages in the firebug log console via firephp.
+ *
+ * {@link http://www.getfirebug.com/ FireBug Website}
+ * {@link http://www.firephp.org/ FirePHP Website}
+ *
+ * @author Yves Berkholz <godzilla80[at]gmx[dot]net>
+ * @version $Id$
+ * @package System.Util
+ * @since 3.1.5
+ */
+class TFirePhpLogRoute extends TLogRoute
+{
+ /**
+ * Default group label
+ */
+ const DEFAULT_LABEL = 'System.Util.TLogRouter(TFirePhpLogRoute)';
+
+ private $_groupLabel = null;
+
+ public function processLogs($logs)
+ {
+ if(empty($logs) || $this->getApplication()->getMode()==='Performance') return;
+
+ require_once Prado::getPathOfNamespace('System.3rdParty.FirePHPCore') . '/FirePHP.class.php';
+ $firephp = FirePHP::getInstance(true);
+ $firephp -> setOptions(array('useNativeJsonEncode' => false));
+
+ $firephp -> group($this->getGroupLabel(), array('Collapsed' => true));
+
+ $firephp ->log('Time, Message');
+
+ $first = $logs[0][3];
+ $c = count($logs);
+ for($i=0,$n=$c;$i<$n;++$i)
+ {
+ $message = $logs[$i][0];
+ $level = $logs[$i][1];
+ $category = $logs[$i][2];
+
+ if ($i<$n-1)
+ {
+ $delta = $logs[$i+1][3] - $logs[$i][3];
+ $total = $logs[$i+1][3] - $first;
+ }
+ else
+ {
+ $delta = '?';
+ $total = $logs[$i][3] - $first;
+ }
+
+ $message = sPrintF('+%0.6f: %s', $delta, preg_replace('/\(line[^\)]+\)$/','',$message));
+ $firephp ->fb($message, $category, self::translateLogLevel($level));
+ }
+ $firephp ->log( sPrintF('%0.6f', $total), 'Cumulated Time');
+ $firephp -> groupEnd();
+ }
+
+ protected static function translateLogLevel($level)
+ {
+ switch($level)
+ {
+ case TLogger::INFO:
+ return FirePHP::INFO;
+ case TLogger::DEBUG:
+ case TLogger::NOTICE:
+ return FirePHP::LOG;
+ case TLogger::WARNING:
+ return FirePHP::WARN;
+ case TLogger::ERROR:
+ case TLogger::ALERT:
+ case TLogger::FATAL:
+ return FirePHP::ERROR;
+ }
+ return FirePHP::LOG;
+ }
+
+ /**
+ * @return string group label. Defaults to TFirePhpLogRoute::DEFAULT_LABEL
+ */
+ public function getGroupLabel()
+ {
+ if($this->_groupLabel===null)
+ $this->_groupLabel=self::DEFAULT_LABEL;
+ return $this->_groupLabel;
+ }
+
+ /**
+ * @param string group label.
+ */
+ public function setGroupLabel($value)
+ {
+ $this->_groupLabel=$value;
+ }
+}
\ No newline at end of file diff --git a/framework/Web/Javascripts/TJSON.php b/framework/Web/Javascripts/TJSON.php index a52a0634..e0d31b05 100644 --- a/framework/Web/Javascripts/TJSON.php +++ b/framework/Web/Javascripts/TJSON.php @@ -146,10 +146,10 @@ class TJSON return (float) $var;
case 'string':
- if (($g=Prado::getApplication()->getGlobalization(false))!==null &&
+ if (($g=Prado::getApplication()->getGlobalization(false))!==null &&
strtoupper($enc=$g->getCharset())!='UTF-8')
$var=iconv($enc, 'UTF-8', $var);
-
+
// STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
$ascii = '';
$strlen_var = strlen($var);
@@ -759,4 +759,4 @@ class TJSON }
-?>
+?>
\ No newline at end of file diff --git a/framework/Web/Javascripts/TJavaScript.php b/framework/Web/Javascripts/TJavaScript.php index 9c4741a4..2134c2d1 100644 --- a/framework/Web/Javascripts/TJavaScript.php +++ b/framework/Web/Javascripts/TJavaScript.php @@ -4,7 +4,7 @@ *
* @author Wei Zhuo<weizhuo[at]gmail[dot]com>
* @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2008 PradoSoft + * @copyright Copyright © 2005-2008 PradoSoft
* @license http://www.pradosoft.com/license/
* @version $Id$
* @package System.Web.Javascripts
@@ -130,9 +130,11 @@ class TJavaScript *
* @param mixed PHP variable to be encoded
* @param boolean whether the output is a map or a list.
+ * @since 3.1.5
+ * @param boolean wether to encode empty strings too. Default to false for BC.
* @return string the encoded string
*/
- public static function encode($value,$toMap=true)
+ public static function encode($value,$toMap=true,$encodeEmptyStrings=false)
{
if(is_string($value))
{
@@ -158,11 +160,11 @@ class TJavaScript {
foreach($value as $k=>$v)
{
- if($v!=='')
+ if($v!=='' || $encodeEmptyStrings)
{
if($results!=='')
$results.=',';
- $results.="'$k':".self::encode($v,$toMap);
+ $results.="'$k':".self::encode($v,$toMap,$encodeEmptyStrings);
}
}
return '{'.$results.'}';
@@ -171,11 +173,11 @@ class TJavaScript {
foreach($value as $v)
{
- if($v!=='')
+ if($v!=='' || $encodeEmptyStrings)
{
if($results!=='')
$results.=',';
- $results.=self::encode($v,$toMap);
+ $results.=self::encode($v,$toMap, $encodeEmptyStrings);
}
}
return '['.$results.']';
diff --git a/framework/Web/Javascripts/clientscripts.php b/framework/Web/Javascripts/clientscripts.php index 046ae627..1eed9e42 100644 --- a/framework/Web/Javascripts/clientscripts.php +++ b/framework/Web/Javascripts/clientscripts.php @@ -47,9 +47,9 @@ */
//run script for as long as needed
-set_time_limit(0);
+@set_time_limit(0);
-//set error_reporting directive
+//set error_reporting directive
@error_reporting(E_ERROR | E_WARNING | E_PARSE);
function get_client_script_files()
@@ -292,9 +292,9 @@ function supports_gzip_encoding() * @package JSMin
* @author Ryan Grove <ryan@wonko.com>
* @copyright 2002 Douglas Crockford <douglas@crockford.com> (jsmin.c)
- * @copyright 2007 Ryan Grove <ryan@wonko.com> (PHP port)
+ * @copyright 2008 Ryan Grove <ryan@wonko.com> (PHP port)
* @license http://opensource.org/licenses/mit-license.php MIT License
- * @version 1.1.0 (2007-06-01)
+ * @version 1.1.1 (2008-03-02)
* @link http://code.google.com/p/jsmin-php/
*/
@@ -308,7 +308,7 @@ class JSMin { protected $inputIndex = 0;
protected $inputLength = 0;
protected $lookAhead = null;
- protected $output = array();
+ protected $output = '';
// -- Public Static Methods --------------------------------------------------
@@ -329,15 +329,15 @@ class JSMin { protected function action($d) {
switch($d) {
case 1:
- $this->output[] = $this->a;
+ $this->output .= $this->a;
case 2:
$this->a = $this->b;
if ($this->a === "'" || $this->a === '"') {
for (;;) {
- $this->output[] = $this->a;
- $this->a = $this->get();
+ $this->output .= $this->a;
+ $this->a = $this->get();
if ($this->a === $this->b) {
break;
@@ -348,8 +348,8 @@ class JSMin { }
if ($this->a === '\\') {
- $this->output[] = $this->a;
- $this->a = $this->get();
+ $this->output .= $this->a;
+ $this->a = $this->get();
}
}
}
@@ -362,25 +362,22 @@ class JSMin { $this->a === ':' || $this->a === '[' || $this->a === '!' ||
$this->a === '&' || $this->a === '|' || $this->a === '?')) {
- $this->output[] = $this->a;
- $this->output[] = $this->b;
+ $this->output .= $this->a . $this->b;
for (;;) {
$this->a = $this->get();
if ($this->a === '/') {
break;
- }
- elseif ($this->a === '\\') {
- $this->output[] = $this->a;
- $this->a = $this->get();
- }
- elseif (ord($this->a) <= self::ORD_LF) {
+ } elseif ($this->a === '\\') {
+ $this->output .= $this->a;
+ $this->a = $this->get();
+ } elseif (ord($this->a) <= self::ORD_LF) {
throw new JSMinException('Unterminated regular expression '.
'literal.');
}
- $this->output[] = $this->a;
+ $this->output .= $this->a;
}
$this->b = $this->next();
@@ -396,8 +393,7 @@ class JSMin { if ($this->inputIndex < $this->inputLength) {
$c = $this->input[$this->inputIndex];
$this->inputIndex += 1;
- }
- else {
+ } else {
$c = null;
}
}
@@ -426,8 +422,7 @@ class JSMin { case ' ':
if ($this->isAlphaNum($this->b)) {
$this->action(1);
- }
- else {
+ } else {
$this->action(2);
}
break;
@@ -496,7 +491,7 @@ class JSMin { }
}
- return implode('', $this->output);
+ return $this->output;
}
protected function next() {
diff --git a/framework/Web/Javascripts/source/packages.php b/framework/Web/Javascripts/source/packages.php index d6c04e7f..b9b4c7bf 100644 --- a/framework/Web/Javascripts/source/packages.php +++ b/framework/Web/Javascripts/source/packages.php @@ -1,15 +1,15 @@ <?php
//$Id$
- -// To make future upgrades easier -if (!defined('PROTOTYPE_DIR')) define ('PROTOTYPE_DIR', 'prototype-1.6.0.2'); -if (!defined('SCRIPTACULOUS_DIR')) define ('SCRIPTACULOUS_DIR', 'scriptaculous-1.8.1'); +
+// To make future upgrades easier
+if (!defined('PROTOTYPE_DIR')) define ('PROTOTYPE_DIR', 'prototype-1.6.0.3');
+if (!defined('SCRIPTACULOUS_DIR')) define ('SCRIPTACULOUS_DIR', 'scriptaculous-1.8.2');
//package names and its contents (files relative to the current directory)
$packages = array(
'prado' => array(
- PROTOTYPE_DIR.'/prototype.js', + PROTOTYPE_DIR.'/prototype.js',
SCRIPTACULOUS_DIR.'/builder.js',
'prado/prado.js',
'prado/scriptaculous-adapter.js',
@@ -42,12 +42,11 @@ $packages = array( 'prado/activecontrols/json.js',
'prado/activecontrols/ajax3.js',
'prado/activecontrols/activecontrols3.js',
- 'prado/activecontrols/inlineeditor.js',
- 'prado/activeratings/ratings.js'
+ 'prado/activecontrols/inlineeditor.js'
),
'dragdrop'=>array(
- SCRIPTACULOUS_DIR.'/dragdrop.js', + SCRIPTACULOUS_DIR.'/dragdrop.js',
'prado/activecontrols/dragdrop.js'
),
@@ -61,14 +60,14 @@ $packages = array( 'tabpanel'=>array(
'prado/controls/tabpanel.js'
- ), - - 'activedatepicker' => array( - 'prado/activecontrols/activedatepicker.js' - ), - - 'activefileupload' => array( - 'prado/activefileupload/activefileupload.js' + ),
+
+ 'activedatepicker' => array(
+ 'prado/activecontrols/activedatepicker.js'
+ ),
+
+ 'activefileupload' => array(
+ 'prado/activefileupload/activefileupload.js'
),
);
@@ -86,8 +85,8 @@ $dependencies = array( 'dragdrop' => array('prado', 'effects', 'ajax', 'dragdrop'),
'slider' => array('prado', 'slider'),
'keyboard' => array('prado', 'keyboard'),
- 'tabpanel' => array('prado', 'tabpanel'), - 'activedatepicker' => array('datepicker', 'ajax', 'activedatepicker'), + 'tabpanel' => array('prado', 'tabpanel'),
+ 'activedatepicker' => array('datepicker', 'ajax', 'activedatepicker'),
'activefileupload' => array('prado', 'ajax', 'activefileupload'),
);
diff --git a/framework/Web/Javascripts/source/prado/activecontrols/activecontrols3.js b/framework/Web/Javascripts/source/prado/activecontrols/activecontrols3.js index d5cae7b8..5402c4e7 100644 --- a/framework/Web/Javascripts/source/prado/activecontrols/activecontrols3.js +++ b/framework/Web/Javascripts/source/prado/activecontrols/activecontrols3.js @@ -1,387 +1,391 @@ -/** - * Generic postback control. - */ -Prado.WebUI.CallbackControl = Class.extend(Prado.WebUI.PostBackControl, -{ - onPostBack : function(event, options) - { - var request = new Prado.CallbackRequest(options.EventTarget, options); - request.dispatch(); - Event.stop(event); - } -}); - -/** - * TActiveButton control. - */ -Prado.WebUI.TActiveButton = Class.extend(Prado.WebUI.CallbackControl); -/** - * TActiveLinkButton control. - */ -Prado.WebUI.TActiveLinkButton = Class.extend(Prado.WebUI.CallbackControl); - -Prado.WebUI.TActiveImageButton = Class.extend(Prado.WebUI.TImageButton, -{ - onPostBack : function(event, options) - { - this.addXYInput(event,options); - var request = new Prado.CallbackRequest(options.EventTarget, options); - request.dispatch(); - Event.stop(event); - this.removeXYInput(event,options); - } -}); -/** - * Active check box. - */ -Prado.WebUI.TActiveCheckBox = Class.extend(Prado.WebUI.CallbackControl, -{ - onPostBack : function(event, options) - { - var request = new Prado.CallbackRequest(options.EventTarget, options); - if(request.dispatch()==false) - Event.stop(event); - } -}); - -/** - * TActiveRadioButton control. - */ -Prado.WebUI.TActiveRadioButton = Class.extend(Prado.WebUI.TActiveCheckBox); - - -Prado.WebUI.TActiveCheckBoxList = Base.extend( -{ - constructor : function(options) - { - for(var i = 0; i<options.ItemCount; i++) - { - var checkBoxOptions = Object.extend( - { - ID : options.ListID+"_c"+i, - EventTarget : options.ListName+"$c"+i - }, options); - new Prado.WebUI.TActiveCheckBox(checkBoxOptions); - } - } -}); - -Prado.WebUI.TActiveRadioButtonList = Prado.WebUI.TActiveCheckBoxList; - -/** - * TActiveTextBox control, handles onchange event. - */ -Prado.WebUI.TActiveTextBox = Class.extend(Prado.WebUI.TTextBox, -{ - onInit : function(options) - { - this.options=options; - if(options['TextMode'] != 'MultiLine') - Event.observe(this.element, "keydown", this.handleReturnKey.bind(this)); - if(this.options['AutoPostBack']==true) - Event.observe(this.element, "change", this.doCallback.bindEvent(this,options)); - }, - - doCallback : function(event, options) - { - var request = new Prado.CallbackRequest(options.EventTarget, options); - request.dispatch(); - if (!Prototype.Browser.IE) - Event.stop(event); - } -}); - -/** - * TAutoComplete control. - */ -Prado.WebUI.TAutoComplete = Class.extend(Autocompleter.Base, Prado.WebUI.TActiveTextBox.prototype); -Prado.WebUI.TAutoComplete = Class.extend(Prado.WebUI.TAutoComplete, -{ - initialize : function(options) - { - this.options = options; - this.hasResults = false; - this.baseInitialize(options.ID, options.ResultPanel, options); - Object.extend(this.options, - { - onSuccess : this.onComplete.bind(this) - }); - - if(options.AutoPostBack) - this.onInit(options); - }, - - doCallback : function(event, options) - { - if(!this.active) - { - var request = new Prado.CallbackRequest(this.options.EventTarget, options); - request.dispatch(); - Event.stop(event); - } - }, - - //Overrides parent implementation, fires onchange event. - onClick: function(event) - { - var element = Event.findElement(event, 'LI'); - this.index = element.autocompleteIndex; - this.selectEntry(); - this.hide(); - Event.fireEvent(this.element, "change"); - }, - - getUpdatedChoices : function() - { - var options = new Array(this.getToken(),"__TAutoComplete_onSuggest__"); - Prado.Callback(this.options.EventTarget, options, null, this.options); - }, - - /** - * Overrides parent implements, don't update if no results. - */ - selectEntry: function() - { - if(this.hasResults) - { - this.active = false; - this.updateElement(this.getCurrentEntry()); - var options = [this.index, "__TAutoComplete_onSuggestionSelected__"]; - Prado.Callback(this.options.EventTarget, options, null, this.options); - } - }, - - onComplete : function(request, boundary) - { - var result = Prado.Element.extractContent(request.transport.responseText, boundary); - if(typeof(result) == "string") - { - if(result.length > 0) - { - this.hasResults = true; - this.updateChoices(result); - } - else - { - this.active = false; - this.hasResults = false; - this.hide(); - } - } - } -}); - -/** - * Time Triggered Callback class. - */ -Prado.WebUI.TTimeTriggeredCallback = Base.extend( -{ - constructor : function(options) - { - this.options = Object.extend({ Interval : 1 }, options || {}); - Prado.WebUI.TTimeTriggeredCallback.register(this); - }, - - startTimer : function() - { - setTimeout(this.onTimerEvent.bind(this), 100); - if(typeof(this.timer) == 'undefined' || this.timer == null) - this.timer = setInterval(this.onTimerEvent.bind(this),this.options.Interval*1000); - }, - - stopTimer : function() - { - if(typeof(this.timer) != 'undefined') - { - clearInterval(this.timer); - this.timer = null; - } - }, - - resetTimer : function() - { - if(typeof(this.timer) != 'undefined') - { - clearInterval(this.timer); - this.timer = null; - this.timer = setInterval(this.onTimerEvent.bind(this),this.options.Interval*1000); - } - }, - - onTimerEvent : function() - { - var request = new Prado.CallbackRequest(this.options.EventTarget, this.options); - request.dispatch(); - }, - - setInterval : function(value) - { - if (this.options.Interval != value){ - this.options.Interval = value; - this.resetTimer(); - } - } -}, -//class methods -{ - timers : {}, - - register : function(timer) - { - Prado.WebUI.TTimeTriggeredCallback.timers[timer.options.ID] = timer; - }, - - start : function(id) - { - if(Prado.WebUI.TTimeTriggeredCallback.timers[id]) - Prado.WebUI.TTimeTriggeredCallback.timers[id].startTimer(); - }, - - stop : function(id) - { - if(Prado.WebUI.TTimeTriggeredCallback.timers[id]) - Prado.WebUI.TTimeTriggeredCallback.timers[id].stopTimer(); - }, - - setInterval : function (id,value) - { - if(Prado.WebUI.TTimeTriggeredCallback.timers[id]) - Prado.WebUI.TTimeTriggeredCallback.timers[id].setInterval(value); - } -}); - -Prado.WebUI.ActiveListControl = Base.extend( -{ - constructor : function(options) - { - this.element = $(options.ID); - if(this.element) - { - this.options = options; - Event.observe(this.element, "change", this.doCallback.bind(this)); - } - }, - - doCallback : function(event) - { - var request = new Prado.CallbackRequest(this.options.EventTarget, this.options); - request.dispatch(); - Event.stop(event); - } -}); - -Prado.WebUI.TActiveDropDownList = Prado.WebUI.ActiveListControl; -Prado.WebUI.TActiveListBox = Prado.WebUI.ActiveListControl; - -/** - * Observe event of a particular control to trigger a callback request. - */ -Prado.WebUI.TEventTriggeredCallback = Base.extend( -{ - constructor : function(options) - { - this.options = options; - var element = $(options['ControlID']); - if(element) - Event.observe(element, this.getEventName(element), this.doCallback.bind(this)); - }, - - getEventName : function(element) - { - var name = this.options.EventName; - if(typeof(name) == "undefined" && element.type) - { - switch (element.type.toLowerCase()) - { - case 'password': - case 'text': - case 'textarea': - case 'select-one': - case 'select-multiple': - return 'change'; - } - } - return typeof(name) == "undefined" || name == "undefined" ? 'click' : name; - }, - - doCallback : function(event) - { - var request = new Prado.CallbackRequest(this.options.EventTarget, this.options); - request.dispatch(); - if(this.options.StopEvent == true) - Event.stop(event); - } -}); - -/** - * Observe changes to a property of a particular control to trigger a callback. - */ -Prado.WebUI.TValueTriggeredCallback = Base.extend( -{ - count : 1, - - observing : true, - - constructor : function(options) - { - this.options = options; - this.options.PropertyName = this.options.PropertyName || 'value'; - var element = $(options['ControlID']); - this.value = element ? element[this.options.PropertyName] : undefined; - Prado.WebUI.TValueTriggeredCallback.register(this); - this.startObserving(); - }, - - stopObserving : function() - { - clearTimeout(this.timer); - this.observing = false; - }, - - startObserving : function() - { - this.timer = setTimeout(this.checkChanges.bind(this), this.options.Interval*1000); - }, - - checkChanges : function() - { - var element = $(this.options.ControlID); - if(element) - { - var value = element[this.options.PropertyName]; - if(this.value != value) - { - this.doCallback(this.value, value); - this.value = value; - this.count=1; - } - else - this.count = this.count + this.options.Decay; - if(this.observing) - this.time = setTimeout(this.checkChanges.bind(this), - parseInt(this.options.Interval*1000*this.count)); - } - }, - - doCallback : function(oldValue, newValue) - { - var request = new Prado.CallbackRequest(this.options.EventTarget, this.options); - var param = {'OldValue' : oldValue, 'NewValue' : newValue}; - request.setCallbackParameter(param); - request.dispatch(); - } -}, -//class methods -{ - timers : {}, - - register : function(timer) - { - Prado.WebUI.TValueTriggeredCallback.timers[timer.options.ID] = timer; - }, - - stop : function(id) - { - Prado.WebUI.TValueTriggeredCallback.timers[id].stopObserving(); - } -}); +/**
+ * Generic postback control.
+ */
+Prado.WebUI.CallbackControl = Class.extend(Prado.WebUI.PostBackControl,
+{
+ onPostBack : function(event, options)
+ {
+ var request = new Prado.CallbackRequest(options.EventTarget, options);
+ request.dispatch();
+ Event.stop(event);
+ }
+});
+
+/**
+ * TActiveButton control.
+ */
+Prado.WebUI.TActiveButton = Class.extend(Prado.WebUI.CallbackControl);
+/**
+ * TActiveLinkButton control.
+ */
+Prado.WebUI.TActiveLinkButton = Class.extend(Prado.WebUI.CallbackControl);
+
+Prado.WebUI.TActiveImageButton = Class.extend(Prado.WebUI.TImageButton,
+{
+ onPostBack : function(event, options)
+ {
+ this.addXYInput(event,options);
+ var request = new Prado.CallbackRequest(options.EventTarget, options);
+ request.dispatch();
+ Event.stop(event);
+ this.removeXYInput(event,options);
+ }
+});
+/**
+ * Active check box.
+ */
+Prado.WebUI.TActiveCheckBox = Class.extend(Prado.WebUI.CallbackControl,
+{
+ onPostBack : function(event, options)
+ {
+ var request = new Prado.CallbackRequest(options.EventTarget, options);
+ if(request.dispatch()==false)
+ Event.stop(event);
+ }
+});
+
+/**
+ * TActiveRadioButton control.
+ */
+Prado.WebUI.TActiveRadioButton = Class.extend(Prado.WebUI.TActiveCheckBox);
+
+
+Prado.WebUI.TActiveCheckBoxList = Base.extend(
+{
+ constructor : function(options)
+ {
+ Prado.Registry.set(options.ListID, this);
+ for(var i = 0; i<options.ItemCount; i++)
+ {
+ var checkBoxOptions = Object.extend(
+ {
+ ID : options.ListID+"_c"+i,
+ EventTarget : options.ListName+"$c"+i
+ }, options);
+ new Prado.WebUI.TActiveCheckBox(checkBoxOptions);
+ }
+ }
+});
+
+Prado.WebUI.TActiveRadioButtonList = Prado.WebUI.TActiveCheckBoxList;
+
+/**
+ * TActiveTextBox control, handles onchange event.
+ */
+Prado.WebUI.TActiveTextBox = Class.extend(Prado.WebUI.TTextBox,
+{
+ onInit : function(options)
+ {
+ this.options=options;
+ if(options['TextMode'] != 'MultiLine')
+ Event.observe(this.element, "keydown", this.handleReturnKey.bind(this));
+ if(this.options['AutoPostBack']==true)
+ Event.observe(this.element, "change", this.doCallback.bindEvent(this,options));
+ },
+
+ doCallback : function(event, options)
+ {
+ var request = new Prado.CallbackRequest(options.EventTarget, options);
+ request.dispatch();
+ if (!Prototype.Browser.IE)
+ Event.stop(event);
+ }
+});
+
+/**
+ * TAutoComplete control.
+ */
+Prado.WebUI.TAutoComplete = Class.extend(Autocompleter.Base, Prado.WebUI.TActiveTextBox.prototype);
+Prado.WebUI.TAutoComplete = Class.extend(Prado.WebUI.TAutoComplete,
+{
+ initialize : function(options)
+ {
+ this.options = options;
+ this.hasResults = false;
+ this.baseInitialize(options.ID, options.ResultPanel, options);
+ Object.extend(this.options,
+ {
+ onSuccess : this.onComplete.bind(this)
+ });
+
+ if(options.AutoPostBack)
+ this.onInit(options);
+ },
+
+ doCallback : function(event, options)
+ {
+ if(!this.active)
+ {
+ var request = new Prado.CallbackRequest(this.options.EventTarget, options);
+ request.dispatch();
+ Event.stop(event);
+ }
+ },
+
+ //Overrides parent implementation, fires onchange event.
+ onClick: function(event)
+ {
+ var element = Event.findElement(event, 'LI');
+ this.index = element.autocompleteIndex;
+ this.selectEntry();
+ this.hide();
+ Event.fireEvent(this.element, "change");
+ },
+
+ getUpdatedChoices : function()
+ {
+ var options = new Array(this.getToken(),"__TAutoComplete_onSuggest__");
+ Prado.Callback(this.options.EventTarget, options, null, this.options);
+ },
+
+ /**
+ * Overrides parent implements, don't update if no results.
+ */
+ selectEntry: function()
+ {
+ if(this.hasResults)
+ {
+ this.active = false;
+ this.updateElement(this.getCurrentEntry());
+ var options = [this.index, "__TAutoComplete_onSuggestionSelected__"];
+ Prado.Callback(this.options.EventTarget, options, null, this.options);
+ }
+ },
+
+ onComplete : function(request, boundary)
+ {
+ var result = Prado.Element.extractContent(request.transport.responseText, boundary);
+ if(typeof(result) == "string")
+ {
+ if(result.length > 0)
+ {
+ this.hasResults = true;
+ this.updateChoices(result);
+ }
+ else
+ {
+ this.active = false;
+ this.hasResults = false;
+ this.hide();
+ }
+ }
+ }
+});
+
+/**
+ * Time Triggered Callback class.
+ */
+Prado.WebUI.TTimeTriggeredCallback = Base.extend(
+{
+ constructor : function(options)
+ {
+ this.options = Object.extend({ Interval : 1 }, options || {});
+ Prado.WebUI.TTimeTriggeredCallback.register(this);
+ Prado.Registry.set(options.ID, this);
+ },
+
+ startTimer : function()
+ {
+ setTimeout(this.onTimerEvent.bind(this), 100);
+ if(typeof(this.timer) == 'undefined' || this.timer == null)
+ this.timer = setInterval(this.onTimerEvent.bind(this),this.options.Interval*1000);
+ },
+
+ stopTimer : function()
+ {
+ if(typeof(this.timer) != 'undefined')
+ {
+ clearInterval(this.timer);
+ this.timer = null;
+ }
+ },
+
+ resetTimer : function()
+ {
+ if(typeof(this.timer) != 'undefined')
+ {
+ clearInterval(this.timer);
+ this.timer = null;
+ this.timer = setInterval(this.onTimerEvent.bind(this),this.options.Interval*1000);
+ }
+ },
+
+ onTimerEvent : function()
+ {
+ var request = new Prado.CallbackRequest(this.options.EventTarget, this.options);
+ request.dispatch();
+ },
+
+ setInterval : function(value)
+ {
+ if (this.options.Interval != value){
+ this.options.Interval = value;
+ this.resetTimer();
+ }
+ }
+},
+//class methods
+{
+ timers : {},
+
+ register : function(timer)
+ {
+ Prado.WebUI.TTimeTriggeredCallback.timers[timer.options.ID] = timer;
+ },
+
+ start : function(id)
+ {
+ if(Prado.WebUI.TTimeTriggeredCallback.timers[id])
+ Prado.WebUI.TTimeTriggeredCallback.timers[id].startTimer();
+ },
+
+ stop : function(id)
+ {
+ if(Prado.WebUI.TTimeTriggeredCallback.timers[id])
+ Prado.WebUI.TTimeTriggeredCallback.timers[id].stopTimer();
+ },
+
+ setInterval : function (id,value)
+ {
+ if(Prado.WebUI.TTimeTriggeredCallback.timers[id])
+ Prado.WebUI.TTimeTriggeredCallback.timers[id].setInterval(value);
+ }
+});
+
+Prado.WebUI.ActiveListControl = Base.extend(
+{
+ constructor : function(options)
+ {
+ this.element = $(options.ID);
+ Prado.Registry.set(options.ID, this);
+ if(this.element)
+ {
+ this.options = options;
+ Event.observe(this.element, "change", this.doCallback.bind(this));
+ }
+ },
+
+ doCallback : function(event)
+ {
+ var request = new Prado.CallbackRequest(this.options.EventTarget, this.options);
+ request.dispatch();
+ Event.stop(event);
+ }
+});
+
+Prado.WebUI.TActiveDropDownList = Prado.WebUI.ActiveListControl;
+Prado.WebUI.TActiveListBox = Prado.WebUI.ActiveListControl;
+
+/**
+ * Observe event of a particular control to trigger a callback request.
+ */
+Prado.WebUI.TEventTriggeredCallback = Base.extend(
+{
+ constructor : function(options)
+ {
+ this.options = options;
+ var element = $(options['ControlID']);
+ if(element)
+ Event.observe(element, this.getEventName(element), this.doCallback.bind(this));
+ },
+
+ getEventName : function(element)
+ {
+ var name = this.options.EventName;
+ if(typeof(name) == "undefined" && element.type)
+ {
+ switch (element.type.toLowerCase())
+ {
+ case 'password':
+ case 'text':
+ case 'textarea':
+ case 'select-one':
+ case 'select-multiple':
+ return 'change';
+ }
+ }
+ return typeof(name) == "undefined" || name == "undefined" ? 'click' : name;
+ },
+
+ doCallback : function(event)
+ {
+ var request = new Prado.CallbackRequest(this.options.EventTarget, this.options);
+ request.dispatch();
+ if(this.options.StopEvent == true)
+ Event.stop(event);
+ }
+});
+
+/**
+ * Observe changes to a property of a particular control to trigger a callback.
+ */
+Prado.WebUI.TValueTriggeredCallback = Base.extend(
+{
+ count : 1,
+
+ observing : true,
+
+ constructor : function(options)
+ {
+ this.options = options;
+ this.options.PropertyName = this.options.PropertyName || 'value';
+ var element = $(options['ControlID']);
+ this.value = element ? element[this.options.PropertyName] : undefined;
+ Prado.WebUI.TValueTriggeredCallback.register(this);
+ Prado.Registry.set(options.ID, this);
+ this.startObserving();
+ },
+
+ stopObserving : function()
+ {
+ clearTimeout(this.timer);
+ this.observing = false;
+ },
+
+ startObserving : function()
+ {
+ this.timer = setTimeout(this.checkChanges.bind(this), this.options.Interval*1000);
+ },
+
+ checkChanges : function()
+ {
+ var element = $(this.options.ControlID);
+ if(element)
+ {
+ var value = element[this.options.PropertyName];
+ if(this.value != value)
+ {
+ this.doCallback(this.value, value);
+ this.value = value;
+ this.count=1;
+ }
+ else
+ this.count = this.count + this.options.Decay;
+ if(this.observing)
+ this.time = setTimeout(this.checkChanges.bind(this),
+ parseInt(this.options.Interval*1000*this.count));
+ }
+ },
+
+ doCallback : function(oldValue, newValue)
+ {
+ var request = new Prado.CallbackRequest(this.options.EventTarget, this.options);
+ var param = {'OldValue' : oldValue, 'NewValue' : newValue};
+ request.setCallbackParameter(param);
+ request.dispatch();
+ }
+},
+//class methods
+{
+ timers : {},
+
+ register : function(timer)
+ {
+ Prado.WebUI.TValueTriggeredCallback.timers[timer.options.ID] = timer;
+ },
+
+ stop : function(id)
+ {
+ Prado.WebUI.TValueTriggeredCallback.timers[id].stopObserving();
+ }
+});
diff --git a/framework/Web/Javascripts/source/prado/activecontrols/activedatepicker.js b/framework/Web/Javascripts/source/prado/activecontrols/activedatepicker.js index 87b48bf3..3aacda21 100755 --- a/framework/Web/Javascripts/source/prado/activecontrols/activedatepicker.js +++ b/framework/Web/Javascripts/source/prado/activecontrols/activedatepicker.js @@ -33,7 +33,8 @@ Prado.WebUI.TActiveDatePicker = Class.extend(Prado.WebUI.TDatePicker, Object.extend(this,options); - Event.observe(this.trigger, triggerEvent, this.show.bindEvent(this)); + if (this.options.ShowCalendar) + Event.observe(this.trigger, triggerEvent, this.show.bindEvent(this)); // Listen to change event if(this.options.InputMode == "TextBox") diff --git a/framework/Web/Javascripts/source/prado/activecontrols/dragdrop.js b/framework/Web/Javascripts/source/prado/activecontrols/dragdrop.js index 0b42afd5..fab7808f 100755 --- a/framework/Web/Javascripts/source/prado/activecontrols/dragdrop.js +++ b/framework/Web/Javascripts/source/prado/activecontrols/dragdrop.js @@ -1,24 +1,25 @@ -/** - * DropContainer control - */ - -Prado.WebUI.DropContainer = Class.extend(Prado.WebUI.CallbackControl); - -Object.extend(Prado.WebUI.DropContainer.prototype, -{ - initialize: function(options) - { - this.options = options; - Object.extend (this.options, - { - onDrop: this.onDrop.bind(this) - }); - - Droppables.add (options.ID, this.options); - }, - - onDrop: function(dragElement, dropElement) - { - Prado.Callback(this.options.EventTarget, dragElement.id, null, this.options); - } -}); +/**
+ * DropContainer control
+ */
+
+Prado.WebUI.DropContainer = Class.extend(Prado.WebUI.CallbackControl);
+
+Object.extend(Prado.WebUI.DropContainer.prototype,
+{
+ initialize: function(options)
+ {
+ this.options = options;
+ Object.extend (this.options,
+ {
+ onDrop: this.onDrop.bind(this)
+ });
+
+ Droppables.add (options.ID, this.options);
+ Prado.Registry.set(options.ID, this);
+ },
+
+ onDrop: function(dragElement, dropElement)
+ {
+ Prado.Callback(this.options.EventTarget, dragElement.id, null, this.options);
+ }
+});
diff --git a/framework/Web/Javascripts/source/prado/activefileupload/activefileupload.js b/framework/Web/Javascripts/source/prado/activefileupload/activefileupload.js index 9f57f912..7a1e0e77 100755 --- a/framework/Web/Javascripts/source/prado/activefileupload/activefileupload.js +++ b/framework/Web/Javascripts/source/prado/activefileupload/activefileupload.js @@ -13,11 +13,15 @@ Prado.WebUI.TActiveFileUpload = Base.extend( this.complete = $(options.completeID); this.error = $(options.errorID); + Prado.Registry.set(options.inputID, this); + // set up events - Event.observe(this.input,"change",this.fileChanged.bind(this)); + if (options.autoPostBack){ + Event.observe(this.input,"change",this.fileChanged.bind(this)); + } }, - fileChanged:function(){ + fileChanged : function(){ // show the upload indicator, and hide the complete and error indicators (if they areSn't already). this.flag.value = '1'; this.complete.style.display = 'none'; @@ -31,7 +35,7 @@ Prado.WebUI.TActiveFileUpload = Base.extend( this.form.target = this.oldtargetID; }, - finishUpload:function(options){ + finishUpload : function(options){ // hide the display indicator. this.flag.value = ''; this.indicator.style.display = 'none'; @@ -56,8 +60,12 @@ Prado.WebUI.TActiveFileUpload = Base.extend( Prado.WebUI.TActiveFileUpload.controls[control.options.ID] = control; }, - onFileUpload: function(options) + onFileUpload : function(options) { Prado.WebUI.TActiveFileUpload.controls[options.clientID].finishUpload(options); + }, + + fileChanged : function(controlID){ + Prado.WebUI.TActiveFileUpload.controls[controlID].fileChanged(); } -});
\ No newline at end of file +}); diff --git a/framework/Web/Javascripts/source/prado/activeratings/blocks.png b/framework/Web/Javascripts/source/prado/activeratings/blocks.png Binary files differdeleted file mode 100644 index 93a5333e..00000000 --- a/framework/Web/Javascripts/source/prado/activeratings/blocks.png +++ /dev/null diff --git a/framework/Web/Javascripts/source/prado/activeratings/blocks_blank.gif b/framework/Web/Javascripts/source/prado/activeratings/blocks_blank.gif Binary files differdeleted file mode 100644 index c0db17c2..00000000 --- a/framework/Web/Javascripts/source/prado/activeratings/blocks_blank.gif +++ /dev/null diff --git a/framework/Web/Javascripts/source/prado/activeratings/blocks_combined.gif b/framework/Web/Javascripts/source/prado/activeratings/blocks_combined.gif Binary files differdeleted file mode 100644 index dfe9da8d..00000000 --- a/framework/Web/Javascripts/source/prado/activeratings/blocks_combined.gif +++ /dev/null diff --git a/framework/Web/Javascripts/source/prado/activeratings/blocks_half.gif b/framework/Web/Javascripts/source/prado/activeratings/blocks_half.gif Binary files differdeleted file mode 100644 index a9e23d7c..00000000 --- a/framework/Web/Javascripts/source/prado/activeratings/blocks_half.gif +++ /dev/null diff --git a/framework/Web/Javascripts/source/prado/activeratings/blocks_selected.gif b/framework/Web/Javascripts/source/prado/activeratings/blocks_selected.gif Binary files differdeleted file mode 100644 index f743d27e..00000000 --- a/framework/Web/Javascripts/source/prado/activeratings/blocks_selected.gif +++ /dev/null diff --git a/framework/Web/Javascripts/source/prado/activeratings/default.png b/framework/Web/Javascripts/source/prado/activeratings/default.png Binary files differdeleted file mode 100644 index a3148ff4..00000000 --- a/framework/Web/Javascripts/source/prado/activeratings/default.png +++ /dev/null diff --git a/framework/Web/Javascripts/source/prado/activeratings/default_blank.gif b/framework/Web/Javascripts/source/prado/activeratings/default_blank.gif Binary files differdeleted file mode 100644 index 0337ad16..00000000 --- a/framework/Web/Javascripts/source/prado/activeratings/default_blank.gif +++ /dev/null diff --git a/framework/Web/Javascripts/source/prado/activeratings/default_combined.gif b/framework/Web/Javascripts/source/prado/activeratings/default_combined.gif Binary files differdeleted file mode 100644 index ddab2e8b..00000000 --- a/framework/Web/Javascripts/source/prado/activeratings/default_combined.gif +++ /dev/null diff --git a/framework/Web/Javascripts/source/prado/activeratings/default_half.gif b/framework/Web/Javascripts/source/prado/activeratings/default_half.gif Binary files differdeleted file mode 100644 index ed214acd..00000000 --- a/framework/Web/Javascripts/source/prado/activeratings/default_half.gif +++ /dev/null diff --git a/framework/Web/Javascripts/source/prado/activeratings/default_selected.gif b/framework/Web/Javascripts/source/prado/activeratings/default_selected.gif Binary files differdeleted file mode 100644 index 98704fad..00000000 --- a/framework/Web/Javascripts/source/prado/activeratings/default_selected.gif +++ /dev/null diff --git a/framework/Web/Javascripts/source/prado/colorpicker/colorpicker.js b/framework/Web/Javascripts/source/prado/colorpicker/colorpicker.js index 66a79922..fd361469 100644 --- a/framework/Web/Javascripts/source/prado/colorpicker/colorpicker.js +++ b/framework/Web/Javascripts/source/prado/colorpicker/colorpicker.js @@ -297,6 +297,8 @@ Object.extend(Prado.WebUI.TColorPicker.prototype, if(options['ShowColorPicker'])
Event.observe(this.button, "click", this._buttonOnClick);
Event.observe(this.input, "change", this.updatePicker.bind(this));
+
+ Prado.Registry.set(options.ID, this);
},
updatePicker : function(e)
diff --git a/framework/Web/Javascripts/source/prado/controls/controls.js b/framework/Web/Javascripts/source/prado/controls/controls.js index dac7a0c0..463b95a7 100644 --- a/framework/Web/Javascripts/source/prado/controls/controls.js +++ b/framework/Web/Javascripts/source/prado/controls/controls.js @@ -9,6 +9,7 @@ Prado.WebUI.PostBackControl.prototype = this._elementOnClick = null, //capture the element's onclick function
this.element = $(options.ID);
+ Prado.Registry.set(options.ID, this);
if(this.element)
{
if(this.onInit)
@@ -270,6 +271,7 @@ Prado.WebUI.TCheckBoxList = Base.extend( {
constructor : function(options)
{
+ Prado.Registry.set(options.ListID, this);
for(var i = 0; i<options.ItemCount; i++)
{
var checkBoxOptions = Object.extend(
@@ -286,6 +288,7 @@ Prado.WebUI.TRadioButtonList = Base.extend( {
constructor : function(options)
{
+ Prado.Registry.set(options.ListID, this);
for(var i = 0; i<options.ItemCount; i++)
{
var radioButtonOptions = Object.extend(
diff --git a/framework/Web/Javascripts/source/prado/controls/keyboard.js b/framework/Web/Javascripts/source/prado/controls/keyboard.js index 978aedb9..b808a46c 100644 --- a/framework/Web/Javascripts/source/prado/controls/keyboard.js +++ b/framework/Web/Javascripts/source/prado/controls/keyboard.js @@ -5,6 +5,7 @@ Prado.WebUI.TKeyboard.prototype = {
this.element = $(options.ID);
this.onInit(options);
+ Prado.Registry.set(options.ID, this);
},
onInit : function(options)
@@ -165,4 +166,4 @@ Prado.WebUI.TKeyboard.prototype = this.forControl.selectionEnd = selectStart + value.length;
}
}
-};
\ No newline at end of file +};
diff --git a/framework/Web/Javascripts/source/prado/controls/tabpanel.js b/framework/Web/Javascripts/source/prado/controls/tabpanel.js index 8033a2ee..c46a5fea 100644 --- a/framework/Web/Javascripts/source/prado/controls/tabpanel.js +++ b/framework/Web/Javascripts/source/prado/controls/tabpanel.js @@ -5,6 +5,7 @@ Prado.WebUI.TTabPanel.prototype = {
this.element = $(options.ID);
this.onInit(options);
+ Prado.Registry.set(options.ID, this);
},
onInit : function(options)
@@ -47,4 +48,4 @@ Prado.WebUI.TTabPanel.prototype = }
}
}
-};
\ No newline at end of file +};
diff --git a/framework/Web/Javascripts/source/prado/datepicker/datepicker.js b/framework/Web/Javascripts/source/prado/datepicker/datepicker.js index a16cc3d6..9236c32e 100644 --- a/framework/Web/Javascripts/source/prado/datepicker/datepicker.js +++ b/framework/Web/Javascripts/source/prado/datepicker/datepicker.js @@ -68,6 +68,8 @@ Prado.WebUI.TDatePicker.prototype = this.minimalDaysInFirstWeek = 4;
this.selectedDate = this.newDate();
this.positionMode = 'Bottom';
+
+ Prado.Registry.set(options.ID, this);
//which element to trigger to show the calendar
if(this.options.Trigger)
@@ -760,4 +762,4 @@ Prado.WebUI.TDatePicker.prototype = }
}
-};
\ No newline at end of file +};
diff --git a/framework/Web/Javascripts/source/prado/prado.js b/framework/Web/Javascripts/source/prado/prado.js index ae96bb6d..9db01b42 100644 --- a/framework/Web/Javascripts/source/prado/prado.js +++ b/framework/Web/Javascripts/source/prado/prado.js @@ -9,6 +9,12 @@ var Prado = * @var Version
*/
Version: '3.1',
+
+ /**
+ * Registry for Prado components
+ * @var Registry
+ */
+ Registry: $H(),
/**
* Returns browser information.
diff --git a/framework/Web/Javascripts/source/prado/ratings/ratings.js b/framework/Web/Javascripts/source/prado/ratings/ratings.js index 8af32bb9..c7322983 100644 --- a/framework/Web/Javascripts/source/prado/ratings/ratings.js +++ b/framework/Web/Javascripts/source/prado/ratings/ratings.js @@ -14,6 +14,7 @@ Prado.WebUI.TRatingList = Base.extend( Prado.WebUI.TRatingList.register(this);
this._init();
+ Prado.Registry.set(options.ListID, this);
this.selectedIndex = options.SelectedIndex;
this.rating = options.Rating;
this.readOnly = options.ReadOnly
diff --git a/framework/Web/Javascripts/source/prado/validator/validation3.js b/framework/Web/Javascripts/source/prado/validator/validation3.js index a3b803d8..850536ab 100644 --- a/framework/Web/Javascripts/source/prado/validator/validation3.js +++ b/framework/Web/Javascripts/source/prado/validator/validation3.js @@ -545,6 +545,7 @@ Prado.WebUI.TValidationSummary.prototype = * @var {element} messages
*/
this.messages = $(options.ID);
+ Prado.Registry.set(options.ID, this);
if(this.messages)
{
/**
@@ -674,7 +675,7 @@ Prado.WebUI.TValidationSummary.prototype = /**
* Return the format parameters for the summary.
* @function {object} ?
- * @param {string} type - Format type: "List", "SingleParagraph" or "BulletList" (default)
+ * @param {string} type - Format type: "List", "SingleParagraph", "HeaderOnly" or "BulletList" (default)
* @return Object with format parameters:
* @... {string} header - Text for header
* @... {string} first - Text to prepend before message list
@@ -690,6 +691,8 @@ Prado.WebUI.TValidationSummary.prototype = return { header : "<br />", first : "", pre : "", post : "<br />", last : ""};
case "SingleParagraph":
return { header : " ", first : "", pre : "", post : " ", last : "<br />"};
+ case "HeaderOnly":
+ return { header : "", first : "<!--", pre : "", post : "", last : "-->"};
case "BulletList":
default:
return { header : "", first : "<ul>", pre : "<li>", post : "</li>", last : "</ul>"};
@@ -831,6 +834,7 @@ Prado.WebUI.TBaseValidator.prototype = * @var {element} message
*/
this.message = $(options.ID);
+ Prado.Registry.set(options.ID, this);
if(this.control && this.message)
{
this.group = options.ValidationGroup;
diff --git a/framework/Web/Javascripts/source/prototype-1.6.0.2/prototype.js b/framework/Web/Javascripts/source/prototype-1.6.0.3/prototype.js index 6385503a..dfe8ab4e 100644 --- a/framework/Web/Javascripts/source/prototype-1.6.0.2/prototype.js +++ b/framework/Web/Javascripts/source/prototype-1.6.0.3/prototype.js @@ -1,4 +1,4 @@ -/* Prototype JavaScript framework, version 1.6.0.2 +/* Prototype JavaScript framework, version 1.6.0.3 * (c) 2005-2008 Sam Stephenson * * Prototype is freely distributable under the terms of an MIT-style license. @@ -7,23 +7,26 @@ *--------------------------------------------------------------------------*/ var Prototype = { - Version: '1.6.0.2', + Version: '1.6.0.3', Browser: { - IE: !!(window.attachEvent && !window.opera), - Opera: !!window.opera, + IE: !!(window.attachEvent && + navigator.userAgent.indexOf('Opera') === -1), + Opera: navigator.userAgent.indexOf('Opera') > -1, WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1, - Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1, + Gecko: navigator.userAgent.indexOf('Gecko') > -1 && + navigator.userAgent.indexOf('KHTML') === -1, MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/) }, BrowserFeatures: { XPath: !!document.evaluate, + SelectorsAPI: !!document.querySelector, ElementExtensions: !!window.HTMLElement, SpecificElementExtensions: - document.createElement('div').__proto__ && - document.createElement('div').__proto__ !== - document.createElement('form').__proto__ + document.createElement('div')['__proto__'] && + document.createElement('div')['__proto__'] !== + document.createElement('form')['__proto__'] }, ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>', @@ -83,12 +86,13 @@ Class.Methods = { var property = properties[i], value = source[property]; if (ancestor && Object.isFunction(value) && value.argumentNames().first() == "$super") { - var method = value, value = Object.extend((function(m) { + var method = value; + value = (function(m) { return function() { return ancestor[m].apply(this, arguments) }; - })(property).wrap(method), { - valueOf: function() { return method }, - toString: function() { return method.toString() } - }); + })(property).wrap(method); + + value.valueOf = method.valueOf.bind(method); + value.toString = method.toString.bind(method); } this.prototype[property] = value; } @@ -167,7 +171,7 @@ Object.extend(Object, { }, isElement: function(object) { - return object && object.nodeType == 1; + return !!(object && object.nodeType == 1); }, isArray: function(object) { @@ -198,7 +202,8 @@ Object.extend(Object, { Object.extend(Function.prototype, { argumentNames: function() { - var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip"); + var names = this.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1] + .replace(/\s+/g, '').split(','); return names.length == 1 && !names[0] ? [] : names; }, @@ -232,6 +237,11 @@ Object.extend(Function.prototype, { }, timeout); }, + defer: function() { + var args = [0.01].concat($A(arguments)); + return this.delay.apply(this, args); + }, + wrap: function(wrapper) { var __method = this; return function() { @@ -248,8 +258,6 @@ Object.extend(Function.prototype, { } }); -Function.prototype.defer = Function.prototype.delay.curry(0.01); - Date.prototype.toJSON = function() { return '"' + this.getUTCFullYear() + '-' + (this.getUTCMonth() + 1).toPaddedString(2) + '-' + @@ -530,7 +538,7 @@ if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.proto return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); }, unescapeHTML: function() { - return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); + return this.stripTags().replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); } }); @@ -547,7 +555,7 @@ Object.extend(String.prototype.escapeHTML, { text: document.createTextNode('') }); -with (String.prototype.escapeHTML) div.appendChild(text); +String.prototype.escapeHTML.div.appendChild(String.prototype.escapeHTML.text); var Template = Class.create({ initialize: function(template, pattern) { @@ -589,10 +597,9 @@ var $break = { }; var Enumerable = { each: function(iterator, context) { var index = 0; - iterator = iterator.bind(context); try { this._each(function(value) { - iterator(value, index++); + iterator.call(context, value, index++); }); } catch (e) { if (e != $break) throw e; @@ -601,47 +608,46 @@ var Enumerable = { }, eachSlice: function(number, iterator, context) { - iterator = iterator ? iterator.bind(context) : Prototype.K; var index = -number, slices = [], array = this.toArray(); + if (number < 1) return array; while ((index += number) < array.length) slices.push(array.slice(index, index+number)); return slices.collect(iterator, context); }, all: function(iterator, context) { - iterator = iterator ? iterator.bind(context) : Prototype.K; + iterator = iterator || Prototype.K; var result = true; this.each(function(value, index) { - result = result && !!iterator(value, index); + result = result && !!iterator.call(context, value, index); if (!result) throw $break; }); return result; }, any: function(iterator, context) { - iterator = iterator ? iterator.bind(context) : Prototype.K; + iterator = iterator || Prototype.K; var result = false; this.each(function(value, index) { - if (result = !!iterator(value, index)) + if (result = !!iterator.call(context, value, index)) throw $break; }); return result; }, collect: function(iterator, context) { - iterator = iterator ? iterator.bind(context) : Prototype.K; + iterator = iterator || Prototype.K; var results = []; this.each(function(value, index) { - results.push(iterator(value, index)); + results.push(iterator.call(context, value, index)); }); return results; }, detect: function(iterator, context) { - iterator = iterator.bind(context); var result; this.each(function(value, index) { - if (iterator(value, index)) { + if (iterator.call(context, value, index)) { result = value; throw $break; } @@ -650,17 +656,16 @@ var Enumerable = { }, findAll: function(iterator, context) { - iterator = iterator.bind(context); var results = []; this.each(function(value, index) { - if (iterator(value, index)) + if (iterator.call(context, value, index)) results.push(value); }); return results; }, grep: function(filter, iterator, context) { - iterator = iterator ? iterator.bind(context) : Prototype.K; + iterator = iterator || Prototype.K; var results = []; if (Object.isString(filter)) @@ -668,7 +673,7 @@ var Enumerable = { this.each(function(value, index) { if (filter.match(value)) - results.push(iterator(value, index)); + results.push(iterator.call(context, value, index)); }); return results; }, @@ -696,9 +701,8 @@ var Enumerable = { }, inject: function(memo, iterator, context) { - iterator = iterator.bind(context); this.each(function(value, index) { - memo = iterator(memo, value, index); + memo = iterator.call(context, memo, value, index); }); return memo; }, @@ -711,10 +715,10 @@ var Enumerable = { }, max: function(iterator, context) { - iterator = iterator ? iterator.bind(context) : Prototype.K; + iterator = iterator || Prototype.K; var result; this.each(function(value, index) { - value = iterator(value, index); + value = iterator.call(context, value, index); if (result == null || value >= result) result = value; }); @@ -722,10 +726,10 @@ var Enumerable = { }, min: function(iterator, context) { - iterator = iterator ? iterator.bind(context) : Prototype.K; + iterator = iterator || Prototype.K; var result; this.each(function(value, index) { - value = iterator(value, index); + value = iterator.call(context, value, index); if (result == null || value < result) result = value; }); @@ -733,10 +737,10 @@ var Enumerable = { }, partition: function(iterator, context) { - iterator = iterator ? iterator.bind(context) : Prototype.K; + iterator = iterator || Prototype.K; var trues = [], falses = []; this.each(function(value, index) { - (iterator(value, index) ? + (iterator.call(context, value, index) ? trues : falses).push(value); }); return [trues, falses]; @@ -751,19 +755,20 @@ var Enumerable = { }, reject: function(iterator, context) { - iterator = iterator.bind(context); var results = []; this.each(function(value, index) { - if (!iterator(value, index)) + if (!iterator.call(context, value, index)) results.push(value); }); return results; }, sortBy: function(iterator, context) { - iterator = iterator.bind(context); return this.map(function(value, index) { - return {value: value, criteria: iterator(value, index)}; + return { + value: value, + criteria: iterator.call(context, value, index) + }; }).sort(function(left, right) { var a = left.criteria, b = right.criteria; return a < b ? -1 : a > b ? 1 : 0; @@ -815,8 +820,12 @@ function $A(iterable) { if (Prototype.Browser.WebKit) { $A = function(iterable) { if (!iterable) return []; - if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') && - iterable.toArray) return iterable.toArray(); + // In Safari, only use the `toArray` method if it's not a NodeList. + // A NodeList is a function, has an function `item` property, and a numeric + // `length` property. Adapted from Google Doctype. + if (!(typeof iterable === 'function' && typeof iterable.length === + 'number' && typeof iterable.item === 'function') && iterable.toArray) + return iterable.toArray(); var length = iterable.length || 0, results = new Array(length); while (length--) results[length] = iterable[length]; return results; @@ -963,8 +972,8 @@ Object.extend(Number.prototype, { return this + 1; }, - times: function(iterator) { - $R(0, this, true).each(iterator); + times: function(iterator, context) { + $R(0, this, true).each(iterator, context); return this; }, @@ -1011,7 +1020,9 @@ var Hash = Class.create(Enumerable, (function() { }, get: function(key) { - return this._object[key]; + // simulating poorly supported hasOwnProperty + if (this._object[key] !== Object.prototype[key]) + return this._object[key]; }, unset: function(key) { @@ -1051,14 +1062,14 @@ var Hash = Class.create(Enumerable, (function() { }, toQueryString: function() { - return this.map(function(pair) { + return this.inject([], function(results, pair) { var key = encodeURIComponent(pair.key), values = pair.value; if (values && typeof values == 'object') { if (Object.isArray(values)) - return values.map(toQueryPair.curry(key)).join('&'); - } - return toQueryPair(key, values); + return results.concat(values.map(toQueryPair.curry(key))); + } else results.push(toQueryPair(key, values)); + return results; }).join('&'); }, @@ -1558,6 +1569,7 @@ if (!Node.ELEMENT_NODE) { return Element.writeAttribute(cache[tagName].cloneNode(false), attributes); }; Object.extend(this.Element, element || { }); + if (element) this.Element.prototype = element.prototype; }).call(window); Element.cache = { }; @@ -1574,12 +1586,14 @@ Element.Methods = { }, hide: function(element) { - $(element).style.display = 'none'; + element = $(element); + element.style.display = 'none'; return element; }, show: function(element) { - $(element).style.display = ''; + element = $(element); + element.style.display = ''; return element; }, @@ -1733,7 +1747,7 @@ Element.Methods = { element = $(element); if (arguments.length == 1) return element.firstDescendant(); return Object.isNumber(expression) ? element.descendants()[expression] : - element.select(expression)[index || 0]; + Element.select(element, expression)[index || 0]; }, previous: function(element, expression, index) { @@ -1863,24 +1877,16 @@ Element.Methods = { descendantOf: function(element, ancestor) { element = $(element), ancestor = $(ancestor); - var originalAncestor = ancestor; if (element.compareDocumentPosition) return (element.compareDocumentPosition(ancestor) & 8) === 8; - if (element.sourceIndex && !Prototype.Browser.Opera) { - var e = element.sourceIndex, a = ancestor.sourceIndex, - nextAncestor = ancestor.nextSibling; - if (!nextAncestor) { - do { ancestor = ancestor.parentNode; } - while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode); - } - if (nextAncestor && nextAncestor.sourceIndex) - return (e > a && e < nextAncestor.sourceIndex); - } + if (ancestor.contains) + return ancestor.contains(element) && ancestor !== element; while (element = element.parentNode) - if (element == originalAncestor) return true; + if (element == ancestor) return true; + return false; }, @@ -1895,7 +1901,7 @@ Element.Methods = { element = $(element); style = style == 'float' ? 'cssFloat' : style.camelize(); var value = element.style[style]; - if (!value) { + if (!value || value == 'auto') { var css = document.defaultView.getComputedStyle(element, null); value = css ? css[style] : null; } @@ -1934,7 +1940,7 @@ Element.Methods = { getDimensions: function(element) { element = $(element); - var display = $(element).getStyle('display'); + var display = element.getStyle('display'); if (display != 'none' && display != null) // Safari bug return {width: element.offsetWidth, height: element.offsetHeight}; @@ -1963,7 +1969,7 @@ Element.Methods = { element.style.position = 'relative'; // Opera returns the offset relative to the positioning context, when an // element is position relative but top and left have not been defined - if (window.opera) { + if (Prototype.Browser.Opera) { element.style.top = 0; element.style.left = 0; } @@ -2018,7 +2024,7 @@ Element.Methods = { valueL += element.offsetLeft || 0; element = element.offsetParent; if (element) { - if (element.tagName == 'BODY') break; + if (element.tagName.toUpperCase() == 'BODY') break; var p = Element.getStyle(element, 'position'); if (p !== 'static') break; } @@ -2028,7 +2034,7 @@ Element.Methods = { absolutize: function(element) { element = $(element); - if (element.getStyle('position') == 'absolute') return; + if (element.getStyle('position') == 'absolute') return element; // Position.prepare(); // To be done manually by Scripty when it needs it. var offsets = element.positionedOffset(); @@ -2052,7 +2058,7 @@ Element.Methods = { relativize: function(element) { element = $(element); - if (element.getStyle('position') == 'relative') return; + if (element.getStyle('position') == 'relative') return element; // Position.prepare(); // To be done manually by Scripty when it needs it. element.style.position = 'relative'; @@ -2103,7 +2109,7 @@ Element.Methods = { element = forElement; do { - if (!Prototype.Browser.Opera || element.tagName == 'BODY') { + if (!Prototype.Browser.Opera || (element.tagName && (element.tagName.toUpperCase() == 'BODY'))) { valueT -= element.scrollTop || 0; valueL -= element.scrollLeft || 0; } @@ -2218,6 +2224,9 @@ else if (Prototype.Browser.IE) { Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap( function(proceed, element) { element = $(element); + // IE throws an error if element is not in document + try { element.offsetParent } + catch(e) { return $(document.body) } var position = element.getStyle('position'); if (position !== 'static') return proceed(element); element.setStyle({ position: 'relative' }); @@ -2231,6 +2240,8 @@ else if (Prototype.Browser.IE) { Element.Methods[method] = Element.Methods[method].wrap( function(proceed, element) { element = $(element); + try { element.offsetParent } + catch(e) { return Element._returnOffset(0,0) } var position = element.getStyle('position'); if (position !== 'static') return proceed(element); // Trigger hasLayout on the offset parent so that IE6 reports @@ -2246,6 +2257,14 @@ else if (Prototype.Browser.IE) { ); }); + Element.Methods.cumulativeOffset = Element.Methods.cumulativeOffset.wrap( + function(proceed, element) { + try { element.offsetParent } + catch(e) { return Element._returnOffset(0,0) } + return proceed(element); + } + ); + Element.Methods.getStyle = function(element, style) { element = $(element); style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize(); @@ -2337,7 +2356,7 @@ else if (Prototype.Browser.IE) { Element._attributeTranslations.has = {}; $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' + - 'encType maxLength readOnly longDesc').each(function(attr) { + 'encType maxLength readOnly longDesc frameBorder').each(function(attr) { Element._attributeTranslations.write.names[attr.toLowerCase()] = attr; Element._attributeTranslations.has[attr.toLowerCase()] = attr; }); @@ -2390,7 +2409,7 @@ else if (Prototype.Browser.WebKit) { (value < 0.00001) ? 0 : value; if (value == 1) - if(element.tagName == 'IMG' && element.width) { + if(element.tagName.toUpperCase() == 'IMG' && element.width) { element.width++; element.width--; } else try { var n = document.createTextNode(' '); @@ -2521,7 +2540,7 @@ Element.Methods.Simulated = { hasAttribute: function(element, attribute) { attribute = Element._attributeTranslations.has[attribute] || attribute; var node = $(element).getAttributeNode(attribute); - return node && node.specified; + return !!(node && node.specified); } }; @@ -2530,9 +2549,9 @@ Element.Methods.ByTag = { }; Object.extend(Element, Element.Methods); if (!Prototype.BrowserFeatures.ElementExtensions && - document.createElement('div').__proto__) { + document.createElement('div')['__proto__']) { window.HTMLElement = { }; - window.HTMLElement.prototype = document.createElement('div').__proto__; + window.HTMLElement.prototype = document.createElement('div')['__proto__']; Prototype.BrowserFeatures.ElementExtensions = true; } @@ -2547,7 +2566,7 @@ Element.extend = (function() { element.nodeType != 1 || element == window) return element; var methods = Object.clone(Methods), - tagName = element.tagName, property, value; + tagName = element.tagName.toUpperCase(), property, value; // extend methods for specific tags if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]); @@ -2643,7 +2662,7 @@ Element.addMethods = function(methods) { if (window[klass]) return window[klass]; window[klass] = { }; - window[klass].prototype = document.createElement(tagName).__proto__; + window[klass].prototype = document.createElement(tagName)['__proto__']; return window[klass]; } @@ -2669,12 +2688,18 @@ Element.addMethods = function(methods) { document.viewport = { getDimensions: function() { - var dimensions = { }; - var B = Prototype.Browser; + var dimensions = { }, B = Prototype.Browser; $w('width height').each(function(d) { var D = d.capitalize(); - dimensions[d] = (B.WebKit && !document.evaluate) ? self['inner' + D] : - (B.Opera) ? document.body['client' + D] : document.documentElement['client' + D]; + if (B.WebKit && !document.evaluate) { + // Safari <3.0 needs self.innerWidth/Height + dimensions[d] = self['inner' + D]; + } else if (B.Opera && parseFloat(window.opera.version()) < 9.5) { + // Opera <9.5 needs document.body.clientWidth/Height + dimensions[d] = document.body['client' + D] + } else { + dimensions[d] = document.documentElement['client' + D]; + } }); return dimensions; }, @@ -2693,14 +2718,24 @@ document.viewport = { window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop); } }; -/* Portions of the Selector class are derived from Jack Slocum’s DomQuery, +/* Portions of the Selector class are derived from Jack Slocum's DomQuery, * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style * license. Please see http://www.yui-ext.com/ for more information. */ var Selector = Class.create({ initialize: function(expression) { this.expression = expression.strip(); - this.compileMatcher(); + + if (this.shouldUseSelectorsAPI()) { + this.mode = 'selectorsAPI'; + } else if (this.shouldUseXPath()) { + this.mode = 'xpath'; + this.compileXPathMatcher(); + } else { + this.mode = "normal"; + this.compileMatcher(); + } + }, shouldUseXPath: function() { @@ -2715,16 +2750,29 @@ var Selector = Class.create({ // XPath can't do namespaced attributes, nor can it read // the "checked" property from DOM nodes - if ((/(\[[\w-]*?:|:checked)/).test(this.expression)) + if ((/(\[[\w-]*?:|:checked)/).test(e)) return false; return true; }, - compileMatcher: function() { - if (this.shouldUseXPath()) - return this.compileXPathMatcher(); + shouldUseSelectorsAPI: function() { + if (!Prototype.BrowserFeatures.SelectorsAPI) return false; + + if (!Selector._div) Selector._div = new Element('div'); + + // Make sure the browser treats the selector as valid. Test on an + // isolated element to minimize cost of this check. + try { + Selector._div.querySelector(this.expression); + } catch(e) { + return false; + } + + return true; + }, + compileMatcher: function() { var e = this.expression, ps = Selector.patterns, h = Selector.handlers, c = Selector.criteria, le, p, m; @@ -2742,7 +2790,7 @@ var Selector = Class.create({ p = ps[i]; if (m = e.match(p)) { this.matcher.push(Object.isFunction(c[i]) ? c[i](m) : - new Template(c[i]).evaluate(m)); + new Template(c[i]).evaluate(m)); e = e.replace(m[0], ''); break; } @@ -2781,8 +2829,27 @@ var Selector = Class.create({ findElements: function(root) { root = root || document; - if (this.xpath) return document._getElementsByXPath(this.xpath, root); - return this.matcher(root); + var e = this.expression, results; + + switch (this.mode) { + case 'selectorsAPI': + // querySelectorAll queries document-wide, then filters to descendants + // of the context element. That's not what we want. + // Add an explicit context to the selector if necessary. + if (root !== document) { + var oldId = root.id, id = $(root).identify(); + e = "#" + id + " " + e; + } + + results = $A(root.querySelectorAll(e)).map(Element.extend); + root.id = oldId; + + return results; + case 'xpath': + return document._getElementsByXPath(this.xpath, root); + default: + return this.matcher(root); + } }, match: function(element) { @@ -2873,10 +2940,10 @@ Object.extend(Selector, { 'first-child': '[not(preceding-sibling::*)]', 'last-child': '[not(following-sibling::*)]', 'only-child': '[not(preceding-sibling::* or following-sibling::*)]', - 'empty': "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]", + 'empty': "[count(*) = 0 and (count(text()) = 0)]", 'checked': "[@checked]", - 'disabled': "[@disabled]", - 'enabled': "[not(@disabled)]", + 'disabled': "[(@disabled) and (@type!='hidden')]", + 'enabled': "[not(@disabled) and (@type!='hidden')]", 'not': function(m) { var e = m[6], p = Selector.patterns, x = Selector.xpath, le, v; @@ -2968,7 +3035,7 @@ Object.extend(Selector, { className: /^\.([\w\-\*]+)(\b|$)/, pseudo: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/, - attrPresence: /^\[([\w]+)\]/, + attrPresence: /^\[((?:[\w]+:)?[\w]+)\]/, attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/ }, @@ -3081,7 +3148,7 @@ Object.extend(Selector, { nextElementSibling: function(node) { while (node = node.nextSibling) - if (node.nodeType == 1) return node; + if (node.nodeType == 1) return node; return null; }, @@ -3270,7 +3337,7 @@ Object.extend(Selector, { 'empty': function(nodes, value, root) { for (var i = 0, results = [], node; node = nodes[i]; i++) { // IE treats comments as element nodes - if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue; + if (node.tagName == '!' || node.firstChild) continue; results.push(node); } return results; @@ -3288,7 +3355,8 @@ Object.extend(Selector, { 'enabled': function(nodes, value, root) { for (var i = 0, results = [], node; node = nodes[i]; i++) - if (!node.disabled) results.push(node); + if (!node.disabled && (!node.type || node.type !== 'hidden')) + results.push(node); return results; }, @@ -3308,11 +3376,14 @@ Object.extend(Selector, { operators: { '=': function(nv, v) { return nv == v; }, '!=': function(nv, v) { return nv != v; }, - '^=': function(nv, v) { return nv.startsWith(v); }, + '^=': function(nv, v) { return nv == v || nv && nv.startsWith(v); }, + '$=': function(nv, v) { return nv == v || nv && nv.endsWith(v); }, + '*=': function(nv, v) { return nv == v || nv && nv.include(v); }, '$=': function(nv, v) { return nv.endsWith(v); }, '*=': function(nv, v) { return nv.include(v); }, '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); }, - '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); } + '|=': function(nv, v) { return ('-' + (nv || "").toUpperCase() + + '-').include('-' + (v || "").toUpperCase() + '-'); } }, split: function(expression) { @@ -3386,7 +3457,7 @@ var Form = { var data = elements.inject({ }, function(result, element) { if (!element.disabled && element.name) { key = element.name; value = $(element).getValue(); - if (value != null && (element.type != 'submit' || (!submitted && + if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted && submit !== false && (!submit || key == submit) && (submitted = true)))) { if (key in result) { // a key is already present; construct an array of values @@ -3547,7 +3618,6 @@ Form.Element.Methods = { disable: function(element) { element = $(element); - element.blur(); element.disabled = true; return element; }, @@ -3587,22 +3657,22 @@ Form.Element.Serializers = { else element.value = value; }, - select: function(element, index) { - if (Object.isUndefined(index)) + select: function(element, value) { + if (Object.isUndefined(value)) return this[element.type == 'select-one' ? 'selectOne' : 'selectMany'](element); else { - var opt, value, single = !Object.isArray(index); + var opt, currentValue, single = !Object.isArray(value); for (var i = 0, length = element.length; i < length; i++) { opt = element.options[i]; - value = this.optionValue(opt); + currentValue = this.optionValue(opt); if (single) { - if (value == index) { + if (currentValue == value) { opt.selected = true; return; } } - else opt.selected = index.include(value); + else opt.selected = value.include(currentValue); } } }, @@ -3773,8 +3843,23 @@ Event.Methods = (function() { isRightClick: function(event) { return isButton(event, 2) }, element: function(event) { - var node = Event.extend(event).target; - return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node); + event = Event.extend(event); + + var node = event.target, + type = event.type, + currentTarget = event.currentTarget; + + if (currentTarget && currentTarget.tagName) { + // Firefox screws up the "click" event when moving between radio buttons + // via arrow keys. It also screws up the "load" and "error" events on images, + // reporting the document as the target instead of the original image. + if (type === 'load' || type === 'error' || + (type === 'click' && currentTarget.tagName.toLowerCase() === 'input' + && currentTarget.type === 'radio')) + node = currentTarget; + } + if (node.nodeType == Node.TEXT_NODE) node = node.parentNode; + return Element.extend(node); }, findElement: function(event, expression) { @@ -3785,11 +3870,15 @@ Event.Methods = (function() { }, pointer: function(event) { + var docElement = document.documentElement, + body = document.body || { scrollLeft: 0, scrollTop: 0 }; return { x: event.pageX || (event.clientX + - (document.documentElement.scrollLeft || document.body.scrollLeft)), + (docElement.scrollLeft || body.scrollLeft) - + (docElement.clientLeft || 0)), y: event.pageY || (event.clientY + - (document.documentElement.scrollTop || document.body.scrollTop)) + (docElement.scrollTop || body.scrollTop) - + (docElement.clientTop || 0)) }; }, @@ -3834,7 +3923,7 @@ Event.extend = (function() { }; } else { - Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__; + Event.prototype = Event.prototype || document.createEvent("HTMLEvents")['__proto__']; Object.extend(Event.prototype, methods); return Prototype.K; } @@ -3899,10 +3988,20 @@ Object.extend(Event, (function() { cache[id][eventName] = null; } + + // Internet Explorer needs to remove event handlers on page unload + // in order to avoid memory leaks. if (window.attachEvent) { window.attachEvent("onunload", destroyCache); } + // Safari has a dummy event handler on page unload so that it won't + // use its bfcache. Safari <= 3.1 has an issue with restoring the "document" + // object when page is returned to via the back button using its bfcache. + if (Prototype.Browser.WebKit) { + window.addEventListener('unload', Prototype.emptyFunction, false); + } + return { observe: function(element, eventName, handler) { element = $(element); diff --git a/framework/Web/Javascripts/source/scriptaculous-1.8.1/README b/framework/Web/Javascripts/source/scriptaculous-1.8.1/README deleted file mode 100644 index 27fefcb6..00000000 --- a/framework/Web/Javascripts/source/scriptaculous-1.8.1/README +++ /dev/null @@ -1,56 +0,0 @@ -== script.aculo.us web 2.0 javascript - -The Web is changing. The 30-year-old terminal-like technology it was originally -is gradually giving way to new ways of doing things. The power of AJAX allows -for rich user interaction without the trouble that has bugged traditional -web applications. - -Building upon the wonderful Prototype JavaScript library, script.aculo.us -provides you with some great additional ingredients to mix in. - -For more information, see http://script.aculo.us/ - -== What's new in this release? - -See the CHANGELOG file for information on what's new. - -== Installation/Usage - -script.aculo.us includes the Prototype JavaScript Framework -V1.6.0. You can use later versions, as they become available -(see http://prototypejs.org/). - -Put prototype.js, and the six files scriptaculous.js, -builder.js, effects.js, dragdrop.js, controls.js and slider.js -in a directory of your website, e.g. /javascripts. - -(The sound.js and unittest.js files are optional) - -Now, you can include the scripts by adding the following -tags to the HEAD section of your HTML pages: - - <script src="/javascripts/prototype.js" type="text/javascript"></script> - <script src="/javascripts/scriptaculous.js" type="text/javascript"></script> - -scriptaculous.js will automatically load the other files of the -script.aculo.us distribution in, provided they are accessible -via the same path. - -See http://wiki.script.aculo.us/scriptaculous/show/Usage for detailed -usage instructions. - -== The distribution - -Besides the script.aculo.us files in src, there's a complete -test tree included which holds functional and unit tests for -script.aculo.us. - -If you need examples on how to implement things, the best place to -start is by opening test/run_functional_tests.html or -test/run_unit_tests.html in your browser, and looking at -the sources of the examples provided. - -== License - -script.aculo.us is licensed under the terms of the MIT License, -see the included MIT-LICENSE file.
\ No newline at end of file diff --git a/framework/Web/Javascripts/source/scriptaculous-1.8.1/CHANGELOG b/framework/Web/Javascripts/source/scriptaculous-1.8.2/CHANGELOG index 75f5b625..5125cd1a 100644 --- a/framework/Web/Javascripts/source/scriptaculous-1.8.1/CHANGELOG +++ b/framework/Web/Javascripts/source/scriptaculous-1.8.2/CHANGELOG @@ -1,3 +1,29 @@ +*V1.8.2* (November 18, 2008) + +* Update to Prototype 1.6.0.3 + +* Make sure InPlaceEditor converts HTML entities to text. [Sean Kirby] + +* Fix that Builder.node did not return extended elements on IE. Closes #71 and #77. + +* Fix a bug in Sortable.destroy to make sure it's called on the referenced Sortable only, which allows for the correct intialization of nested Sortables. Closes Trac #8615. [Leon Chevalier] + +* Change Effect.Base#render not to use eval(), so certain JavaScript runtime environments (like Adobe AIR) that do not support eval() work. [King Maxemilian, John-David Dalton] + +* Fixed a calculation error in Effect.Transitions.pulse that could lead to flickering, add easing and change it to be a normal 0 to 1 transition that can be used with any effects; Effect.Pulsate now uses its own implementation. [Thomas Fuchs] + +* Fixed Effect.ScrollTo. Changeset 8686 had a typo, document.viewport.getScrollOffsets[0] is always undefined. Removed the max check as it is not a cross-browser way to get scroll height and breaks the effect. Depending on scrollTo to do the right thing. Closes #11306. [Nick Stakenburg] + +* Update version check so all Prototype versions can be required, not just x.x.x. Closes #10966. [Nick Stakenburg] + +* Using $$ in the loader instead of getElementsByTagName to prevent limitations. Closes #9032. [Nick Stakenburg] + +* Fix some missing semicolons. [jdalton] + +* Fix an issue with Effect.ScrollTo that caused Firefox to scroll to the wrong offset in some situations. Closes #10245. [nik.wakelin] + +* Fixes an issue with IE ghosting on non-absolute elements. Closes #10423. [Tanrikut, tdd] + *V1.8.1* (January 3, 2008) * Fix Element#getStyles in IE. Closes #10563. [Tobie Langel] diff --git a/framework/Web/Javascripts/source/scriptaculous-1.8.1/MIT-LICENSE b/framework/Web/Javascripts/source/scriptaculous-1.8.2/MIT-LICENSE index b5e74b40..3889631a 100644 --- a/framework/Web/Javascripts/source/scriptaculous-1.8.1/MIT-LICENSE +++ b/framework/Web/Javascripts/source/scriptaculous-1.8.2/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/framework/Web/Javascripts/source/scriptaculous-1.8.1/builder.js b/framework/Web/Javascripts/source/scriptaculous-1.8.2/builder.js index 83019994..dba8beca 100644 --- a/framework/Web/Javascripts/source/scriptaculous-1.8.1/builder.js +++ b/framework/Web/Javascripts/source/scriptaculous-1.8.2/builder.js @@ -1,6 +1,6 @@ -// script.aculo.us builder.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008 +// script.aculo.us builder.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008 -// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) // // script.aculo.us is freely distributable under the terms of an MIT-style license. // For details, see the script.aculo.us web site: http://script.aculo.us/ @@ -26,7 +26,7 @@ var Builder = { // due to a Firefox bug node: function(elementName) { elementName = elementName.toUpperCase(); - + // try innerHTML approach var parentTag = this.NODEMAP[elementName] || 'div'; var parentElement = document.createElement(parentTag); @@ -34,14 +34,14 @@ var Builder = { parentElement.innerHTML = "<" + elementName + "></" + elementName + ">"; } catch(e) {} var element = parentElement.firstChild || null; - + // see if browser added wrapping tags if(element && (element.tagName.toUpperCase() != elementName)) element = element.getElementsByTagName(elementName)[0]; - + // fallback to createElement approach if(!element) element = document.createElement(elementName); - + // abort if nothing could be created if(!element) return; @@ -62,19 +62,19 @@ var Builder = { // workaround firefox 1.0.X bug if(!element) { element = document.createElement(elementName); - for(attr in arguments[1]) + for(attr in arguments[1]) element[attr == 'class' ? 'className' : attr] = arguments[1][attr]; } if(element.tagName.toUpperCase() != elementName) element = parentElement.getElementsByTagName(elementName)[0]; } - } + } // text, or array of children if(arguments[2]) this._children(element, arguments[2]); - return element; + return $(element); }, _text: function(text) { return document.createTextNode(text); @@ -100,7 +100,7 @@ var Builder = { if(typeof children=='object') { // array can hold nodes and text children.flatten().each( function(e) { if(typeof e=='object') - element.appendChild(e) + element.appendChild(e); else if(Builder._isStringOrNumber(e)) element.appendChild(Builder._text(e)); @@ -117,20 +117,20 @@ var Builder = { $(element).update(html.strip()); return element.down(); }, - dump: function(scope) { - if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope - + dump: function(scope) { + if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope + var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " + "BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " + "FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+ "KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+ "PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+ "TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/); - - tags.each( function(tag){ - scope[tag] = function() { - return Builder.node.apply(Builder, [tag].concat($A(arguments))); - } + + tags.each( function(tag){ + scope[tag] = function() { + return Builder.node.apply(Builder, [tag].concat($A(arguments))); + }; }); } -} +};
\ No newline at end of file diff --git a/framework/Web/Javascripts/source/scriptaculous-1.8.1/controls.js b/framework/Web/Javascripts/source/scriptaculous-1.8.2/controls.js index 5012cb81..c56ccb79 100644 --- a/framework/Web/Javascripts/source/scriptaculous-1.8.1/controls.js +++ b/framework/Web/Javascripts/source/scriptaculous-1.8.2/controls.js @@ -1,24 +1,24 @@ -// script.aculo.us controls.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008 +// script.aculo.us controls.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008 -// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) -// (c) 2005-2007 Ivan Krstic (http://blogs.law.harvard.edu/ivan) -// (c) 2005-2007 Jon Tirsen (http://www.tirsen.com) +// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// (c) 2005-2008 Ivan Krstic (http://blogs.law.harvard.edu/ivan) +// (c) 2005-2008 Jon Tirsen (http://www.tirsen.com) // Contributors: // Richard Livsey // Rahul Bhargava // Rob Wills -// +// // script.aculo.us is freely distributable under the terms of an MIT-style license. // For details, see the script.aculo.us web site: http://script.aculo.us/ -// Autocompleter.Base handles all the autocompletion functionality +// Autocompleter.Base handles all the autocompletion functionality // that's independent of the data source for autocompletion. This // includes drawing the autocompletion menu, observing keyboard // and mouse events, and similar. // -// Specific autocompleters need to provide, at the very least, +// Specific autocompleters need to provide, at the very least, // a getUpdatedChoices function that will be invoked every time -// the text inside the monitored textbox changes. This method +// the text inside the monitored textbox changes. This method // should get the text for which to provide autocompletion by // invoking this.getToken(), NOT by directly accessing // this.element.value. This is to allow incremental tokenized @@ -32,23 +32,23 @@ // will incrementally autocomplete with a comma as the token. // Additionally, ',' in the above example can be replaced with // a token array, e.g. { tokens: [',', '\n'] } which -// enables autocompletion on multiple tokens. This is most -// useful when one of the tokens is \n (a newline), as it +// enables autocompletion on multiple tokens. This is most +// useful when one of the tokens is \n (a newline), as it // allows smart autocompletion after linebreaks. if(typeof Effect == 'undefined') throw("controls.js requires including script.aculo.us' effects.js library"); -var Autocompleter = { } +var Autocompleter = { }; Autocompleter.Base = Class.create({ baseInitialize: function(element, update, options) { - element = $(element) - this.element = element; - this.update = $(update); - this.hasFocus = false; - this.changed = false; - this.active = false; - this.index = 0; + element = $(element); + this.element = element; + this.update = $(update); + this.hasFocus = false; + this.changed = false; + this.active = false; + this.index = 0; this.entryCount = 0; this.oldElementValue = this.element.value; @@ -61,28 +61,28 @@ Autocompleter.Base = Class.create({ 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){ + 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, + setHeight: false, offsetTop: element.offsetHeight }); } Effect.Appear(update,{duration:0.15}); }; - this.options.onHide = this.options.onHide || + this.options.onHide = this.options.onHide || function(element, update){ new Effect.Fade(update,{duration:0.15}) }; - if(typeof(this.options.tokens) == 'string') + if(typeof(this.options.tokens) == 'string') this.options.tokens = new Array(this.options.tokens); // Force carriage returns as token delimiters anyway if (!this.options.tokens.include('\n')) this.options.tokens.push('\n'); this.observer = null; - + this.element.setAttribute('autocomplete','off'); Element.hide(this.update); @@ -93,10 +93,10 @@ Autocompleter.Base = Class.create({ show: function() { if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update); - if(!this.iefix && + if(!this.iefix && (Prototype.Browser.IE) && (Element.getStyle(this.update, 'position')=='absolute')) { - new Insertion.After(this.update, + 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>'); @@ -104,7 +104,7 @@ Autocompleter.Base = Class.create({ } if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50); }, - + fixIEOverlapping: function() { Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)}); this.iefix.style.zIndex = 1; @@ -152,15 +152,15 @@ Autocompleter.Base = Class.create({ Event.stop(event); return; } - else - if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN || + else + if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN || (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return; this.changed = true; this.hasFocus = true; if(this.observer) clearTimeout(this.observer); - this.observer = + this.observer = setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000); }, @@ -172,35 +172,35 @@ Autocompleter.Base = Class.create({ onHover: function(event) { var element = Event.findElement(event, 'LI'); - if(this.index != element.autocompleteIndex) + 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) { // needed to make click events working setTimeout(this.hide.bind(this), 250); this.hasFocus = false; - this.active = 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") : + this.index==i ? + Element.addClassName(this.getEntry(i),"selected") : Element.removeClassName(this.getEntry(i),"selected"); - if(this.hasFocus) { + if(this.hasFocus) { this.show(); this.active = true; } @@ -209,27 +209,27 @@ Autocompleter.Base = Class.create({ this.hide(); } }, - + markPrevious: function() { - if(this.index > 0) this.index-- + if(this.index > 0) this.index--; else this.index = this.entryCount-1; this.getEntry(this.index).scrollIntoView(true); }, - + markNext: function() { - if(this.index < this.entryCount-1) this.index++ + if(this.index < this.entryCount-1) this.index++; else this.index = 0; this.getEntry(this.index).scrollIntoView(false); }, - + 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()); @@ -246,7 +246,7 @@ Autocompleter.Base = Class.create({ if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select); } else value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal'); - + var bounds = this.getTokenBounds(); if (bounds[0] != -1) { var newValue = this.element.value.substr(0, bounds[0]); @@ -259,7 +259,7 @@ Autocompleter.Base = Class.create({ } this.oldElementValue = this.element.value; this.element.focus(); - + if (this.options.afterUpdateElement) this.options.afterUpdateElement(this.element, selectedElement); }, @@ -271,20 +271,20 @@ Autocompleter.Base = Class.create({ Element.cleanWhitespace(this.update.down()); if(this.update.firstChild && this.update.down().childNodes) { - this.entryCount = + this.entryCount = this.update.down().childNodes.length; for (var i = 0; i < this.entryCount; i++) { var entry = this.getEntry(i); entry.autocompleteIndex = i; this.addObservers(entry); } - } else { + } else { this.entryCount = 0; } this.stopIndicator(); this.index = 0; - + if(this.entryCount==1 && this.options.autoSelect) { this.selectEntry(); this.hide(); @@ -300,7 +300,7 @@ Autocompleter.Base = Class.create({ }, onObserverEvent: function() { - this.changed = false; + this.changed = false; this.tokenBounds = null; if(this.getToken().length>=this.options.minChars) { this.getUpdatedChoices(); @@ -353,16 +353,16 @@ Ajax.Autocompleter = Class.create(Autocompleter.Base, { getUpdatedChoices: function() { this.startIndicator(); - - var entry = encodeURIComponent(this.options.paramName) + '=' + + + var 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) + if(this.options.defaultParams) this.options.parameters += '&' + this.options.defaultParams; - + new Ajax.Request(this.url, this.options); }, @@ -384,7 +384,7 @@ Ajax.Autocompleter = Class.create(Autocompleter.Base, { // - choices - How many autocompletion choices to offer // // - partialSearch - If false, the autocompleter will match entered -// text only at the beginning of strings in the +// text only at the beginning of strings in the // autocomplete array. Defaults to true, which will // match text at the beginning of any *word* in the // strings in the autocomplete array. If you want to @@ -401,7 +401,7 @@ Ajax.Autocompleter = Class.create(Autocompleter.Base, { // - ignoreCase - Whether to ignore case when autocompleting. // Defaults to true. // -// It's possible to pass in a custom function as the 'selector' +// It's possible to pass in a custom function as the 'selector' // option, if you prefer to write your own autocompletion logic. // In that case, the other options above will not apply unless // you support them. @@ -429,20 +429,20 @@ Autocompleter.Local = Class.create(Autocompleter.Base, { var entry = instance.getToken(); var count = 0; - for (var i = 0; i < instance.options.array.length && - ret.length < instance.options.choices ; i++) { + 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()) : + 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>" + + 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 && + } 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>" + @@ -452,14 +452,14 @@ Autocompleter.Local = Class.create(Autocompleter.Base, { } } - foundPos = instance.options.ignoreCase ? - elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : + 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)) + ret = ret.concat(partial.slice(0, instance.options.choices - ret.length)); return "<ul>" + ret.join('') + "</ul>"; } }, options || { }); @@ -476,7 +476,7 @@ Field.scrollFreeActivate = function(field) { setTimeout(function() { Field.activate(field); }, 1); -} +}; Ajax.InPlaceEditor = Class.create({ initialize: function(element, url, options) { @@ -606,7 +606,7 @@ Ajax.InPlaceEditor = Class.create({ this.triggerCallback('onEnterHover'); }, getText: function() { - return this.element.innerHTML; + return this.element.innerHTML.unescapeHTML(); }, handleAJAXFailure: function(transport) { this.triggerCallback('onFailure', transport); @@ -782,7 +782,7 @@ Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, { onSuccess: function(transport) { var js = transport.responseText.strip(); if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check - throw 'Server returned an invalid collection representation.'; + throw('Server returned an invalid collection representation.'); this._collection = eval(js); this.checkForExternalText(); }.bind(this), @@ -939,7 +939,7 @@ Ajax.InPlaceCollectionEditor.DefaultOptions = { loadingCollectionText: 'Loading options...' }; -// Delayed observer, like Form.Element.Observer, +// Delayed observer, like Form.Element.Observer, // but waits for delay after last key input // Ideal for live-search fields @@ -949,7 +949,7 @@ Form.Element.DelayedObserver = Class.create({ this.element = $(element); this.callback = callback; this.timer = null; - this.lastValue = $F(this.element); + this.lastValue = $F(this.element); Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this)); }, delayedListener: function(event) { @@ -962,4 +962,4 @@ Form.Element.DelayedObserver = Class.create({ this.timer = null; this.callback(this.element, $F(this.element)); } -}); +});
\ No newline at end of file diff --git a/framework/Web/Javascripts/source/scriptaculous-1.8.1/dragdrop.js b/framework/Web/Javascripts/source/scriptaculous-1.8.2/dragdrop.js index 14f9546e..07c98e2b 100644 --- a/framework/Web/Javascripts/source/scriptaculous-1.8.1/dragdrop.js +++ b/framework/Web/Javascripts/source/scriptaculous-1.8.2/dragdrop.js @@ -1,8 +1,8 @@ -// script.aculo.us dragdrop.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008 +// script.aculo.us dragdrop.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008 -// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) -// (c) 2005-2007 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz) -// +// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// (c) 2005-2008 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz) +// // script.aculo.us is freely distributable under the terms of an MIT-style license. // For details, see the script.aculo.us web site: http://script.aculo.us/ @@ -34,7 +34,7 @@ var Droppables = { options._containers.push($(containment)); } } - + if(options.accept) options.accept = [options.accept].flatten(); Element.makePositioned(element); // fix IE @@ -42,34 +42,34 @@ var Droppables = { 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; + 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( + (Element.classNames(element).detect( function(v) { return drop.accept.include(v) } ) )) && Position.within(drop.element, point[0], point[1]) ); }, @@ -89,12 +89,12 @@ var Droppables = { show: function(point, element) { if(!this.drops.length) return; var drop, affected = []; - + this.drops.each( function(drop) { if(Droppables.isAffected(point, element, drop)) affected.push(drop); }); - + if(affected.length>0) drop = Droppables.findDeepestChild(affected); @@ -103,7 +103,7 @@ var Droppables = { Position.within(drop.element, point[0], point[1]); if(drop.onHover) drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element)); - + if (drop != this.last_active) Droppables.activate(drop); } }, @@ -114,8 +114,8 @@ var Droppables = { 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); - return true; + this.last_active.onDrop(element, this.last_active.element, event); + return true; } }, @@ -123,25 +123,25 @@ var Droppables = { 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) { @@ -150,24 +150,24 @@ var Draggables = { Event.stopObserving(document, "keypress", this.eventKeypress); } }, - + activate: function(draggable) { - if(draggable.options.delay) { - this._timeout = setTimeout(function() { - Draggables._timeout = null; - window.focus(); - Draggables.activeDraggable = draggable; - }.bind(this), draggable.options.delay); + if(draggable.options.delay) { + this._timeout = setTimeout(function() { + Draggables._timeout = null; + window.focus(); + Draggables.activeDraggable = draggable; + }.bind(this), draggable.options.delay); } else { window.focus(); // allows keypress events if window isn't currently focused, fails for Safari this.activeDraggable = draggable; } }, - + deactivate: function() { this.activeDraggable = null; }, - + updateDrag: function(event) { if(!this.activeDraggable) return; var pointer = [Event.pointerX(event), Event.pointerY(event)]; @@ -175,36 +175,36 @@ var Draggables = { // the same coordinates, prevent needless redrawing (moz bug?) if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return; this._lastPointer = pointer; - + this.activeDraggable.updateDrag(event, pointer); }, - + endDrag: function(event) { - if(this._timeout) { - clearTimeout(this._timeout); - this._timeout = null; + if(this._timeout) { + clearTimeout(this._timeout); + this._timeout = null; } 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) { // element instead of observer fixes mem leaks this.observers = this.observers.reject( function(o) { return o.element==element }); this._cacheObserverCallbacks(); }, - + notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag' if(this[eventName+'Count'] > 0) this.observers.each( function(o) { @@ -212,7 +212,7 @@ var Draggables = { }); if(draggable.options[eventName]) draggable.options[eventName](draggable, event); }, - + _cacheObserverCallbacks: function() { ['onStart','onEnd','onDrag'].each( function(eventName) { Draggables[eventName+'Count'] = Draggables.observers.select( @@ -220,7 +220,7 @@ var Draggables = { ).length; }); } -} +}; /*--------------------------------------------------------------------------*/ @@ -236,12 +236,12 @@ var Draggable = Class.create({ }, endeffect: function(element) { var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0; - new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity, + new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity, queue: {scope:'_draggable', position:'end'}, - afterFinish: function(){ - Draggable._dragging[element] = false + afterFinish: function(){ + Draggable._dragging[element] = false } - }); + }); }, zindex: 1000, revert: false, @@ -252,57 +252,57 @@ var Draggable = Class.create({ snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] } delay: 0 }; - + if(!arguments[1] || Object.isUndefined(arguments[1].endeffect)) Object.extend(defaults, { starteffect: function(element) { element._opacity = Element.getOpacity(element); Draggable._dragging[element] = true; - new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7}); + new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7}); } }); - + var options = Object.extend(defaults, arguments[1] || { }); this.element = $(element); - + if(options.handle && Object.isString(options.handle)) this.handle = this.element.down('.'+options.handle, 0); - + if(!this.handle) this.handle = $(options.handle); if(!this.handle) this.handle = this.element; - + if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) { options.scroll = $(options.scroll); this._isScrollChild = Element.childOf(this.element, options.scroll); } - Element.makePositioned(this.element); // fix IE + Element.makePositioned(this.element); // fix IE this.options = options; - this.dragging = false; + 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(!Object.isUndefined(Draggable._dragging[this.element]) && Draggable._dragging[this.element]) return; - if(Event.isLeftClick(event)) { + if(Event.isLeftClick(event)) { // abort on form elements, fixes a Firefox issue var src = Event.element(event); if((tag_name = src.tagName.toUpperCase()) && ( @@ -311,34 +311,34 @@ var Draggable = Class.create({ tag_name=='OPTION' || tag_name=='BUTTON' || tag_name=='TEXTAREA')) return; - + 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.delta) this.delta = this.currentDelta(); - + 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); - this.element._originallyAbsolute = (this.element.getStyle('position') == 'absolute'); - if (!this.element._originallyAbsolute) + this._originallyAbsolute = (this.element.getStyle('position') == 'absolute'); + if (!this._originallyAbsolute) 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); @@ -349,28 +349,28 @@ var Draggable = Class.create({ 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); - + if(!this.options.quiet){ 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 ]; } @@ -388,16 +388,16 @@ var Draggable = Class.create({ if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity); this.startScrolling(speed); } - + // fix AppleWebKit rendering if(Prototype.Browser.WebKit) window.scrollBy(0,0); - + Event.stop(event); }, - + finishDrag: function(event, success) { this.dragging = false; - + if(this.options.quiet){ Position.prepare(); var pointer = [Event.pointerX(event), Event.pointerY(event)]; @@ -405,24 +405,24 @@ var Draggable = Class.create({ } if(this.options.ghosting) { - if (!this.element._originallyAbsolute) + if (!this._originallyAbsolute) Position.relativize(this.element); - this.element._originallyAbsolute=null; + delete this._originallyAbsolute; Element.remove(this._clone); this._clone = null; } - var dropped = false; - if(success) { - dropped = Droppables.fire(event, this.element); - if (!dropped) dropped = false; + var dropped = false; + if(success) { + dropped = Droppables.fire(event, this.element); + if (!dropped) dropped = false; } if(dropped && this.options.onDropped) this.options.onDropped(this.element); Draggables.notify('onEnd', this, event); var revert = this.options.revert; if(revert && Object.isFunction(revert)) revert = revert(this.element); - + var d = this.currentDelta(); if(revert && this.options.reverteffect) { if (dropped == 0 || revert != 'failure') @@ -435,67 +435,67 @@ var Draggable = Class.create({ if(this.options.zindex) this.element.style.zIndex = this.originalZ; - if(this.options.endeffect) + 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); if(this.options.ghosting) { var r = Position.realOffset(this.element); pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY; } - + var d = this.currentDelta(); pos[0] -= d[0]; pos[1] -= d[1]; - + if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) { 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]) + + var p = [0,1].map(function(i){ + return (point[i]-pos[i]-this.offset[i]) }.bind(this)); - + if(this.options.snap) { if(Object.isFunction(this.options.snap)) { p = this.options.snap(p[0],p[1],this); } else { if(Object.isArray(this.options.snap)) { p = p.map( function(v, i) { - return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this)) + return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this)); } else { p = p.map( function(v) { - return (v/this.options.snap).round()*this.options.snap }.bind(this)) + return (v/this.options.snap).round()*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 = ""; // fix gecko rendering }, - + stopScrolling: function() { if(this.scrollInterval) { clearInterval(this.scrollInterval); @@ -503,14 +503,14 @@ var Draggable = Class.create({ Draggables._lastScrollPointer = null; } }, - + startScrolling: function(speed) { if(!(speed[0] || speed[1])) return; 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; @@ -526,7 +526,7 @@ var Draggable = Class.create({ 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); @@ -540,10 +540,10 @@ var Draggable = Class.create({ 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) { @@ -562,7 +562,7 @@ var Draggable = Class.create({ H = documentElement.clientHeight; } else { W = body.offsetWidth; - H = body.offsetHeight + H = body.offsetHeight; } } return { top: T, left: L, width: W, height: H }; @@ -579,11 +579,11 @@ var SortableObserver = Class.create({ 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)) @@ -593,11 +593,11 @@ var SortableObserver = Class.create({ var Sortable = { SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/, - + sortables: { }, - + _findRootElement: function(element) { - while (element.tagName.toUpperCase() != "BODY") { + while (element.tagName.toUpperCase() != "BODY") { if(element.id && Sortable.sortables[element.id]) return element; element = element.parentNode; } @@ -608,22 +608,23 @@ var Sortable = { if(!element) return; return Sortable.sortables[element.id]; }, - + destroy: function(element){ - var s = Sortable.options(element); - + element = $(element); + var s = Sortable.sortables[element.id]; + 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({ + var options = Object.extend({ element: element, tag: 'li', // assumes li children, override with tag: 'tagname' dropOnEmpty: false, @@ -637,17 +638,17 @@ var Sortable = { delay: 0, hoverclass: null, ghosting: false, - quiet: false, + quiet: false, scroll: false, scrollSensitivity: 20, scrollSpeed: 15, format: this.SERIALIZE_RULE, - - // these take arrays of elements or ids and can be + + // these take arrays of elements or ids and can be // used for better initialization performance elements: false, handles: false, - + onChange: Prototype.emptyFunction, onUpdate: Prototype.emptyFunction }, arguments[1] || { }); @@ -684,24 +685,24 @@ var Sortable = { if(options.zindex) options_for_draggable.zindex = options.zindex; - // build options for the droppables + // build options for the droppables 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 - } + }; // fix for gecko engine - Element.cleanWhitespace(element); + Element.cleanWhitespace(element); options.draggables = []; options.droppables = []; @@ -714,14 +715,14 @@ var Sortable = { (options.elements || this.findElements(element, options) || []).each( function(e,i) { var handle = options.handles ? $(options.handles[i]) : - (options.handle ? $(e).select('.' + options.handle)[0] : e); + (options.handle ? $(e).select('.' + 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); + options.droppables.push(e); }); - + if(options.tree) { (Sortable.findTreeElements(element, options) || []).each( function(e) { Droppables.add(e, options_for_tree); @@ -743,7 +744,7 @@ var Sortable = { 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); @@ -760,7 +761,7 @@ var Sortable = { var oldParentNode = element.parentNode; element.style.visibility = "hidden"; // fix gecko rendering dropon.parentNode.insertBefore(element, dropon); - if(dropon.parentNode!=oldParentNode) + if(dropon.parentNode!=oldParentNode) Sortable.options(oldParentNode).onChange(element); Sortable.options(dropon.parentNode).onChange(element); } @@ -771,26 +772,26 @@ var Sortable = { var oldParentNode = element.parentNode; element.style.visibility = "hidden"; // fix gecko rendering dropon.parentNode.insertBefore(element, nextElement); - if(dropon.parentNode!=oldParentNode) + 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, only: droponOptions.only}); 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); @@ -803,9 +804,9 @@ var Sortable = { } } } - + dropon.insertBefore(element, child); - + Sortable.options(oldParentNode).onChange(element); droponOptions.onChange(element); } @@ -818,34 +819,34 @@ var Sortable = { mark: function(dropon, position) { // mark on ghosting only var sortable = Sortable.options(dropon.parentNode); - if(sortable && !sortable.ghosting) return; + if(sortable && !sortable.ghosting) return; if(!Sortable._marker) { - Sortable._marker = + Sortable._marker = ($('dropmarker') || Element.extend(document.createElement('DIV'))). hide().addClassName('dropmarker').setStyle({position:'absolute'}); document.getElementsByTagName("body").item(0).appendChild(Sortable._marker); - } + } var offsets = Position.cumulativeOffset(dropon); Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'}); - + if(position=='after') - if(sortable.overlap == 'horizontal') + if(sortable.overlap == 'horizontal') Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'}); else Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'}); - + Sortable._marker.show(); }, - + _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, @@ -853,16 +854,16 @@ var Sortable = { children: [], position: parent.children.length, container: $(children[i]).down(options.treeTag) - } - + }; + /* Get the element containing the children and recurse over it */ if (child.container) - this._tree(child.container, options, child) - + this._tree(child.container, options, child); + parent.children.push (child); } - return parent; + return parent; }, tree: function(element) { @@ -875,15 +876,15 @@ var Sortable = { name: element.id, format: sortableOptions.format }, arguments[1] || { }); - + var root = { id: null, parent: null, children: [], container: element, position: 0 - } - + }; + return Sortable._tree(element, options, root); }, @@ -899,7 +900,7 @@ var Sortable = { 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] : ''; }); @@ -908,14 +909,14 @@ var Sortable = { 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) { @@ -924,16 +925,16 @@ var Sortable = { } }); }, - + 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) + "[id]=" + + return [name + Sortable._constructIndex(item) + "[id]=" + encodeURIComponent(item.id)].concat(item.children.map(arguments.callee)); }).flatten().join('&'); } else { @@ -942,16 +943,16 @@ var Sortable = { }).join('&'); } } -} +}; // Returns true if child is contained within element Element.isParent = function(child, element) { if (!child.parentNode || child == element) return false; if (child.parentNode == element) return true; return Element.isParent(child.parentNode, element); -} +}; -Element.findChildren = function(element, only, recursive, tagName) { +Element.findChildren = function(element, only, recursive, tagName) { if(!element.hasChildNodes()) return null; tagName = tagName.toUpperCase(); if(only) only = [only].flatten(); @@ -967,8 +968,8 @@ Element.findChildren = function(element, only, recursive, tagName) { }); return (elements.length>0 ? elements.flatten() : []); -} +}; Element.offsetSize = function (element, type) { return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')]; -} +};
\ No newline at end of file diff --git a/framework/Web/Javascripts/source/scriptaculous-1.8.1/effects.js b/framework/Web/Javascripts/source/scriptaculous-1.8.2/effects.js index b8c0259f..f31a81a0 100644 --- a/framework/Web/Javascripts/source/scriptaculous-1.8.1/effects.js +++ b/framework/Web/Javascripts/source/scriptaculous-1.8.2/effects.js @@ -1,50 +1,50 @@ -// script.aculo.us effects.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008 +// script.aculo.us effects.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008 -// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) // Contributors: // Justin Palmer (http://encytemedia.com/) // Mark Pilgrim (http://diveintomark.org/) // Martin Bialasinki -// +// // script.aculo.us is freely distributable under the terms of an MIT-style license. -// For details, see the script.aculo.us web site: http://script.aculo.us/ +// For details, see the script.aculo.us web site: http://script.aculo.us/ -// converts rgb() and #xxx to #xxxxxx format, -// returns self (or first argument) if not convertable -String.prototype.parseColor = function() { +// converts rgb() and #xxx to #xxxxxx format, +// returns self (or first argument) if not convertable +String.prototype.parseColor = function() { var color = '#'; - if (this.slice(0,4) == 'rgb(') { - var cols = this.slice(4,this.length-1).split(','); - var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3); - } else { - if (this.slice(0,1) == '#') { - if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase(); - if (this.length==7) color = this.toLowerCase(); - } - } - return (color.length==7 ? color : (arguments[0] || this)); + if (this.slice(0,4) == 'rgb(') { + var cols = this.slice(4,this.length-1).split(','); + var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3); + } else { + if (this.slice(0,1) == '#') { + if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase(); + if (this.length==7) color = this.toLowerCase(); + } + } + return (color.length==7 ? color : (arguments[0] || this)); }; /*--------------------------------------------------------------------------*/ -Element.collectTextNodes = function(element) { +Element.collectTextNodes = function(element) { return $A($(element).childNodes).collect( function(node) { - return (node.nodeType==3 ? node.nodeValue : + return (node.nodeType==3 ? node.nodeValue : (node.hasChildNodes() ? Element.collectTextNodes(node) : '')); }).flatten().join(''); }; -Element.collectTextNodesIgnoreClass = function(element, className) { +Element.collectTextNodesIgnoreClass = function(element, className) { return $A($(element).childNodes).collect( function(node) { - return (node.nodeType==3 ? node.nodeValue : - ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? + return (node.nodeType==3 ? node.nodeValue : + ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? Element.collectTextNodesIgnoreClass(node, className) : '')); }).flatten().join(''); }; Element.setContentZoom = function(element, percent) { - element = $(element); - element.setStyle({fontSize: (percent/100) + 'em'}); + element = $(element); + element.setStyle({fontSize: (percent/100) + 'em'}); if (Prototype.Browser.WebKit) window.scrollBy(0,0); return element; }; @@ -72,28 +72,23 @@ var Effect = { Transitions: { linear: Prototype.K, sinoidal: function(pos) { - return (-Math.cos(pos*Math.PI)/2) + 0.5; + return (-Math.cos(pos*Math.PI)/2) + .5; }, reverse: function(pos) { return 1-pos; }, flicker: function(pos) { - var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4; + var pos = ((-Math.cos(pos*Math.PI)/4) + .75) + Math.random()/4; return pos > 1 ? 1 : pos; }, wobble: function(pos) { - return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5; + return (-Math.cos(pos*Math.PI*(9*pos))/2) + .5; }, - pulse: function(pos, pulses) { - pulses = pulses || 5; - return ( - ((pos % (1/pulses)) * pulses).round() == 0 ? - ((pos * pulses * 2) - (pos * pulses * 2).floor()) : - 1 - ((pos * pulses * 2) - (pos * pulses * 2).floor()) - ); + pulse: function(pos, pulses) { + return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5; }, - spring: function(pos) { - return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6)); + spring: function(pos) { + return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6)); }, none: function(pos) { return 0; @@ -114,14 +109,14 @@ var Effect = { tagifyText: function(element) { var tagifyStyle = 'position:relative'; if (Prototype.Browser.IE) tagifyStyle += ';zoom:1'; - + element = $(element); $A(element.childNodes).each( function(child) { if (child.nodeType==3) { child.nodeValue.toArray().each( function(character) { element.insertBefore( new Element('span', {style: tagifyStyle}).update( - character == ' ' ? String.fromCharCode(160) : character), + character == ' ' ? String.fromCharCode(160) : character), child); }); Element.remove(child); @@ -130,13 +125,13 @@ var Effect = { }, multiple: function(element, effect) { var elements; - if (((typeof element == 'object') || - Object.isFunction(element)) && + if (((typeof element == 'object') || + Object.isFunction(element)) && (element.length)) elements = element; else elements = $(element).childNodes; - + var options = Object.extend({ speed: 0.1, delay: 0.0 @@ -158,7 +153,7 @@ var Effect = { var options = Object.extend({ queue: { position:'end', scope:(element.id || 'global'), limit: 1 } }, arguments[2] || { }); - Effect[element.visible() ? + Effect[element.visible() ? Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options); } }; @@ -170,20 +165,20 @@ Effect.DefaultOptions.transition = Effect.Transitions.sinoidal; Effect.ScopedQueue = Class.create(Enumerable, { initialize: function() { this.effects = []; - this.interval = null; + this.interval = null; }, _each: function(iterator) { this.effects._each(iterator); }, add: function(effect) { var timestamp = new Date().getTime(); - - var position = Object.isString(effect.options.queue) ? + + var position = Object.isString(effect.options.queue) ? effect.options.queue : effect.options.queue.position; - + switch(position) { case 'front': - // move unstarted effects after this effect + // move unstarted effects after this effect this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) { e.startOn += effect.finishOn; e.finishOn += effect.finishOn; @@ -197,13 +192,13 @@ Effect.ScopedQueue = Class.create(Enumerable, { timestamp = this.effects.pluck('finishOn').max() || timestamp; break; } - + effect.startOn += timestamp; effect.finishOn += timestamp; if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit)) this.effects.push(effect); - + if (!this.interval) this.interval = setInterval(this.loop.bind(this), 15); }, @@ -216,7 +211,7 @@ Effect.ScopedQueue = Class.create(Enumerable, { }, loop: function() { var timePos = new Date().getTime(); - for(var i=0, len=this.effects.length;i<len;i++) + for(var i=0, len=this.effects.length;i<len;i++) this.effects[i] && this.effects[i].loop(timePos); } }); @@ -225,7 +220,7 @@ Effect.Queues = { instances: $H(), get: function(queueName) { if (!Object.isString(queueName)) return queueName; - + return this.instances.get(queueName) || this.instances.set(queueName, new Effect.ScopedQueue()); } @@ -250,23 +245,35 @@ Effect.Base = Class.create({ this.fromToDelta = this.options.to-this.options.from; this.totalTime = this.finishOn-this.startOn; this.totalFrames = this.options.fps*this.options.duration; - - eval('this.render = function(pos){ '+ - 'if (this.state=="idle"){this.state="running";'+ - codeForEvent(this.options,'beforeSetup')+ - (this.setup ? 'this.setup();':'')+ - codeForEvent(this.options,'afterSetup')+ - '};if (this.state=="running"){'+ - 'pos=this.options.transition(pos)*'+this.fromToDelta+'+'+this.options.from+';'+ - 'this.position=pos;'+ - codeForEvent(this.options,'beforeUpdate')+ - (this.update ? 'this.update(pos);':'')+ - codeForEvent(this.options,'afterUpdate')+ - '}}'); - + + this.render = (function() { + function dispatch(effect, eventName) { + if (effect.options[eventName + 'Internal']) + effect.options[eventName + 'Internal'](effect); + if (effect.options[eventName]) + effect.options[eventName](effect); + } + + return function(pos) { + if (this.state === "idle") { + this.state = "running"; + dispatch(this, 'beforeSetup'); + if (this.setup) this.setup(); + dispatch(this, 'afterSetup'); + } + if (this.state === "running") { + pos = (this.options.transition(pos) * this.fromToDelta) + this.options.from; + this.position = pos; + dispatch(this, 'beforeUpdate'); + if (this.update) this.update(pos); + dispatch(this, 'afterUpdate'); + } + }; + })(); + this.event('beforeStart'); if (!this.options.sync) - Effect.Queues.get(Object.isString(this.options.queue) ? + Effect.Queues.get(Object.isString(this.options.queue) ? 'global' : this.options.queue.scope).add(this); }, loop: function(timePos) { @@ -275,9 +282,9 @@ Effect.Base = Class.create({ this.render(1.0); this.cancel(); this.event('beforeFinish'); - if (this.finish) this.finish(); + if (this.finish) this.finish(); this.event('afterFinish'); - return; + return; } var pos = (timePos - this.startOn) / this.totalTime, frame = (pos * this.totalFrames).round(); @@ -289,7 +296,7 @@ Effect.Base = Class.create({ }, cancel: function() { if (!this.options.sync) - Effect.Queues.get(Object.isString(this.options.queue) ? + Effect.Queues.get(Object.isString(this.options.queue) ? 'global' : this.options.queue.scope).remove(this); this.state = 'finished'; }, @@ -327,10 +334,10 @@ Effect.Parallel = Class.create(Effect.Base, { Effect.Tween = Class.create(Effect.Base, { initialize: function(object, from, to) { object = Object.isString(object) ? $(object) : object; - var args = $A(arguments), method = args.last(), + var args = $A(arguments), method = args.last(), options = args.length == 5 ? args[3] : null; this.method = Object.isFunction(method) ? method.bind(object) : - Object.isFunction(object[method]) ? object[method].bind(object) : + Object.isFunction(object[method]) ? object[method].bind(object) : function(value) { object[method] = value }; this.start(Object.extend({ from: from, to: to }, options || { })); }, @@ -394,7 +401,7 @@ Effect.Move = Class.create(Effect.Base, { // for backwards compatibility Effect.MoveBy = function(element, toTop, toLeft) { - return new Effect.Move(element, + return new Effect.Move(element, Object.extend({ x: toLeft, y: toTop }, arguments[3] || { })); }; @@ -416,15 +423,15 @@ Effect.Scale = Class.create(Effect.Base, { setup: function() { this.restoreAfterFinish = this.options.restoreAfterFinish || false; this.elementPositioning = this.element.getStyle('position'); - + this.originalStyle = { }; ['top','left','width','height','fontSize'].each( function(k) { this.originalStyle[k] = this.element.style[k]; }.bind(this)); - + this.originalTop = this.element.offsetTop; this.originalLeft = this.element.offsetLeft; - + var fontSize = this.element.getStyle('font-size') || '100%'; ['em','px','%','pt'].each( function(fontSizeType) { if (fontSize.indexOf(fontSizeType)>0) { @@ -432,9 +439,9 @@ Effect.Scale = Class.create(Effect.Base, { this.fontSizeType = fontSizeType; } }.bind(this)); - + this.factor = (this.options.scaleTo - this.options.scaleFrom)/100; - + this.dims = null; if (this.options.scaleMode=='box') this.dims = [this.element.offsetHeight, this.element.offsetWidth]; @@ -509,17 +516,16 @@ Effect.Highlight = Class.create(Effect.Base, { Effect.ScrollTo = function(element) { var options = arguments[1] || { }, - scrollOffsets = document.viewport.getScrollOffsets(), - elementOffsets = $(element).cumulativeOffset(), - max = (window.height || document.body.scrollHeight) - document.viewport.getHeight(); + scrollOffsets = document.viewport.getScrollOffsets(), + elementOffsets = $(element).cumulativeOffset(); if (options.offset) elementOffsets[1] += options.offset; return new Effect.Tween(null, scrollOffsets.top, - elementOffsets[1] > max ? max : elementOffsets[1], + elementOffsets[1], options, - function(p){ scrollTo(scrollOffsets.left, p.round()) } + function(p){ scrollTo(scrollOffsets.left, p.round()); } ); }; @@ -531,9 +537,9 @@ Effect.Fade = function(element) { var options = Object.extend({ from: element.getOpacity() || 1.0, to: 0.0, - afterFinishInternal: function(effect) { + afterFinishInternal: function(effect) { if (effect.options.to!=0) return; - effect.element.hide().setStyle({opacity: oldOpacity}); + effect.element.hide().setStyle({opacity: oldOpacity}); } }, arguments[1] || { }); return new Effect.Opacity(element,options); @@ -549,15 +555,15 @@ Effect.Appear = function(element) { effect.element.forceRerendering(); }, beforeSetup: function(effect) { - effect.element.setOpacity(effect.options.from).show(); + effect.element.setOpacity(effect.options.from).show(); }}, arguments[1] || { }); return new Effect.Opacity(element,options); }; Effect.Puff = function(element) { element = $(element); - var oldStyle = { - opacity: element.getInlineOpacity(), + var oldStyle = { + opacity: element.getInlineOpacity(), position: element.getStyle('position'), top: element.style.top, left: element.style.left, @@ -565,12 +571,12 @@ Effect.Puff = function(element) { height: element.style.height }; return new Effect.Parallel( - [ new Effect.Scale(element, 200, - { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), - new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], - Object.extend({ duration: 1.0, + [ new Effect.Scale(element, 200, + { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), + new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], + Object.extend({ duration: 1.0, beforeSetupInternal: function(effect) { - Position.absolutize(effect.effects[0].element) + Position.absolutize(effect.effects[0].element); }, afterFinishInternal: function(effect) { effect.effects[0].element.hide().setStyle(oldStyle); } @@ -582,12 +588,12 @@ Effect.BlindUp = function(element) { element = $(element); element.makeClipping(); return new Effect.Scale(element, 0, - Object.extend({ scaleContent: false, - scaleX: false, + Object.extend({ scaleContent: false, + scaleX: false, restoreAfterFinish: true, afterFinishInternal: function(effect) { effect.element.hide().undoClipping(); - } + } }, arguments[1] || { }) ); }; @@ -595,15 +601,15 @@ Effect.BlindUp = function(element) { Effect.BlindDown = function(element) { element = $(element); var elementDimensions = element.getDimensions(); - return new Effect.Scale(element, 100, Object.extend({ - scaleContent: false, + return new Effect.Scale(element, 100, Object.extend({ + scaleContent: false, scaleX: false, scaleFrom: 0, scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, restoreAfterFinish: true, afterSetup: function(effect) { - effect.element.makeClipping().setStyle({height: '0px'}).show(); - }, + effect.element.makeClipping().setStyle({height: '0px'}).show(); + }, afterFinishInternal: function(effect) { effect.element.undoClipping(); } @@ -618,16 +624,16 @@ Effect.SwitchOff = function(element) { from: 0, transition: Effect.Transitions.flicker, afterFinishInternal: function(effect) { - new Effect.Scale(effect.element, 1, { + new Effect.Scale(effect.element, 1, { duration: 0.3, scaleFromCenter: true, scaleX: false, scaleContent: false, restoreAfterFinish: true, - beforeSetup: function(effect) { + beforeSetup: function(effect) { effect.element.makePositioned().makeClipping(); }, afterFinishInternal: function(effect) { effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity}); } - }) + }); } }, arguments[1] || { })); }; @@ -639,16 +645,16 @@ Effect.DropOut = function(element) { left: element.getStyle('left'), opacity: element.getInlineOpacity() }; return new Effect.Parallel( - [ new Effect.Move(element, {x: 0, y: 100, sync: true }), + [ new Effect.Move(element, {x: 0, y: 100, sync: true }), new Effect.Opacity(element, { sync: true, to: 0.0 }) ], Object.extend( { duration: 0.5, beforeSetup: function(effect) { - effect.effects[0].element.makePositioned(); + effect.effects[0].element.makePositioned(); }, afterFinishInternal: function(effect) { effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle); - } + } }, arguments[1] || { })); }; @@ -676,7 +682,7 @@ Effect.Shake = function(element) { new Effect.Move(effect.element, { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) { effect.element.undoPositioned().setStyle(oldStyle); - }}) }}) }}) }}) }}) }}); + }}); }}); }}); }}); }}); }}); }; Effect.SlideDown = function(element) { @@ -684,9 +690,9 @@ Effect.SlideDown = function(element) { // SlideDown need to have the content of the element wrapped in a container element with fixed height! var oldInnerBottom = element.down().getStyle('bottom'); var elementDimensions = element.getDimensions(); - return new Effect.Scale(element, 100, Object.extend({ - scaleContent: false, - scaleX: false, + return new Effect.Scale(element, 100, Object.extend({ + scaleContent: false, + scaleX: false, scaleFrom: window.opera ? 0 : 1, scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, restoreAfterFinish: true, @@ -694,11 +700,11 @@ Effect.SlideDown = function(element) { effect.element.makePositioned(); effect.element.down().makePositioned(); if (window.opera) effect.element.setStyle({top: ''}); - effect.element.makeClipping().setStyle({height: '0px'}).show(); + effect.element.makeClipping().setStyle({height: '0px'}).show(); }, afterUpdateInternal: function(effect) { effect.element.down().setStyle({bottom: - (effect.dims[0] - effect.element.clientHeight) + 'px' }); + (effect.dims[0] - effect.element.clientHeight) + 'px' }); }, afterFinishInternal: function(effect) { effect.element.undoClipping().undoPositioned(); @@ -712,8 +718,8 @@ Effect.SlideUp = function(element) { var oldInnerBottom = element.down().getStyle('bottom'); var elementDimensions = element.getDimensions(); return new Effect.Scale(element, window.opera ? 0 : 1, - Object.extend({ scaleContent: false, - scaleX: false, + Object.extend({ scaleContent: false, + scaleX: false, scaleMode: 'box', scaleFrom: 100, scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, @@ -723,7 +729,7 @@ Effect.SlideUp = function(element) { effect.element.down().makePositioned(); if (window.opera) effect.element.setStyle({top: ''}); effect.element.makeClipping().show(); - }, + }, afterUpdateInternal: function(effect) { effect.element.down().setStyle({bottom: (effect.dims[0] - effect.element.clientHeight) + 'px' }); @@ -736,15 +742,15 @@ Effect.SlideUp = function(element) { ); }; -// Bug in opera makes the TD containing this element expand for a instance after finish +// Bug in opera makes the TD containing this element expand for a instance after finish Effect.Squish = function(element) { - return new Effect.Scale(element, window.opera ? 1 : 0, { + return new Effect.Scale(element, window.opera ? 1 : 0, { restoreAfterFinish: true, beforeSetup: function(effect) { - effect.element.makeClipping(); - }, + effect.element.makeClipping(); + }, afterFinishInternal: function(effect) { - effect.element.hide().undoClipping(); + effect.element.hide().undoClipping(); } }); }; @@ -764,13 +770,13 @@ Effect.Grow = function(element) { width: element.style.width, opacity: element.getInlineOpacity() }; - var dims = element.getDimensions(); + var dims = element.getDimensions(); var initialMoveX, initialMoveY; var moveX, moveY; - + switch (options.direction) { case 'top-left': - initialMoveX = initialMoveY = moveX = moveY = 0; + initialMoveX = initialMoveY = moveX = moveY = 0; break; case 'top-right': initialMoveX = dims.width; @@ -795,11 +801,11 @@ Effect.Grow = function(element) { moveY = -dims.height / 2; break; } - + return new Effect.Move(element, { x: initialMoveX, y: initialMoveY, - duration: 0.01, + duration: 0.01, beforeSetup: function(effect) { effect.element.hide().makeClipping().makePositioned(); }, @@ -808,17 +814,17 @@ Effect.Grow = function(element) { [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }), new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }), new Effect.Scale(effect.element, 100, { - scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, + scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true}) ], Object.extend({ beforeSetup: function(effect) { - effect.effects[0].element.setStyle({height: '0px'}).show(); + effect.effects[0].element.setStyle({height: '0px'}).show(); }, afterFinishInternal: function(effect) { - effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); + effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); } }, options) - ) + ); } }); }; @@ -840,7 +846,7 @@ Effect.Shrink = function(element) { var dims = element.getDimensions(); var moveX, moveY; - + switch (options.direction) { case 'top-left': moveX = moveY = 0; @@ -857,19 +863,19 @@ Effect.Shrink = function(element) { moveX = dims.width; moveY = dims.height; break; - case 'center': + case 'center': moveX = dims.width / 2; moveY = dims.height / 2; break; } - + return new Effect.Parallel( [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }), new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}), new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }) - ], Object.extend({ + ], Object.extend({ beforeStartInternal: function(effect) { - effect.effects[0].element.makePositioned().makeClipping(); + effect.effects[0].element.makePositioned().makeClipping(); }, afterFinishInternal: function(effect) { effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); } @@ -879,12 +885,14 @@ Effect.Shrink = function(element) { Effect.Pulsate = function(element) { element = $(element); - var options = arguments[1] || { }; - var oldOpacity = element.getInlineOpacity(); - var transition = options.transition || Effect.Transitions.sinoidal; - var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) }; - reverser.bind(transition); - return new Effect.Opacity(element, + var options = arguments[1] || { }, + oldOpacity = element.getInlineOpacity(), + transition = options.transition || Effect.Transitions.linear, + reverser = function(pos){ + return 1 - transition((-Math.cos((pos*(options.pulses||5)*2)*Math.PI)/2) + .5); + }; + + return new Effect.Opacity(element, Object.extend(Object.extend({ duration: 2.0, from: 0, afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); } }, options), {transition: reverser})); @@ -898,12 +906,12 @@ Effect.Fold = function(element) { width: element.style.width, height: element.style.height }; element.makeClipping(); - return new Effect.Scale(element, 5, Object.extend({ + return new Effect.Scale(element, 5, Object.extend({ scaleContent: false, scaleX: false, afterFinishInternal: function(effect) { - new Effect.Scale(element, 1, { - scaleContent: false, + new Effect.Scale(element, 1, { + scaleContent: false, scaleY: false, afterFinishInternal: function(effect) { effect.element.hide().undoClipping().setStyle(oldStyle); @@ -918,7 +926,7 @@ Effect.Morph = Class.create(Effect.Base, { var options = Object.extend({ style: { } }, arguments[1] || { }); - + if (!Object.isString(options.style)) this.style = $H(options.style); else { if (options.style.include(':')) @@ -936,18 +944,18 @@ Effect.Morph = Class.create(Effect.Base, { effect.transforms.each(function(transform) { effect.element.style[transform.style] = ''; }); - } + }; } } this.start(options); }, - + setup: function(){ function parseColor(color){ if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff'; color = color.parseColor(); return $R(0,2).map(function(i){ - return parseInt( color.slice(i*2+1,i*2+3), 16 ) + return parseInt( color.slice(i*2+1,i*2+3), 16 ); }); } this.transforms = this.style.map(function(pair){ @@ -967,9 +975,9 @@ Effect.Morph = Class.create(Effect.Base, { } var originalValue = this.element.getStyle(property); - return { - style: property.camelize(), - originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0), + return { + style: property.camelize(), + originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0), targetValue: unit=='color' ? parseColor(value) : value, unit: unit }; @@ -980,13 +988,13 @@ Effect.Morph = Class.create(Effect.Base, { transform.unit != 'color' && (isNaN(transform.originalValue) || isNaN(transform.targetValue)) ) - ) + ); }); }, update: function(position) { var style = { }, transform, i = this.transforms.length; while(i--) - style[(transform = this.transforms[i]).style] = + style[(transform = this.transforms[i]).style] = transform.unit=='color' ? '#'+ (Math.round(transform.originalValue[0]+ (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() + @@ -995,7 +1003,7 @@ Effect.Morph = Class.create(Effect.Base, { (Math.round(transform.originalValue[2]+ (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() : (transform.originalValue + - (transform.targetValue - transform.originalValue) * position).toFixed(3) + + (transform.targetValue - transform.originalValue) * position).toFixed(3) + (transform.unit === null ? '' : transform.unit); this.element.setStyle(style, true); } @@ -1032,7 +1040,7 @@ Effect.Transform = Class.create({ }); Element.CSS_PROPERTIES = $w( - 'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' + + 'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' + 'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' + 'borderRightColor borderRightStyle borderRightWidth borderSpacing ' + 'borderTopColor borderTopStyle borderTopWidth bottom clip color ' + @@ -1041,7 +1049,7 @@ Element.CSS_PROPERTIES = $w( 'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' + 'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' + 'right textIndent top width wordSpacing zIndex'); - + Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/; String.__parseStyleElement = document.createElement('div'); @@ -1053,11 +1061,11 @@ String.prototype.parseStyle = function(){ String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>'; style = String.__parseStyleElement.childNodes[0].style; } - + Element.CSS_PROPERTIES.each(function(property){ - if (style[property]) styleRules.set(property, style[property]); + if (style[property]) styleRules.set(property, style[property]); }); - + if (Prototype.Browser.IE && this.include('opacity')) styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]); @@ -1083,7 +1091,7 @@ if (document.defaultView && document.defaultView.getComputedStyle) { if (!styles.opacity) styles.opacity = element.getOpacity(); return styles; }; -}; +} Effect.Methods = { morph: function(element, style) { @@ -1092,7 +1100,7 @@ Effect.Methods = { return element; }, visualEffect: function(element, effect, options) { - element = $(element) + element = $(element); var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1); new Effect[klass](element, options); return element; @@ -1106,17 +1114,17 @@ Effect.Methods = { $w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+ 'pulsate shake puff squish switchOff dropOut').each( - function(effect) { + function(effect) { Effect.Methods[effect] = function(element, options){ element = $(element); Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options); return element; - } + }; } ); -$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each( +$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each( function(f) { Effect.Methods[f] = Element[f]; } ); -Element.addMethods(Effect.Methods); +Element.addMethods(Effect.Methods);
\ No newline at end of file diff --git a/framework/Web/Javascripts/source/scriptaculous-1.8.1/scriptaculous.js b/framework/Web/Javascripts/source/scriptaculous-1.8.2/scriptaculous.js index 6cfe36e8..3e5543bd 100644 --- a/framework/Web/Javascripts/source/scriptaculous-1.8.1/scriptaculous.js +++ b/framework/Web/Javascripts/source/scriptaculous-1.8.2/scriptaculous.js @@ -1,7 +1,7 @@ -// script.aculo.us scriptaculous.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008 +// script.aculo.us scriptaculous.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008 -// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) -// +// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including @@ -9,7 +9,7 @@ // 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. // @@ -24,35 +24,37 @@ // For details, see the script.aculo.us web site: http://script.aculo.us/ var Scriptaculous = { - Version: '1.8.1', + Version: '1.8.2', require: function(libraryName) { // inserting via DOM fails in Safari 2.0, so brute force approach document.write('<script type="text/javascript" src="'+libraryName+'"><\/script>'); }, - REQUIRED_PROTOTYPE: '1.6.0', + REQUIRED_PROTOTYPE: '1.6.0.3', load: function() { - function convertVersionString(versionString){ - var r = versionString.split('.'); - return parseInt(r[0])*100000 + parseInt(r[1])*1000 + parseInt(r[2]); + function convertVersionString(versionString) { + var v = versionString.replace(/_.*|\./g, ''); + v = parseInt(v + '0'.times(4-v.length)); + return versionString.indexOf('_') > -1 ? v-1 : v; } - - if((typeof Prototype=='undefined') || - (typeof Element == 'undefined') || + + if((typeof Prototype=='undefined') || + (typeof Element == 'undefined') || (typeof Element.Methods=='undefined') || - (convertVersionString(Prototype.Version) < + (convertVersionString(Prototype.Version) < convertVersionString(Scriptaculous.REQUIRED_PROTOTYPE))) throw("script.aculo.us requires the Prototype JavaScript framework >= " + Scriptaculous.REQUIRED_PROTOTYPE); - - $A(document.getElementsByTagName("script")).findAll( function(s) { - return (s.src && s.src.match(/scriptaculous\.js(\?.*)?$/)) - }).each( function(s) { - var path = s.src.replace(/scriptaculous\.js(\?.*)?$/,''); - var includes = s.src.match(/\?.*load=([a-z,]*)/); + + var js = /scriptaculous\.js(\?.*)?$/; + $$('head script[src]').findAll(function(s) { + return s.src.match(js); + }).each(function(s) { + var path = s.src.replace(js, ''), + includes = s.src.match(/\?.*load=([a-z,]*)/); (includes ? includes[1] : 'builder,effects,dragdrop,controls,slider,sound').split(',').each( function(include) { Scriptaculous.require(path+include+'.js') }); }); } -} +}; Scriptaculous.load();
\ No newline at end of file diff --git a/framework/Web/Javascripts/source/scriptaculous-1.8.1/slider.js b/framework/Web/Javascripts/source/scriptaculous-1.8.2/slider.js index cc46fe37..40b73711 100644 --- a/framework/Web/Javascripts/source/scriptaculous-1.8.1/slider.js +++ b/framework/Web/Javascripts/source/scriptaculous-1.8.2/slider.js @@ -1,6 +1,6 @@ -// script.aculo.us slider.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008 +// script.aculo.us slider.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008 -// Copyright (c) 2005-2007 Marty Haught, Thomas Fuchs +// Copyright (c) 2005-2008 Marty Haught, Thomas Fuchs // // script.aculo.us is freely distributable under the terms of an MIT-style license. // For details, see the script.aculo.us web site: http://script.aculo.us/ @@ -16,13 +16,13 @@ if (!Control) var Control = { }; Control.Slider = Class.create({ initialize: function(handle, track, options) { var slider = this; - + if (Object.isArray(handle)) { this.handles = handle.collect( function(e) { return $(e) }); } else { this.handles = [$(handle)]; } - + this.track = $(track); this.options = options || { }; @@ -30,7 +30,7 @@ Control.Slider = Class.create({ this.increment = this.options.increment || 1; this.step = parseInt(this.options.step || '1'); this.range = this.options.range || $R(0,1); - + this.value = 0; // assure backwards compat this.values = this.handles.map( function() { return 0 }); this.spans = this.options.spans ? this.options.spans.map(function(s){ return $(s) }) : false; @@ -45,13 +45,13 @@ Control.Slider = Class.create({ // Will be used to align the handle onto the track, if necessary 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 != 0 ? - this.handles[0].offsetHeight : this.handles[0].style.height.replace(/px$/,"")) : - (this.handles[0].offsetWidth != 0 ? this.handles[0].offsetWidth : + this.handleLength = this.isVertical() ? + (this.handles[0].offsetHeight != 0 ? + this.handles[0].offsetHeight : this.handles[0].style.height.replace(/px$/,"")) : + (this.handles[0].offsetWidth != 0 ? this.handles[0].offsetWidth : this.handles[0].style.width.replace(/px$/,"")); this.active = false; @@ -75,20 +75,20 @@ Control.Slider = Class.create({ this.handles.each( function(h,i) { i = slider.handles.length-1-i; slider.setValue(parseFloat( - (Object.isArray(slider.options.sliderValue) ? - slider.options.sliderValue[i] : slider.options.sliderValue) || + (Object.isArray(slider.options.sliderValue) ? + slider.options.sliderValue[i] : slider.options.sliderValue) || slider.range.start), i); h.makePositioned().observe("mousedown", slider.eventMouseDown); }); - + this.track.observe("mousedown", this.eventMouseDown); document.observe("mouseup", this.eventMouseUp); document.observe("mousemove", this.eventMouseMove); - + this.initialized = true; }, dispose: function() { - var slider = this; + var slider = this; Event.stopObserving(this.track, "mousedown", this.eventMouseDown); Event.stopObserving(document, "mouseup", this.eventMouseUp); Event.stopObserving(document, "mousemove", this.eventMouseMove); @@ -101,12 +101,12 @@ Control.Slider = Class.create({ }, 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) { @@ -114,7 +114,7 @@ Control.Slider = Class.create({ if (currentOffset <= offset){ newValue = v; offset = currentOffset; - } + } }); return newValue; } @@ -138,28 +138,28 @@ Control.Slider = Class.create({ sliderValue = this.getNearestValue(sliderValue); this.values[handleIdx] = sliderValue; this.value = this.values[0]; // assure backwards compat - - this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] = + + 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, + 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)) * + ((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) * (value - this.range.start)) + "px"; }, translateToValue: function(offset) { - return ((offset/(this.trackLength-this.handleLength) * + 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); + var v = this.values.sortBy(Prototype.K); range = range || 0; return $R(v[range],v[range+1]); }, @@ -167,12 +167,12 @@ Control.Slider = Class.create({ return(this.isVertical() ? this.alignY : this.alignX); }, maximumOffset: function(){ - return(this.isVertical() ? + return(this.isVertical() ? (this.track.offsetHeight != 0 ? this.track.offsetHeight : - this.track.style.height.replace(/px$/,"")) - this.alignY : - (this.track.offsetWidth != 0 ? this.track.offsetWidth : + this.track.style.height.replace(/px$/,"")) - this.alignY : + (this.track.offsetWidth != 0 ? this.track.offsetWidth : this.track.style.width.replace(/px$/,"")) - this.alignX); - }, + }, isVertical: function(){ return (this.axis == 'vertical'); }, @@ -184,7 +184,7 @@ Control.Slider = Class.create({ 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, + 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) { @@ -204,14 +204,14 @@ Control.Slider = Class.create({ if (Event.isLeftClick(event)) { if (!this.disabled){ this.active = true; - + var handle = Event.element(event); var pointer = [Event.pointerX(event), Event.pointerY(event)]; var track = handle; if (track==this.track) { - var offsets = Position.cumulativeOffset(this.track); + var offsets = Position.cumulativeOffset(this.track); this.event = event; - this.setValue(this.translateToValue( + this.setValue(this.translateToValue( (this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2) )); var offsets = Position.cumulativeOffset(this.activeHandle); @@ -219,14 +219,14 @@ Control.Slider = Class.create({ this.offsetY = (pointer[1] - offsets[1]); } else { // find the handle (prevents issues with Safari) - while((this.handles.indexOf(handle) == -1) && handle.parentNode) + while((this.handles.indexOf(handle) == -1) && handle.parentNode) handle = handle.parentNode; - + if (this.handles.indexOf(handle)!=-1) { this.activeHandle = handle; this.activeHandleIdx = this.handles.indexOf(this.activeHandle); this.updateStyles(); - + var offsets = Position.cumulativeOffset(this.activeHandle); this.offsetX = (pointer[0] - offsets[0]); this.offsetY = (pointer[1] - offsets[1]); @@ -261,15 +261,15 @@ Control.Slider = Class.create({ } 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) + if (this.initialized && this.options.onChange) this.options.onChange(this.values.length>1 ? this.values : this.value, this); this.event = null; } -}); +});
\ No newline at end of file diff --git a/framework/Web/Javascripts/source/scriptaculous-1.8.1/sound.js b/framework/Web/Javascripts/source/scriptaculous-1.8.2/sound.js index eba38432..e2487cde 100644 --- a/framework/Web/Javascripts/source/scriptaculous-1.8.1/sound.js +++ b/framework/Web/Javascripts/source/scriptaculous-1.8.2/sound.js @@ -1,6 +1,6 @@ -// script.aculo.us sound.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008 +// script.aculo.us sound.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008 -// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) // // Based on code created by Jules Gravinese (http://www.webveteran.com/) // @@ -23,23 +23,23 @@ Sound = { var options = Object.extend({ track: 'global', url: url, replace: false }, arguments[1] || {}); - + if(options.replace && this.tracks[options.track]) { $R(0, this.tracks[options.track].id).each(function(id){ var sound = $('sound_'+options.track+'_'+id); sound.Stop && sound.Stop(); sound.remove(); - }) + }); this.tracks[options.track] = null; } - + if(!this.tracks[options.track]) - this.tracks[options.track] = { id: 0 } + this.tracks[options.track] = { id: 0 }; else this.tracks[options.track].id++; - + options.id = this.tracks[options.track].id; - $$('body')[0].insert( + $$('body')[0].insert( Prototype.Browser.IE ? new Element('bgsound',{ id: 'sound_'+options.track+'_'+options.id, src: options.url, loop: 1, autostart: true @@ -49,7 +49,7 @@ Sound = { if(Prototype.Browser.Gecko && navigator.userAgent.indexOf("Win") > 0){ if(navigator.plugins && $A(navigator.plugins).detect(function(p){ return p.name.indexOf('QuickTime') != -1 })) - Sound.template = new Template('<object id="sound_#{track}_#{id}" width="0" height="0" type="audio/mpeg" data="#{url}"/>') + Sound.template = new Template('<object id="sound_#{track}_#{id}" width="0" height="0" type="audio/mpeg" data="#{url}"/>'); else - Sound.play = function(){} -} + Sound.play = function(){}; +}
\ No newline at end of file diff --git a/framework/Web/Services/TJsonService.php b/framework/Web/Services/TJsonService.php index 611a3c22..52997358 100644 --- a/framework/Web/Services/TJsonService.php +++ b/framework/Web/Services/TJsonService.php @@ -136,7 +136,7 @@ class TJsonService extends TService }
}
else
- throw new THttpException(404,'jsonservice_feed_unknown',$id);
+ throw new THttpException(404,'jsonservice_provider_unknown',$id);
}
/**
diff --git a/framework/Web/Services/TPageService.php b/framework/Web/Services/TPageService.php index 4d08ed4c..e4f9804c 100644 --- a/framework/Web/Services/TPageService.php +++ b/framework/Web/Services/TPageService.php @@ -119,7 +119,7 @@ class TPageService extends TService /**
* @var array list of initial page property values
*/
- private $_properties;
+ private $_properties=array();
/**
* @var boolean whether service is initialized
*/
@@ -172,7 +172,7 @@ class TPageService extends TService protected function applyConfiguration($config)
{
// initial page properties (to be set when page runs)
- $this->_properties=$config->getProperties();
+ $this->_properties=array_merge($this->_properties, $config->getProperties());
$this->getApplication()->getAuthorizationRules()->mergeWith($config->getRules());
$pagePath=$this->getRequestedPagePath();
// external configurations
@@ -857,4 +857,5 @@ class TPageConfiguration extends TComponent $this->_includes[$filePath]=array($configPagePath,$when);
}
}
-}
\ No newline at end of file +}
+
diff --git a/framework/Web/THttpResponse.php b/framework/Web/THttpResponse.php index fd45acf5..e3373855 100644 --- a/framework/Web/THttpResponse.php +++ b/framework/Web/THttpResponse.php @@ -1,576 +1,587 @@ -<?php -/** - * THttpResponse class - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2008 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web - */ - -/** - * Includes the THttpResponse adapter. - */ -Prado::using('System.Web.THttpResponseAdapter'); - -/** - * THttpResponse class - * - * THttpResponse implements the mechanism for sending output to client users. - * - * To output a string to client, use {@link write()}. By default, the output is - * buffered until {@link flush()} is called or the application ends. The output in - * the buffer can also be cleaned by {@link clear()}. To disable output buffering, - * set BufferOutput property to false. - * - * To send cookies to client, use {@link getCookies()}. - * To redirect client browser to a new URL, use {@link redirect()}. - * To send a file to client, use {@link writeFile()}. - * - * By default, THttpResponse is registered with {@link TApplication} as the - * response module. It can be accessed via {@link TApplication::getResponse()}. - * - * THttpResponse may be configured in application configuration file as follows - * - * <module id="response" class="System.Web.THttpResponse" CacheExpire="20" CacheControl="nocache" BufferOutput="true" /> - * - * where {@link getCacheExpire CacheExpire}, {@link getCacheControl CacheControl} - * and {@link getBufferOutput BufferOutput} are optional properties of THttpResponse. - * - * THttpResponse sends charset header if either {@link setCharset() Charset} - * or {@link TGlobalization::setCharset() TGlobalization.Charset} is set. - * - * Since 3.1.2, HTTP status code can be set with the {@link setStatusCode StatusCode} property. - * - * Note: Some HTTP Status codes can require additional header or body information. So, if you use {@link setStatusCode StatusCode} - * in your application, be sure to add theses informations. - * E.g : to make an http authentication : - * <code> - * public function clickAuth ($sender, $param) - * { - * $response=$this->getResponse(); - * $response->setStatusCode(401); - * $response->appendHeader('WWW-Authenticate: Basic realm="Test"'); - * } - * </code> - * - * This event handler will sent the 401 status code (Unauthorized) to the browser, with the WWW-Authenticate header field. This - * will force the browser to ask for a username and a password. - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @version $Id$ - * @package System.Web - * @since 3.0 - */ -class THttpResponse extends TModule implements ITextWriter -{ - /** - * @var The differents defined status code by RFC 2616 {@link http://www.faqs.org/rfcs/rfc2616} - */ - private static $HTTP_STATUS_CODES = array( - 100 => 'Continue', 101 => 'Switching Protocols', - 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', - 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 307 => 'Temporary Redirect', - 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Time-out', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Large', 415 => 'Unsupported Media Type', 416 => 'Requested range not satisfiable', 417 => 'Expectation Failed', - 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Time-out', 505 => 'HTTP Version not supported' - ); - - /** - * @var boolean whether to buffer output - */ - private $_bufferOutput=true; - /** - * @var boolean if the application is initialized - */ - private $_initialized=false; - /** - * @var THttpCookieCollection list of cookies to return - */ - private $_cookies=null; - /** - * @var integer response status code - */ - private $_status=200; - /** - * @var string reason correspond to status code - */ - private $_reason='OK'; - /** - * @var string HTML writer type - */ - private $_htmlWriterType='System.Web.UI.THtmlWriter'; - /** - * @var string content type - */ - private $_contentType=null; - /** - * @var string character set, e.g. UTF-8 - */ - private $_charset=''; - /** - * @var THttpResponseAdapter adapter. - */ - private $_adapter; - - /** - * Destructor. - * Flushes any existing content in buffer. - */ - public function __destruct() - { - //if($this->_bufferOutput) - // @ob_end_flush(); - } - - /** - * @param THttpResponseAdapter response adapter - */ - public function setAdapter(THttpResponseAdapter $adapter) - { - $this->_adapter=$adapter; - } - - /** - * @return THttpResponseAdapter response adapter, null if not exist. - */ - public function getAdapter() - { - return $this->_adapter; - } - - /** - * @return boolean true if adapter exists, false otherwise. - */ - public function getHasAdapter() - { - return !is_null($this->_adapter); - } - - /** - * Initializes the module. - * This method is required by IModule and is invoked by application. - * It starts output buffer if it is enabled. - * @param TXmlElement module configuration - */ - public function init($config) - { - if($this->_bufferOutput) - ob_start(); - $this->_initialized=true; - $this->getApplication()->setResponse($this); - } - - /** - * @return integer time-to-live for cached session pages in minutes, this has no effect for nocache limiter. Defaults to 180. - */ - public function getCacheExpire() - { - return session_cache_expire(); - } - - /** - * @param integer time-to-live for cached session pages in minutes, this has no effect for nocache limiter. - */ - public function setCacheExpire($value) - { - session_cache_expire(TPropertyValue::ensureInteger($value)); - } - - /** - * @return string cache control method to use for session pages - */ - public function getCacheControl() - { - return session_cache_limiter(); - } - - /** - * @param string cache control method to use for session pages. Valid values - * include none/nocache/private/private_no_expire/public - */ - public function setCacheControl($value) - { - session_cache_limiter(TPropertyValue::ensureEnum($value,array('none','nocache','private','private_no_expire','public'))); - } - - /** - * @return string content type, default is text/html - */ - public function setContentType($type) - { - $this->_contentType = $type; - } - - /** - * @return string current content type - */ - public function getContentType() - { - return $this->_contentType; - } - - /** - * @return string output charset. - */ - public function getCharset() - { - return $this->_charset; - } - - /** - * @param string output charset. - */ - public function setCharset($charset) - { - $this->_charset = $charset; - } - - /** - * @return boolean whether to enable output buffer - */ - public function getBufferOutput() - { - return $this->_bufferOutput; - } - - /** - * @param boolean whether to enable output buffer - * @throws TInvalidOperationException if session is started already - */ - public function setBufferOutput($value) - { - if($this->_initialized) - throw new TInvalidOperationException('httpresponse_bufferoutput_unchangeable'); - else - $this->_bufferOutput=TPropertyValue::ensureBoolean($value); - } - - /** - * @return integer HTTP status code, defaults to 200 - */ - public function getStatusCode() - { - return $this->_status; - } - - /** - * Set the HTTP status code for the response. - * The code and its reason will be sent to client using the currently requested http protocol version (see {@link THttpRequest::getHttpProtocolVersion}) - * Keep in mind that HTTP/1.0 clients might not understand all status codes from HTTP/1.1 - * - * @param integer HTTP status code - * @param string HTTP status reason, defaults to standard HTTP reasons - */ - public function setStatusCode($status, $reason=null) - { - $status=TPropertyValue::ensureInteger($status); - if(isset(self::$HTTP_STATUS_CODES[$status])) { - $this->_reason=self::$HTTP_STATUS_CODES[$status]; - }else{ - if($reason===null || $reason==='') { - throw new TInvalidDataValueException("response_status_reason_missing"); - } - $reason=TPropertyValue::ensureString($reason); - if(strpos($reason, "\r")!=false || strpos($reason, "\n")!=false) { - throw new TInvalidDataValueException("response_status_reason_barchars"); - } - $this->_reason=$reason; - } - $this->_status=$status; - } - - /** - * @param string HTTP status reason - */ - public function getStatusReason() { - return $this->_reason; - } - - /** - * @return THttpCookieCollection list of output cookies - */ - public function getCookies() - { - if($this->_cookies===null) - $this->_cookies=new THttpCookieCollection($this); - return $this->_cookies; - } - - /** - * Outputs a string. - * It may not be sent back to user immediately if output buffer is enabled. - * @param string string to be output - */ - public function write($str) - { - echo $str; - } - - /** - * Sends a file back to user. - * Make sure not to output anything else after calling this method. - * @param string file name - * @param string content to be set. If null, the content will be read from the server file pointed to by $fileName. - * @param string mime type of the content. - * @param array list of headers to be sent. Each array element represents a header string (e.g. 'Content-Type: text/plain'). - * @throws TInvalidDataValueException if the file cannot be found - */ - public function writeFile($fileName,$content=null,$mimeType=null,$headers=null) - { - static $defaultMimeTypes=array( - 'css'=>'text/css', - 'gif'=>'image/gif', - 'jpg'=>'image/jpeg', - 'jpeg'=>'image/jpeg', - 'htm'=>'text/html', - 'html'=>'text/html', - 'js'=>'javascript/js', - 'pdf'=>'application/pdf', - 'xls'=>'application/vnd.ms-excel', - ); - - if($mimeType===null) - { - $mimeType='text/plain'; - if(function_exists('mime_content_type')) - $mimeType=mime_content_type($fileName); - else if(($ext=strrchr($fileName,'.'))!==false) - { - $ext=substr($ext,1); - if(isset($defaultMimeTypes[$ext])) - $mimeType=$defaultMimeTypes[$ext]; - } - } - $fn=basename($fileName); - $this->sendHttpHeader(); - if(is_array($headers)) - { - foreach($headers as $h) - header($h); - } - else - { - header('Pragma: public'); - header('Expires: 0'); - header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); - } - header("Content-type: $mimeType"); - header('Content-Length: '.($content===null?filesize($fileName):strlen($content))); - header("Content-Disposition: attachment; filename=\"$fn\""); - header('Content-Transfer-Encoding: binary'); - if($content===null) - readfile($fileName); - else - echo $content; - } - - /** - * Redirects the browser to the specified URL. - * The current application will be terminated after this method is invoked. - * @param string URL to be redirected to. If the URL is a relative one, the base URL of - * the current request will be inserted at the beginning. - */ - public function redirect($url) - { - if($this->getHasAdapter()) - $this->_adapter->httpRedirect($url); - else - $this->httpRedirect($url); - } - - /** - * Redirect the browser to another URL and exists the current application. - * This method is used internally. Please use {@link redirect} instead. - * @param string URL to be redirected to. If the URL is a relative one, the base URL of - * the current request will be inserted at the beginning. - */ - public function httpRedirect($url) - { - if(!$this->getApplication()->getRequestCompleted()) - $this->getApplication()->onEndRequest(); - if($url[0]==='/') - $url=$this->getRequest()->getBaseUrl().$url; - header('Location: '.str_replace('&','&',$url)); - exit(); - } - - /** - * Reloads the current page. - * The effect of this method call is the same as user pressing the - * refresh button on his browser (without post data). - **/ - public function reload() - { - $this->redirect($this->getRequest()->getRequestUri()); - } - - /** - * Flush the response contents and headers. - */ - public function flush() - { - if($this->getHasAdapter()) - $this->_adapter->flushContent(); - else - $this->flushContent(); - } - - /** - * Outputs the buffered content, sends content-type and charset header. - * This method is used internally. Please use {@link flush} instead. - */ - public function flushContent() - { - Prado::trace("Flushing output",'System.Web.THttpResponse'); - $this->sendHttpHeader(); - $this->sendContentTypeHeader(); - if($this->_bufferOutput) - ob_flush(); - } - - /** - * Send the HTTP header with the status code (defaults to 200) and status reason (defaults to OK) - */ - protected function sendHttpHeader () - { - if (($version=$this->getRequest()->getHttpProtocolVersion())==='') - header (' ', true, $this->_status); - else - header($version.' '.$this->_status.' '.$this->_reason, true, $this->_status); - } - - /** - * Sends content type header if charset is not empty. - */ - protected function sendContentTypeHeader() - { - $charset=$this->getCharset(); - if($charset==='' && ($globalization=$this->getApplication()->getGlobalization(false))!==null) - $charset=$globalization->getCharset(); - if($charset!=='') - { - $contentType=$this->_contentType===null?'text/html':$this->_contentType; - $this->appendHeader('Content-Type: '.$contentType.';charset='.$charset); - } - else if($this->_contentType!==null) - $this->appendHeader('Content-Type: '.$this->_contentType.';charset=UTF-8'); - } - - /** - * Returns the content in the output buffer. - * The buffer will NOT be cleared after calling this method. - * Use {@link clear()} is you want to clear the buffer. - * @return string output that is in the buffer. - */ - public function getContents() - { - Prado::trace("Retrieving output",'System.Web.THttpResponse'); - return $this->_bufferOutput?ob_get_contents():''; - } - - /** - * Clears any existing buffered content. - */ - public function clear() - { - if($this->_bufferOutput) - ob_clean(); - Prado::trace("Clearing output",'System.Web.THttpResponse'); - } - - /** - * Sends a header. - * @param string header - */ - public function appendHeader($value) - { - Prado::trace("Sending header '$value'",'System.Web.THttpResponse'); - header($value); - } - - /** - * Writes a log message into error log. - * This method is simple wrapper of PHP function error_log. - * @param string The error message that should be logged - * @param integer where the error should go - * @param string The destination. Its meaning depends on the message parameter as described above - * @param string The extra headers. It's used when the message parameter is set to 1. This message type uses the same internal function as mail() does. - * @see http://us2.php.net/manual/en/function.error-log.php - */ - public function appendLog($message,$messageType=0,$destination='',$extraHeaders='') - { - error_log($message,$messageType,$destination,$extraHeaders); - } - - /** - * Sends a cookie. - * Do not call this method directly. Operate with the result of {@link getCookies} instead. - * @param THttpCookie cook to be sent - */ - public function addCookie($cookie) - { - $request=$this->getRequest(); - if($request->getEnableCookieValidation()) - { - $value=$this->getApplication()->getSecurityManager()->hashData($cookie->getValue()); - setcookie($cookie->getName(),$value,$cookie->getExpire(),$cookie->getPath(),$cookie->getDomain(),$cookie->getSecure()); - } - else - setcookie($cookie->getName(),$cookie->getValue(),$cookie->getExpire(),$cookie->getPath(),$cookie->getDomain(),$cookie->getSecure()); - } - - /** - * Deletes a cookie. - * Do not call this method directly. Operate with the result of {@link getCookies} instead. - * @param THttpCookie cook to be deleted - */ - public function removeCookie($cookie) - { - setcookie($cookie->getName(),null,0,$cookie->getPath(),$cookie->getDomain(),$cookie->getSecure()); - } - - /** - * @return string the type of HTML writer to be used, defaults to THtmlWriter - */ - public function getHtmlWriterType() - { - return $this->_htmlWriterType; - } - - /** - * @param string the type of HTML writer to be used, may be the class name or the namespace - */ - public function setHtmlWriterType($value) - { - $this->_htmlWriterType=$value; - } - - /** - * Creates a new instance of HTML writer. - * If the type of the HTML writer is not supplied, {@link getHtmlWriterType HtmlWriterType} will be assumed. - * @param string type of the HTML writer to be created. If null, {@link getHtmlWriterType HtmlWriterType} will be assumed. - */ - public function createHtmlWriter($type=null) - { - if($type===null) - $type=$this->getHtmlWriterType(); - if($this->getHasAdapter()) - return $this->_adapter->createNewHtmlWriter($type, $this); - else - return $this->createNewHtmlWriter($type, $this); - } - - /** - * Create a new html writer instance. - * This method is used internally. Please use {@link createHtmlWriter} instead. - * @param string type of HTML writer to be created. - * @param ITextWriter text writer holding the contents. - */ - public function createNewHtmlWriter($type, $writer) - { - return Prado::createComponent($type, $writer); - } -} - +<?php
+/**
+ * THttpResponse class
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005-2008 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Web
+ */
+
+/**
+ * Includes the THttpResponse adapter.
+ */
+Prado::using('System.Web.THttpResponseAdapter');
+
+/**
+ * THttpResponse class
+ *
+ * THttpResponse implements the mechanism for sending output to client users.
+ *
+ * To output a string to client, use {@link write()}. By default, the output is
+ * buffered until {@link flush()} is called or the application ends. The output in
+ * the buffer can also be cleaned by {@link clear()}. To disable output buffering,
+ * set BufferOutput property to false.
+ *
+ * To send cookies to client, use {@link getCookies()}.
+ * To redirect client browser to a new URL, use {@link redirect()}.
+ * To send a file to client, use {@link writeFile()}.
+ *
+ * By default, THttpResponse is registered with {@link TApplication} as the
+ * response module. It can be accessed via {@link TApplication::getResponse()}.
+ *
+ * THttpResponse may be configured in application configuration file as follows
+ *
+ * <module id="response" class="System.Web.THttpResponse" CacheExpire="20" CacheControl="nocache" BufferOutput="true" />
+ *
+ * where {@link getCacheExpire CacheExpire}, {@link getCacheControl CacheControl}
+ * and {@link getBufferOutput BufferOutput} are optional properties of THttpResponse.
+ *
+ * THttpResponse sends charset header if either {@link setCharset() Charset}
+ * or {@link TGlobalization::setCharset() TGlobalization.Charset} is set.
+ *
+ * Since 3.1.2, HTTP status code can be set with the {@link setStatusCode StatusCode} property.
+ *
+ * Note: Some HTTP Status codes can require additional header or body information. So, if you use {@link setStatusCode StatusCode}
+ * in your application, be sure to add theses informations.
+ * E.g : to make an http authentication :
+ * <code>
+ * public function clickAuth ($sender, $param)
+ * {
+ * $response=$this->getResponse();
+ * $response->setStatusCode(401);
+ * $response->appendHeader('WWW-Authenticate: Basic realm="Test"');
+ * }
+ * </code>
+ *
+ * This event handler will sent the 401 status code (Unauthorized) to the browser, with the WWW-Authenticate header field. This
+ * will force the browser to ask for a username and a password.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id$
+ * @package System.Web
+ * @since 3.0
+ */
+class THttpResponse extends TModule implements ITextWriter
+{
+ /**
+ * @var The differents defined status code by RFC 2616 {@link http://www.faqs.org/rfcs/rfc2616}
+ */
+ private static $HTTP_STATUS_CODES = array(
+ 100 => 'Continue', 101 => 'Switching Protocols',
+ 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content',
+ 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 307 => 'Temporary Redirect',
+ 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Time-out', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Large', 415 => 'Unsupported Media Type', 416 => 'Requested range not satisfiable', 417 => 'Expectation Failed',
+ 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Time-out', 505 => 'HTTP Version not supported'
+ );
+
+ /**
+ * @var boolean whether to buffer output
+ */
+ private $_bufferOutput=true;
+ /**
+ * @var boolean if the application is initialized
+ */
+ private $_initialized=false;
+ /**
+ * @var THttpCookieCollection list of cookies to return
+ */
+ private $_cookies=null;
+ /**
+ * @var integer response status code
+ */
+ private $_status=200;
+ /**
+ * @var string reason correspond to status code
+ */
+ private $_reason='OK';
+ /**
+ * @var string HTML writer type
+ */
+ private $_htmlWriterType='System.Web.UI.THtmlWriter';
+ /**
+ * @var string content type
+ */
+ private $_contentType=null;
+ /**
+ * @var string character set, e.g. UTF-8
+ */
+ private $_charset='';
+ /**
+ * @var THttpResponseAdapter adapter.
+ */
+ private $_adapter;
+
+ /**
+ * Destructor.
+ * Flushes any existing content in buffer.
+ */
+ public function __destruct()
+ {
+ //if($this->_bufferOutput)
+ // @ob_end_flush();
+ }
+
+ /**
+ * @param THttpResponseAdapter response adapter
+ */
+ public function setAdapter(THttpResponseAdapter $adapter)
+ {
+ $this->_adapter=$adapter;
+ }
+
+ /**
+ * @return THttpResponseAdapter response adapter, null if not exist.
+ */
+ public function getAdapter()
+ {
+ return $this->_adapter;
+ }
+
+ /**
+ * @return boolean true if adapter exists, false otherwise.
+ */
+ public function getHasAdapter()
+ {
+ return $this->_adapter!==null;
+ }
+
+ /**
+ * Initializes the module.
+ * This method is required by IModule and is invoked by application.
+ * It starts output buffer if it is enabled.
+ * @param TXmlElement module configuration
+ */
+ public function init($config)
+ {
+ if($this->_bufferOutput)
+ ob_start();
+ $this->_initialized=true;
+ $this->getApplication()->setResponse($this);
+ }
+
+ /**
+ * @return integer time-to-live for cached session pages in minutes, this has no effect for nocache limiter. Defaults to 180.
+ */
+ public function getCacheExpire()
+ {
+ return session_cache_expire();
+ }
+
+ /**
+ * @param integer time-to-live for cached session pages in minutes, this has no effect for nocache limiter.
+ */
+ public function setCacheExpire($value)
+ {
+ session_cache_expire(TPropertyValue::ensureInteger($value));
+ }
+
+ /**
+ * @return string cache control method to use for session pages
+ */
+ public function getCacheControl()
+ {
+ return session_cache_limiter();
+ }
+
+ /**
+ * @param string cache control method to use for session pages. Valid values
+ * include none/nocache/private/private_no_expire/public
+ */
+ public function setCacheControl($value)
+ {
+ session_cache_limiter(TPropertyValue::ensureEnum($value,array('none','nocache','private','private_no_expire','public')));
+ }
+
+ /**
+ * @return string content type, default is text/html
+ */
+ public function setContentType($type)
+ {
+ $this->_contentType = $type;
+ }
+
+ /**
+ * @return string current content type
+ */
+ public function getContentType()
+ {
+ return $this->_contentType;
+ }
+
+ /**
+ * @return string output charset.
+ */
+ public function getCharset()
+ {
+ return $this->_charset;
+ }
+
+ /**
+ * @param string output charset.
+ */
+ public function setCharset($charset)
+ {
+ $this->_charset = $charset;
+ }
+
+ /**
+ * @return boolean whether to enable output buffer
+ */
+ public function getBufferOutput()
+ {
+ return $this->_bufferOutput;
+ }
+
+ /**
+ * @param boolean whether to enable output buffer
+ * @throws TInvalidOperationException if session is started already
+ */
+ public function setBufferOutput($value)
+ {
+ if($this->_initialized)
+ throw new TInvalidOperationException('httpresponse_bufferoutput_unchangeable');
+ else
+ $this->_bufferOutput=TPropertyValue::ensureBoolean($value);
+ }
+
+ /**
+ * @return integer HTTP status code, defaults to 200
+ */
+ public function getStatusCode()
+ {
+ return $this->_status;
+ }
+
+ /**
+ * Set the HTTP status code for the response.
+ * The code and its reason will be sent to client using the currently requested http protocol version (see {@link THttpRequest::getHttpProtocolVersion})
+ * Keep in mind that HTTP/1.0 clients might not understand all status codes from HTTP/1.1
+ *
+ * @param integer HTTP status code
+ * @param string HTTP status reason, defaults to standard HTTP reasons
+ */
+ public function setStatusCode($status, $reason=null)
+ {
+ $status=TPropertyValue::ensureInteger($status);
+ if(isset(self::$HTTP_STATUS_CODES[$status])) {
+ $this->_reason=self::$HTTP_STATUS_CODES[$status];
+ }else{
+ if($reason===null || $reason==='') {
+ throw new TInvalidDataValueException("response_status_reason_missing");
+ }
+ $reason=TPropertyValue::ensureString($reason);
+ if(strpos($reason, "\r")!=false || strpos($reason, "\n")!=false) {
+ throw new TInvalidDataValueException("response_status_reason_barchars");
+ }
+ $this->_reason=$reason;
+ }
+ $this->_status=$status;
+ }
+
+ /**
+ * @param string HTTP status reason
+ */
+ public function getStatusReason() {
+ return $this->_reason;
+ }
+
+ /**
+ * @return THttpCookieCollection list of output cookies
+ */
+ public function getCookies()
+ {
+ if($this->_cookies===null)
+ $this->_cookies=new THttpCookieCollection($this);
+ return $this->_cookies;
+ }
+
+ /**
+ * Outputs a string.
+ * It may not be sent back to user immediately if output buffer is enabled.
+ * @param string string to be output
+ */
+ public function write($str)
+ {
+ echo $str;
+ }
+
+ /**
+ * Sends a file back to user.
+ * Make sure not to output anything else after calling this method.
+ * @param string file name
+ * @param string content to be set. If null, the content will be read from the server file pointed to by $fileName.
+ * @param string mime type of the content.
+ * @param array list of headers to be sent. Each array element represents a header string (e.g. 'Content-Type: text/plain').
+ * @throws TInvalidDataValueException if the file cannot be found
+ */
+ public function writeFile($fileName,$content=null,$mimeType=null,$headers=null)
+ {
+ static $defaultMimeTypes=array(
+ 'css'=>'text/css',
+ 'gif'=>'image/gif',
+ 'jpg'=>'image/jpeg',
+ 'jpeg'=>'image/jpeg',
+ 'htm'=>'text/html',
+ 'html'=>'text/html',
+ 'js'=>'javascript/js',
+ 'pdf'=>'application/pdf',
+ 'xls'=>'application/vnd.ms-excel',
+ );
+
+ if($mimeType===null)
+ {
+ $mimeType='text/plain';
+ if(function_exists('mime_content_type'))
+ $mimeType=mime_content_type($fileName);
+ else if(($ext=strrchr($fileName,'.'))!==false)
+ {
+ $ext=substr($ext,1);
+ if(isset($defaultMimeTypes[$ext]))
+ $mimeType=$defaultMimeTypes[$ext];
+ }
+ }
+ $fn=basename($fileName);
+ $this->sendHttpHeader();
+ if(is_array($headers))
+ {
+ foreach($headers as $h)
+ header($h);
+ }
+ else
+ {
+ header('Pragma: public');
+ header('Expires: 0');
+ header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
+ }
+ header("Content-type: $mimeType");
+ header('Content-Length: '.($content===null?filesize($fileName):strlen($content)));
+ header("Content-Disposition: attachment; filename=\"$fn\"");
+ header('Content-Transfer-Encoding: binary');
+ if($content===null)
+ readfile($fileName);
+ else
+ echo $content;
+ }
+
+ /**
+ * Redirects the browser to the specified URL.
+ * The current application will be terminated after this method is invoked.
+ * @param string URL to be redirected to. If the URL is a relative one, the base URL of
+ * the current request will be inserted at the beginning.
+ */
+ public function redirect($url)
+ {
+ if($this->getHasAdapter())
+ $this->_adapter->httpRedirect($url);
+ else
+ $this->httpRedirect($url);
+ }
+
+ /**
+ * Redirect the browser to another URL and exists the current application.
+ * This method is used internally. Please use {@link redirect} instead.
+ *
+ * @since 3.1.5
+ * You can set the set {@link setStatusCode StatusCode} to a value between 300 and 399 before
+ * calling this function to change the type of redirection.
+ * If not specified, StatusCode will be 302 (Found) by default
+ *
+ * @param string URL to be redirected to. If the URL is a relative one, the base URL of
+ * the current request will be inserted at the beginning.
+ */
+ public function httpRedirect($url)
+ {
+ if(!$this->getApplication()->getRequestCompleted())
+ $this->getApplication()->onEndRequest();
+ if($url[0]==='/')
+ $url=$this->getRequest()->getBaseUrl().$url;
+ if ($this->_status >= 300 && $this->_status < 400)
+ // The status code has been modified to a valid redirection status, send it
+ header('Location: '.str_replace('&','&',$url), true, $this->_status);
+ else
+ header('Location: '.str_replace('&','&',$url));
+
+ exit();
+ }
+
+ /**
+ * Reloads the current page.
+ * The effect of this method call is the same as user pressing the
+ * refresh button on his browser (without post data).
+ **/
+ public function reload()
+ {
+ $this->redirect($this->getRequest()->getRequestUri());
+ }
+
+ /**
+ * Flush the response contents and headers.
+ */
+ public function flush()
+ {
+ if($this->getHasAdapter())
+ $this->_adapter->flushContent();
+ else
+ $this->flushContent();
+ }
+
+ /**
+ * Outputs the buffered content, sends content-type and charset header.
+ * This method is used internally. Please use {@link flush} instead.
+ */
+ public function flushContent()
+ {
+ Prado::trace("Flushing output",'System.Web.THttpResponse');
+ $this->sendHttpHeader();
+ $this->sendContentTypeHeader();
+ if($this->_bufferOutput)
+ ob_flush();
+ }
+
+ /**
+ * Send the HTTP header with the status code (defaults to 200) and status reason (defaults to OK)
+ */
+ protected function sendHttpHeader ()
+ {
+ if (($version=$this->getRequest()->getHttpProtocolVersion())==='')
+ header (' ', true, $this->_status);
+ else
+ header($version.' '.$this->_status.' '.$this->_reason, true, $this->_status);
+ }
+
+ /**
+ * Sends content type header if charset is not empty.
+ */
+ protected function sendContentTypeHeader()
+ {
+ $charset=$this->getCharset();
+ if($charset==='' && ($globalization=$this->getApplication()->getGlobalization(false))!==null)
+ $charset=$globalization->getCharset();
+ if($charset!=='')
+ {
+ $contentType=$this->_contentType===null?'text/html':$this->_contentType;
+ $this->appendHeader('Content-Type: '.$contentType.';charset='.$charset);
+ }
+ else if($this->_contentType!==null)
+ $this->appendHeader('Content-Type: '.$this->_contentType.';charset=UTF-8');
+ }
+
+ /**
+ * Returns the content in the output buffer.
+ * The buffer will NOT be cleared after calling this method.
+ * Use {@link clear()} is you want to clear the buffer.
+ * @return string output that is in the buffer.
+ */
+ public function getContents()
+ {
+ Prado::trace("Retrieving output",'System.Web.THttpResponse');
+ return $this->_bufferOutput?ob_get_contents():'';
+ }
+
+ /**
+ * Clears any existing buffered content.
+ */
+ public function clear()
+ {
+ if($this->_bufferOutput)
+ ob_clean();
+ Prado::trace("Clearing output",'System.Web.THttpResponse');
+ }
+
+ /**
+ * Sends a header.
+ * @param string header
+ */
+ public function appendHeader($value)
+ {
+ Prado::trace("Sending header '$value'",'System.Web.THttpResponse');
+ header($value);
+ }
+
+ /**
+ * Writes a log message into error log.
+ * This method is simple wrapper of PHP function error_log.
+ * @param string The error message that should be logged
+ * @param integer where the error should go
+ * @param string The destination. Its meaning depends on the message parameter as described above
+ * @param string The extra headers. It's used when the message parameter is set to 1. This message type uses the same internal function as mail() does.
+ * @see http://us2.php.net/manual/en/function.error-log.php
+ */
+ public function appendLog($message,$messageType=0,$destination='',$extraHeaders='')
+ {
+ error_log($message,$messageType,$destination,$extraHeaders);
+ }
+
+ /**
+ * Sends a cookie.
+ * Do not call this method directly. Operate with the result of {@link getCookies} instead.
+ * @param THttpCookie cook to be sent
+ */
+ public function addCookie($cookie)
+ {
+ $request=$this->getRequest();
+ if($request->getEnableCookieValidation())
+ {
+ $value=$this->getApplication()->getSecurityManager()->hashData($cookie->getValue());
+ setcookie($cookie->getName(),$value,$cookie->getExpire(),$cookie->getPath(),$cookie->getDomain(),$cookie->getSecure());
+ }
+ else
+ setcookie($cookie->getName(),$cookie->getValue(),$cookie->getExpire(),$cookie->getPath(),$cookie->getDomain(),$cookie->getSecure());
+ }
+
+ /**
+ * Deletes a cookie.
+ * Do not call this method directly. Operate with the result of {@link getCookies} instead.
+ * @param THttpCookie cook to be deleted
+ */
+ public function removeCookie($cookie)
+ {
+ setcookie($cookie->getName(),null,0,$cookie->getPath(),$cookie->getDomain(),$cookie->getSecure());
+ }
+
+ /**
+ * @return string the type of HTML writer to be used, defaults to THtmlWriter
+ */
+ public function getHtmlWriterType()
+ {
+ return $this->_htmlWriterType;
+ }
+
+ /**
+ * @param string the type of HTML writer to be used, may be the class name or the namespace
+ */
+ public function setHtmlWriterType($value)
+ {
+ $this->_htmlWriterType=$value;
+ }
+
+ /**
+ * Creates a new instance of HTML writer.
+ * If the type of the HTML writer is not supplied, {@link getHtmlWriterType HtmlWriterType} will be assumed.
+ * @param string type of the HTML writer to be created. If null, {@link getHtmlWriterType HtmlWriterType} will be assumed.
+ */
+ public function createHtmlWriter($type=null)
+ {
+ if($type===null)
+ $type=$this->getHtmlWriterType();
+ if($this->getHasAdapter())
+ return $this->_adapter->createNewHtmlWriter($type, $this);
+ else
+ return $this->createNewHtmlWriter($type, $this);
+ }
+
+ /**
+ * Create a new html writer instance.
+ * This method is used internally. Please use {@link createHtmlWriter} instead.
+ * @param string type of HTML writer to be created.
+ * @param ITextWriter text writer holding the contents.
+ */
+ public function createNewHtmlWriter($type, $writer)
+ {
+ return Prado::createComponent($type, $writer);
+ }
+}
+
diff --git a/framework/Web/TUrlMapping.php b/framework/Web/TUrlMapping.php index 30e62496..09e45d99 100644 --- a/framework/Web/TUrlMapping.php +++ b/framework/Web/TUrlMapping.php @@ -4,7 +4,7 @@ *
* @author Wei Zhuo <weizhuo[at]gamil[dot]com>
* @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2008 PradoSoft
+ * @copyright Copyright © 2005-2008 PradoSoft * @license http://www.pradosoft.com/license/
* @version $Id$
* @package System.Web
@@ -293,7 +293,7 @@ class TUrlMapping extends TUrlManager $params[$key]=$value;
}
if (!$pattern->getIsWildCardPattern())
- $params[$pattern->getServiceID()]=$pattern->getServiceParameter();
+ $params[$pattern->getServiceID()]=$pattern->getServiceParameter();
return $params;
}
}
@@ -328,8 +328,8 @@ class TUrlMapping extends TUrlManager if(!(is_array($getItems) || ($getItems instanceof Traversable)))
$getItems=array();
$key=$serviceID.':'.$serviceParam;
- $wildCardKey = ($pos=strrpos($serviceParam,'.'))!==false ?
- $serviceID.':'.substr($serviceParam,0,$pos).'.*' : $serviceID.':*';
+ $wildCardKey = ($pos=strrpos($serviceParam,'.'))!==false ? + $serviceID.':'.substr($serviceParam,0,$pos).'.*' : $serviceID.':*'; if(isset($this->_constructRules[$key]))
{
foreach($this->_constructRules[$key] as $rule)
@@ -337,16 +337,16 @@ class TUrlMapping extends TUrlManager if($rule->supportCustomUrl($getItems))
return $rule->constructUrl($getItems,$encodeAmpersand,$encodeGetItems);
}
- }
- elseif(isset($this->_constructRules[$wildCardKey]))
- {
+ } + elseif(isset($this->_constructRules[$wildCardKey])) + { foreach($this->_constructRules[$wildCardKey] as $rule)
{
if($rule->supportCustomUrl($getItems))
- {
- $getItems['*']= $pos ? substr($serviceParam,$pos+1) : $serviceParam;
+ { + $getItems['*']= $pos ? substr($serviceParam,$pos+1) : $serviceParam; return $rule->constructUrl($getItems,$encodeAmpersand,$encodeGetItems);
- }
+ } }
}
}
@@ -408,6 +408,42 @@ class TUrlMapping extends TUrlManager * The {@link setServiceParameter ServiceParameter} and {@link setServiceID ServiceID}
* (the default ID is 'page') set the service parameter and service id respectively.
*
+ * Since 3.1.4 you can also use simplyfied wildcard patterns to match multiple + * ServiceParameters with a single rule. The pattern must contain the placeholder + * {*} for the ServiceParameter. For example + * + * <url ServiceParameter="adminpages.*" pattern="admin/{*}" /> + * + * This rule will match an URL like <tt>http://example.com/index.php/admin/edituser</tt> + * and resolve it to the page Application.pages.admin.edituser. The wildcard matching + * is non-recursive. That means you have to add a rule for every subdirectory you + * want to access pages in: + * + * <url ServiceParameter="adminpages.users.*" pattern="useradmin/{*}" /> + * + * It is still possible to define an explicit rule for a page in the wildcard path. + * This rule has to preceed the wildcard rule. + * + * You can also use parameters with wildcard patterns. The parameters are then + * available with every matching page: + * + * <url ServiceParameter="adminpages.*" pattern="admin/{*}/{id}" parameters.id="\d+" /> + * + * To enable automatic parameter encoding in a path format fro wildcard patterns you can set + * {@setUrlFormat UrlFormat} to 'Path': + * + * <url ServiceParameter="adminpages.*" pattern="admin/{*}" UrlFormat="Path" /> + * + * This will create and parse URLs of the form + * <tt>.../index.php/admin/listuser/param1/value1/param2/value2</tt>. + * + * Use {@setUrlParamSeparator} to define another separator character between parameter + * name and value. Parameter/value pairs are always separated by a '/'. + * + * <url ServiceParameter="adminpages.*" pattern="admin/{*}" UrlFormat="Path" UrlParamSeparator="-" /> + * + * <tt>.../index.php/admin/listuser/param1-value1/param2-value2</tt>. + * * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
* @version $Id$
* @package System.Web
@@ -441,9 +477,13 @@ class TUrlMappingPattern extends TComponent private $_manager;
private $_caseSensitive=true;
-
+ private $_isWildCardPattern=false;
+ private $_urlFormat=THttpRequestUrlFormat::Get;
+
+ private $_separator='/';
+
/**
* Constructor.
* @param TUrlManager the URL manager instance
@@ -472,8 +512,8 @@ class TUrlMappingPattern extends TComponent {
if($this->_serviceParameter===null)
throw new TConfigurationException('urlmappingpattern_serviceparameter_required', $this->getPattern());
- if(strpos($this->_serviceParameter,'*')!==false)
- $this->_isWildCardPattern=true;
+ if(strpos($this->_serviceParameter,'*')!==false) + $this->_isWildCardPattern=true; }
/**
@@ -490,15 +530,19 @@ class TUrlMappingPattern extends TComponent $params[]='{'.$key.'}';
$values[]='(?P<'.$key.'>'.$value.')';
}
- if ($this->getIsWildCardPattern()) {
- $params[]='{*}';
- // service parameter must not contain '=' and '/'
- $values[]='(?P<'.$this->getServiceID().'>[^=/]+)';
- }
+ if ($this->getIsWildCardPattern()) { + $params[]='{*}'; + // service parameter must not contain '=' and '/' + $values[]='(?P<'.$this->getServiceID().'>[^=/]+)'; + } $params[]='/';
$values[]='\\/';
$regexp=str_replace($params,$values,trim($this->getPattern(),'/').'/');
- $regexp='/^'.$regexp.'$/u';
+ if ($this->_urlFormat===THttpRequestUrlFormat::Get) + $regexp='/^'.$regexp.'$/u';
+ else + $regexp='/^'.$regexp.'(?P<urlparams>.*)$/u';
+ if(!$this->getCaseSensitive())
$regexp.='i';
return $regexp;
@@ -613,6 +657,30 @@ class TUrlMappingPattern extends TComponent preg_match($pattern,$request->getPathInfo(),$matches);
else
preg_match($this->getParameterizedPattern(),trim($request->getPathInfo(),'/').'/',$matches);
+ + if($this->getIsWildCardPattern() && isset($matches[$this->_serviceID])) + $matches[$this->_serviceID]=str_replace('*',$matches[$this->_serviceID],$this->_serviceParameter); + + if (isset($matches['urlparams'])) + { + $params=explode('/',$matches['urlparams']); + if ($this->_separator==='/') + { + while($key=array_shift($params)) + $matches2[$key]=($value=array_shift($params)) ? $value : ''; + } + else + { + array_pop($params); + foreach($params as $param) + { + list($key,$value)=explode($this->_separator,$param,2); + $matches[$key]=$value; + } + } + unset($matches['urlparams']); + } + return $matches;
}
@@ -640,7 +708,51 @@ class TUrlMappingPattern extends TComponent * @since 3.1.4
*/
public function getIsWildCardPattern() {
- return $this->_isWildCardPattern;
+ return $this->_isWildCardPattern;
+ }
+
+ /**
+ * @return THttpRequestUrlFormat the format of URLs. Defaults to THttpRequestUrlFormat::Get.
+ */
+ public function getUrlFormat()
+ {
+ return $this->_urlFormat;
+ }
+
+ /**
+ * Sets the format of URLs constructed and interpreted by this pattern.
+ * A Get URL format is like index.php?name1=value1&name2=value2
+ * while a Path URL format is like index.php/name1/value1/name2/value.
+ * The separating character between name and value can be configured with
+ * {@link setUrlParamSeparator} and defaults to '/'.
+ * Changing the UrlFormat will affect {@link constructUrl} and how GET variables
+ * are parsed.
+ * @param THttpRequestUrlFormat the format of URLs.
+ * @param since 3.1.4
+ */
+ public function setUrlFormat($value)
+ {
+ $this->_urlFormat=TPropertyValue::ensureEnum($value,'THttpRequestUrlFormat');
+ }
+
+ /**
+ * @return string separator used to separate GET variable name and value when URL format is Path. Defaults to slash '/'.
+ */
+ public function getUrlParamSeparator()
+ {
+ return $this->_separator;
+ }
+
+ /**
+ * @param string separator used to separate GET variable name and value when URL format is Path.
+ * @throws TInvalidDataValueException if the separator is not a single character
+ */
+ public function setUrlParamSeparator($value)
+ {
+ if(strlen($value)===1)
+ $this->_separator=$value;
+ else
+ throw new TInvalidDataValueException('httprequest_separator_invalid');
}
/**
@@ -686,6 +798,12 @@ class TUrlMappingPattern extends TComponent // for the rest of the GET variables, put them in the query string
if(count($extra)>0)
{
+ if ($this->_urlFormat===THttpRequestUrlFormat::Path && $this->getIsWildCardPattern()) { + foreach ($extra as $name=>$value) + $url.='/'.$name.$this->_separator.($encodeGetItems?rawurlencode($value):$value); + return $url; + } + $url2='';
$amp=$encodeAmpersand?'&':'&';
if($encodeGetItems)
diff --git a/framework/Web/UI/ActiveControls/TActiveCheckBox.php b/framework/Web/UI/ActiveControls/TActiveCheckBox.php index 55244372..3e078876 100644 --- a/framework/Web/UI/ActiveControls/TActiveCheckBox.php +++ b/framework/Web/UI/ActiveControls/TActiveCheckBox.php @@ -121,6 +121,9 @@ class TActiveCheckBox extends TCheckBox implements ICallbackEventHandler, IActiv /** * Ensure that the ID attribute is rendered and registers the javascript code * for initializing the active control. + * + * Since 3.1.4, the javascript code is not rendered if {@link setAutoPostBack AutoPostBack} is false + * * @param THtmlWriter the writer for the rendering purpose * @param string checkbox id * @param string onclick js @@ -128,8 +131,9 @@ class TActiveCheckBox extends TCheckBox implements ICallbackEventHandler, IActiv protected function renderInputTag($writer,$clientID,$onclick) { parent::renderInputTag($writer,$clientID,$onclick); - $this->getActiveControl()->registerCallbackClientScript( - $this->getClientClassName(), $this->getPostBackOptions()); + if ($this->getAutoPostBack()) + $this->getActiveControl()->registerCallbackClientScript( + $this->getClientClassName(), $this->getPostBackOptions()); } /** diff --git a/framework/Web/UI/ActiveControls/TActiveControlAdapter.php b/framework/Web/UI/ActiveControls/TActiveControlAdapter.php index 34872f98..8c6ba430 100644 --- a/framework/Web/UI/ActiveControls/TActiveControlAdapter.php +++ b/framework/Web/UI/ActiveControls/TActiveControlAdapter.php @@ -64,7 +64,7 @@ class TActiveControlAdapter extends TControlAdapter */
protected function setBaseControlClass($type)
{
- if(is_null($type))
+ if($type===null)
{
if($this->getControl() instanceof ICallbackEventHandler)
$this->_activeControlType = 'TBaseActiveCallbackControl';
@@ -117,7 +117,7 @@ class TActiveControlAdapter extends TControlAdapter */
public function getBaseActiveControl()
{
- if(is_null($this->_baseActiveControl))
+ if($this->_baseActiveControl===null)
{
$type = $this->_activeControlType;
$this->_baseActiveControl = new $type($this->getControl());
@@ -161,7 +161,7 @@ class TActiveControlAdapter extends TControlAdapter */
public function saveState()
{
- if(!is_null($this->_stateTracker)
+ if(($this->_stateTracker!==null)
&& $this->getControl()->getActiveControl()->canUpdateClientSide())
{
$this->_stateTracker->respondToChanges();
@@ -353,7 +353,7 @@ class TCallbackPageStateTracker */
protected function updateStyle($style)
{
- if(!is_null($style['CssClass']))
+ if($style['CssClass']!==null)
$this->client()->setAttribute($this->_control, 'class', $style['CssClass']);
if(count($style['Style']) > 0)
$this->client()->setStyle($this->_control, $style['Style']);
@@ -487,9 +487,9 @@ class TStyleDiff extends TViewStateDiff */
protected function getCssClassDiff()
{
- if(is_null($this->_old))
+ if($this->_old===null)
{
- return !is_null($this->_new) && $this->_new->hasCssClass()
+ return ($this->_new!==null) && $this->_new->hasCssClass()
? $this->_new->getCssClass() : null;
}
else
@@ -515,13 +515,13 @@ class TStyleDiff extends TViewStateDiff */
public function getDifference()
{
- if(is_null($this->_new))
+ if($this->_new===null)
return $this->_null;
else
{
$css = $this->getCssClassDiff();
$style = $this->getStyleDiff();
- if(!is_null($css) || !is_null($style))
+ if(($css!==null) || ($style!==null))
return array('CssClass' => $css, 'Style' => $style);
else
$this->_null;
@@ -546,9 +546,9 @@ class TMapCollectionDiff extends TViewStateDiff */
public function getDifference()
{
- if(is_null($this->_old))
+ if($this->_old===null)
{
- return !is_null($this->_new) ? $this->_new->toArray() : $this->_null;
+ return ($this->_new!==null) ? $this->_new->toArray() : $this->_null;
}
else
{
diff --git a/framework/Web/UI/ActiveControls/TActiveCustomValidator.php b/framework/Web/UI/ActiveControls/TActiveCustomValidator.php index 6c74aa7d..1f719bf6 100644 --- a/framework/Web/UI/ActiveControls/TActiveCustomValidator.php +++ b/framework/Web/UI/ActiveControls/TActiveCustomValidator.php @@ -4,7 +4,7 @@ *
* @author Wei Zhuo <weizhuo[at]gamil[dot]com>
* @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2008 PradoSoft + * @copyright Copyright © 2005-2008 PradoSoft
* @license http://www.pradosoft.com/license/
* @version $Id$
* @package System.Web.UI.ActiveControls
@@ -236,6 +236,6 @@ class TActiveCustomValidatorClientSide extends TCallbackClientSide public function getObserveChanges()
{
$changes = $this->getOption('ObserveChanges');
- return is_null($changes) ? true : $changes;
+ return ($changes===null) ? true : $changes;
}
}
diff --git a/framework/Web/UI/ActiveControls/TActiveDatePicker.php b/framework/Web/UI/ActiveControls/TActiveDatePicker.php index 052ed199..fafd21e5 100755 --- a/framework/Web/UI/ActiveControls/TActiveDatePicker.php +++ b/framework/Web/UI/ActiveControls/TActiveDatePicker.php @@ -39,6 +39,7 @@ class TActiveDatePicker extends TDatePicker implements ICallbackEventHandler, I protected function getDatePickerOptions(){ $options = parent::getDatePickerOptions(); $options['EventTarget'] = $this->getUniqueID(); + $options['ShowCalendar'] = $this->getShowCalendar(); return $options; } @@ -108,22 +109,20 @@ class TActiveDatePicker extends TDatePicker implements ICallbackEventHandler, I */ protected function registerCalendarClientScript() { - if($this->getShowCalendar()) - { - $cs = $this->getPage()->getClientScript(); - $cs->registerPradoScript("activedatepicker"); - - if(!$cs->isEndScriptRegistered('TDatePicker.spacer')) - { - $spacer = $this->getAssetUrl('spacer.gif'); - $code = "Prado.WebUI.TDatePicker.spacer = '$spacer';"; - $cs->registerEndScript('TDatePicker.spacer', $code); - } + + $cs = $this->getPage()->getClientScript(); + $cs->registerPradoScript("activedatepicker"); - $options = TJavaScript::encode($this->getDatePickerOptions()); - $code = "new Prado.WebUI.TActiveDatePicker($options);"; - $cs->registerEndScript("prado:".$this->getClientID(), $code); + if(!$cs->isEndScriptRegistered('TDatePicker.spacer')) + { + $spacer = $this->getAssetUrl('spacer.gif'); + $code = "Prado.WebUI.TDatePicker.spacer = '$spacer';"; + $cs->registerEndScript('TDatePicker.spacer', $code); } + + $options = TJavaScript::encode($this->getDatePickerOptions()); + $code = "new Prado.WebUI.TActiveDatePicker($options);"; + $cs->registerEndScript("prado:".$this->getClientID(), $code); } } ?> diff --git a/framework/Web/UI/ActiveControls/TActiveDropDownList.php b/framework/Web/UI/ActiveControls/TActiveDropDownList.php index e66b4cbd..c9458494 100644 --- a/framework/Web/UI/ActiveControls/TActiveDropDownList.php +++ b/framework/Web/UI/ActiveControls/TActiveDropDownList.php @@ -105,8 +105,9 @@ class TActiveDropDownList extends TDropDownList implements ICallbackEventHandler { parent::addAttributesToRender($writer); $writer->addAttribute('id',$this->getClientID()); - $this->getActiveControl()->registerCallbackClientScript( - $this->getClientClassName(), $this->getPostBackOptions()); + if ($this->getAutoPostBack()) + $this->getActiveControl()->registerCallbackClientScript( + $this->getClientClassName(), $this->getPostBackOptions()); } /** diff --git a/framework/Web/UI/ActiveControls/TActiveFileUpload.php b/framework/Web/UI/ActiveControls/TActiveFileUpload.php index e1b85f0d..b935936b 100755 --- a/framework/Web/UI/ActiveControls/TActiveFileUpload.php +++ b/framework/Web/UI/ActiveControls/TActiveFileUpload.php @@ -133,7 +133,30 @@ EOS; * default "Application.runtime.*" */ public function setTempPath($value){ - $this->setViewState('TempNamespace',$value,'Application.runtime.*'); + $this->setViewState('TempPath',$value,'Application.runtime.*'); + } + + /** + * @return boolean a value indicating whether an automatic callback to the server will occur whenever the user modifies the text in the TTextBox control and then tabs out of the component. Defaults to true. + * Note: When set to false, you will need to trigger the callback yourself. + */ + public function getAutoPostBack(){ + return $this->getViewState('AutoPostBack', true); + } + + /** + * @param boolean a value indicating whether an automatic callback to the server will occur whenever the user modifies the text in the TTextBox control and then tabs out of the component. Defaults to true. + * Note: When set to false, you will need to trigger the callback yourself. + */ + public function setAutoPostBack($value){ + $this->setViewState('AutoPostBack',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return string A chuck of javascript that will need to be called if {{@link getAutoPostBack AutoPostBack} is set to false} + */ + public function getCallbackJavascript(){ + return "Prado.WebUI.TActiveFileUpload.fileChanged(\"{$this->getClientID()}\")"; } /** @@ -172,6 +195,7 @@ EOS; */ public function onPreRender($param){ parent::onPreRender($param); + $this->getPage()->getClientScript()->registerPradoScript('effects'); $this->getPage()->getClientScript()->registerPradoScript('activefileupload'); } @@ -266,6 +290,7 @@ EOS; $options['indicatorID'] = $this->_busy->getClientID(); $options['completeID'] = $this->_success->getClientID(); $options['errorID'] = $this->_error->getClientID(); + $options['autoPostBack'] = $this->getAutoPostBack(); return $options; } @@ -312,4 +337,4 @@ EOS; $this->ensureChildControls(); return $this->_busy; } -}
\ No newline at end of file +} diff --git a/framework/Web/UI/ActiveControls/TActiveListBox.php b/framework/Web/UI/ActiveControls/TActiveListBox.php index 3deb9a0f..29857ee5 100644 --- a/framework/Web/UI/ActiveControls/TActiveListBox.php +++ b/framework/Web/UI/ActiveControls/TActiveListBox.php @@ -149,8 +149,9 @@ class TActiveListBox extends TListBox implements IActiveControl, ICallbackEventH { parent::addAttributesToRender($writer); $writer->addAttribute('id',$this->getClientID()); - $this->getActiveControl()->registerCallbackClientScript( - $this->getClientClassName(), $this->getPostBackOptions()); + if ($this->getAutoPostBack()) + $this->getActiveControl()->registerCallbackClientScript( + $this->getClientClassName(), $this->getPostBackOptions()); } } diff --git a/framework/Web/UI/ActiveControls/TActivePageAdapter.php b/framework/Web/UI/ActiveControls/TActivePageAdapter.php index c52f0775..90eed970 100644 --- a/framework/Web/UI/ActiveControls/TActivePageAdapter.php +++ b/framework/Web/UI/ActiveControls/TActivePageAdapter.php @@ -1,369 +1,369 @@ -<?php -/** - * TActivePageAdapter, TCallbackErrorHandler and TInvalidCallbackException class file. - * - * @author Wei Zhuo <weizhuo[at]gamil[dot]com> - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2008 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.ActiveControls - */ - -/** - * Load callback response adapter class. - */ -Prado::using('System.Web.UI.ActiveControls.TCallbackResponseAdapter'); -Prado::using('System.Web.UI.ActiveControls.TCallbackClientScript'); -Prado::using('System.Web.UI.ActiveControls.TCallbackEventParameter'); - -/** - * TActivePageAdapter class. - * - * Callback request handler. - * - * @author Wei Zhuo <weizhuo[at]gamil[dot]com> - * @version $Id$ - * @package System.Web.UI.ActiveControls - * @since 3.1 - */ -class TActivePageAdapter extends TControlAdapter -{ - /** - * Callback response data header name. - */ - const CALLBACK_DATA_HEADER = 'X-PRADO-DATA'; - /** - * Callback response client-side action header name. - */ - const CALLBACK_ACTION_HEADER = 'X-PRADO-ACTIONS'; - /** - * Callback error header name. - */ - const CALLBACK_ERROR_HEADER = 'X-PRADO-ERROR'; - /** - * Callback page state header name. - */ - const CALLBACK_PAGESTATE_HEADER = 'X-PRADO-PAGESTATE'; - - /** - * Callback redirect url header name. - */ - const CALLBACK_REDIRECT = 'X-PRADO-REDIRECT'; - - /** - * @var ICallbackEventHandler callback event handler. - */ - private $_callbackEventTarget; - /** - * @var mixed callback event parameter. - */ - private $_callbackEventParameter; - /** - * @var TCallbackClientScript callback client script handler - */ - private $_callbackClient; - - private $_controlsToRender=array(); - - /** - * Constructor, trap errors and exception to let the callback response - * handle them. - */ - public function __construct(TPage $control) - { - parent::__construct($control); - - //TODO: can this be done later? - $response = $this->getApplication()->getResponse(); - $response->setAdapter(new TCallbackResponseAdapter($response)); - - $this->trapCallbackErrorsExceptions(); - } - - /** - * Process the callback request. - * @param THtmlWriter html content writer. - */ - public function processCallbackEvent($writer) - { - Prado::trace("ActivePage raiseCallbackEvent()",'System.Web.UI.ActiveControls.TActivePageAdapter'); - $this->raiseCallbackEvent(); - } - - /** - * Register a control for defered render() call. - * @param TControl control for defered rendering - * @param THtmlWriter the renderer - */ - public function registerControlToRender($control,$writer) - { - $id = $control->getUniqueID(); - if(!isset($this->_controlsToRender[$id])) - $this->_controlsToRender[$id] = array($control,$writer); - } - - /** - * Trap errors and exceptions to be handled by TCallbackErrorHandler. - */ - protected function trapCallbackErrorsExceptions() - { - $this->getApplication()->setErrorHandler(new TCallbackErrorHandler); - } - - /** - * Render the callback response. - * @param THtmlWriter html content writer. - */ - public function renderCallbackResponse($writer) - { - Prado::trace("ActivePage renderCallbackResponse()",'System.Web.UI.ActiveControls.TActivePageAdapter'); - if(($url = $this->getResponse()->getAdapter()->getRedirectedUrl())===null) - $this->renderResponse($writer); - else - $this->redirect($url); - } - - /** - * Redirect url on the client-side using javascript. - * @param string new url to load. - */ - protected function redirect($url) - { - Prado::trace("ActivePage redirect()",'System.Web.UI.ActiveControls.TActivePageAdapter'); - $this->appendContentPart($this->getResponse(), self::CALLBACK_REDIRECT, $url); - //$this->getResponse()->appendHeader(self::CALLBACK_REDIRECT.': '.$url); - } - - /** - * Renders the callback response by adding additional callback data and - * javascript actions in the header and page state if required. - * @param THtmlWriter html content writer. - */ - protected function renderResponse($writer) - { - Prado::trace("ActivePage renderResponse()",'System.Web.UI.ActiveControls.TActivePageAdapter'); - //renders all the defered render() calls. - foreach($this->_controlsToRender as $rid => $forRender) - $forRender[0]->render($forRender[1]); - - $response = $this->getResponse(); - - //send response data in header - if($response->getHasAdapter()) - { - $responseData = $response->getAdapter()->getResponseData(); - if(!is_null($responseData)) - { - $data = TJavaScript::jsonEncode($responseData); - - $this->appendContentPart($response, self::CALLBACK_DATA_HEADER, $data); - //$response->appendHeader(self::CALLBACK_DATA_HEADER.': '.$data); - } - } - - //sends page state in header - if(($handler = $this->getCallbackEventTarget()) !== null) - { - if($handler->getActiveControl()->getClientSide()->getEnablePageStateUpdate()) - { - $pagestate = $this->getPage()->getClientState(); - $this->appendContentPart($response, self::CALLBACK_PAGESTATE_HEADER, $pagestate); - //$response->appendHeader(self::CALLBACK_PAGESTATE_HEADER.': '.$pagestate); - } - } - - //safari must receive at least 1 byte of data. - $writer->write(" "); - - //output the end javascript - if($this->getPage()->getClientScript()->hasEndScripts()) - { - $writer = $response->createHtmlWriter(); - $this->getPage()->getClientScript()->renderEndScripts($writer); - $this->getPage()->getCallbackClient()->evaluateScript($writer); - } - - //output the actions - $executeJavascript = $this->getCallbackClientHandler()->getClientFunctionsToExecute(); - $actions = TJavaScript::jsonEncode($executeJavascript); - $this->appendContentPart($response, self::CALLBACK_ACTION_HEADER, $actions); - //$response->appendHeader(self::CALLBACK_ACTION_HEADER.': '.$actions); - } - - /** - * Appends data or javascript code to the body content surrounded with delimiters - */ - private function appendContentPart($response, $delimiter, $data) - { - $content = $response->createHtmlWriter(); - $content->getWriter()->setBoundary($delimiter); - $content->write($data); - } - - /** - * Trys to find the callback event handler and raise its callback event. - * @throws TInvalidCallbackException if call back target is not found. - * @throws TInvalidCallbackException if the requested target does not - * implement ICallbackEventHandler. - */ - private function raiseCallbackEvent() - { - if(($callbackHandler=$this->getCallbackEventTarget())!==null) - { - if($callbackHandler instanceof ICallbackEventHandler) - { - $param = $this->getCallbackEventParameter(); - $result = new TCallbackEventParameter($this->getResponse(), $param); - $callbackHandler->raiseCallbackEvent($result); - } - else - { - throw new TInvalidCallbackException( - 'callback_invalid_handler', $callbackHandler->getUniqueID()); - } - } - else - { - $target = $this->getRequest()->itemAt(TPage::FIELD_CALLBACK_TARGET); - throw new TInvalidCallbackException('callback_invalid_target', $target); - } - } - - /** - * @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; - } - - /** - * Gets callback parameter. JSON encoding is assumed. - * @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); - } - return $this->_callbackEventParameter; - } - - /** - * @param mixed postback event parameter - */ - public function setCallbackEventParameter($value) - { - $this->_callbackEventParameter=$value; - } - - /** - * Gets the callback client script handler. It handlers the 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; - } -} - -/** - * TCallbackErrorHandler class. - * - * Captures errors and exceptions and send them back during callback response. - * When the application is in debug mode, the error and exception stack trace - * are shown. A TJavascriptLogger must be present on the client-side to view - * the error stack trace. - * - * @author Wei Zhuo <weizhuo[at]gmail[dot]com> - * @version $Id$ - * @package System.Web.UI.ActiveControls - * @since 3.1 - */ -class TCallbackErrorHandler extends TErrorHandler -{ - /** - * Displays the exceptions to the client-side TJavascriptLogger. - * A HTTP 500 status code is sent and the stack trace is sent as JSON encoded. - * @param Exception exception details. - */ - protected function displayException($exception) - { - if($this->getApplication()->getMode()===TApplication::STATE_DEBUG) - { - $response = $this->getApplication()->getResponse(); - $trace = TJavaScript::jsonEncode($this->getExceptionStackTrace($exception)); - $response->appendHeader('HTTP/1.0 500 Internal Error'); - $response->appendHeader(TActivePageAdapter::CALLBACK_ERROR_HEADER.': '.$trace); - } - else - { - error_log("Error happened while processing an existing error:\n".$exception->__toString()); - header('HTTP/1.0 500 Internal Error'); - } - $this->getApplication()->getResponse()->flush(); - } - - /** - * @param Exception exception details. - * @return array exception stack trace details. - */ - private function getExceptionStackTrace($exception) - { - $data['code']=$exception->getCode() > 0 ? $exception->getCode() : 500; - $data['file']=$exception->getFile(); - $data['line']=$exception->getLine(); - $data['trace']=$exception->getTrace(); - if($exception instanceof TPhpErrorException) - { - // if PHP exception, we want to show the 2nd stack level context - // because the 1st stack level is of little use (it's in error handler) - if(isset($trace[0]) && isset($trace[0]['file']) && isset($trace[0]['line'])) - { - $data['file']=$trace[0]['file']; - $data['line']=$trace[0]['line']; - } - } - $data['type']=get_class($exception); - $data['message']=$exception->getMessage(); - $data['version']=$_SERVER['SERVER_SOFTWARE'].' '.Prado::getVersion(); - $data['time']=@strftime('%Y-%m-%d %H:%M',time()); - return $data; - } -} - -/** - * TInvalidCallbackException class. - * - * @author Wei Zhuo <weizhuo[at]gmail[dot]com> - * @version $Id$ - * @package System.Web.UI.ActiveControls - * @since 3.1 - */ -class TInvalidCallbackException extends TException -{ -} - +<?php
+/**
+ * TActivePageAdapter, TCallbackErrorHandler and TInvalidCallbackException class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gamil[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005-2008 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Web.UI.ActiveControls
+ */
+
+/**
+ * Load callback response adapter class.
+ */
+Prado::using('System.Web.UI.ActiveControls.TCallbackResponseAdapter');
+Prado::using('System.Web.UI.ActiveControls.TCallbackClientScript');
+Prado::using('System.Web.UI.ActiveControls.TCallbackEventParameter');
+
+/**
+ * TActivePageAdapter class.
+ *
+ * Callback request handler.
+ *
+ * @author Wei Zhuo <weizhuo[at]gamil[dot]com>
+ * @version $Id$
+ * @package System.Web.UI.ActiveControls
+ * @since 3.1
+ */
+class TActivePageAdapter extends TControlAdapter
+{
+ /**
+ * Callback response data header name.
+ */
+ const CALLBACK_DATA_HEADER = 'X-PRADO-DATA';
+ /**
+ * Callback response client-side action header name.
+ */
+ const CALLBACK_ACTION_HEADER = 'X-PRADO-ACTIONS';
+ /**
+ * Callback error header name.
+ */
+ const CALLBACK_ERROR_HEADER = 'X-PRADO-ERROR';
+ /**
+ * Callback page state header name.
+ */
+ const CALLBACK_PAGESTATE_HEADER = 'X-PRADO-PAGESTATE';
+
+ /**
+ * Callback redirect url header name.
+ */
+ const CALLBACK_REDIRECT = 'X-PRADO-REDIRECT';
+
+ /**
+ * @var ICallbackEventHandler callback event handler.
+ */
+ private $_callbackEventTarget;
+ /**
+ * @var mixed callback event parameter.
+ */
+ private $_callbackEventParameter;
+ /**
+ * @var TCallbackClientScript callback client script handler
+ */
+ private $_callbackClient;
+
+ private $_controlsToRender=array();
+
+ /**
+ * Constructor, trap errors and exception to let the callback response
+ * handle them.
+ */
+ public function __construct(TPage $control)
+ {
+ parent::__construct($control);
+
+ //TODO: can this be done later?
+ $response = $this->getApplication()->getResponse();
+ $response->setAdapter(new TCallbackResponseAdapter($response));
+
+ $this->trapCallbackErrorsExceptions();
+ }
+
+ /**
+ * Process the callback request.
+ * @param THtmlWriter html content writer.
+ */
+ public function processCallbackEvent($writer)
+ {
+ Prado::trace("ActivePage raiseCallbackEvent()",'System.Web.UI.ActiveControls.TActivePageAdapter');
+ $this->raiseCallbackEvent();
+ }
+
+ /**
+ * Register a control for defered render() call.
+ * @param TControl control for defered rendering
+ * @param THtmlWriter the renderer
+ */
+ public function registerControlToRender($control,$writer)
+ {
+ $id = $control->getUniqueID();
+ if(!isset($this->_controlsToRender[$id]))
+ $this->_controlsToRender[$id] = array($control,$writer);
+ }
+
+ /**
+ * Trap errors and exceptions to be handled by TCallbackErrorHandler.
+ */
+ protected function trapCallbackErrorsExceptions()
+ {
+ $this->getApplication()->setErrorHandler(new TCallbackErrorHandler);
+ }
+
+ /**
+ * Render the callback response.
+ * @param THtmlWriter html content writer.
+ */
+ public function renderCallbackResponse($writer)
+ {
+ Prado::trace("ActivePage renderCallbackResponse()",'System.Web.UI.ActiveControls.TActivePageAdapter');
+ if(($url = $this->getResponse()->getAdapter()->getRedirectedUrl())===null)
+ $this->renderResponse($writer);
+ else
+ $this->redirect($url);
+ }
+
+ /**
+ * Redirect url on the client-side using javascript.
+ * @param string new url to load.
+ */
+ protected function redirect($url)
+ {
+ Prado::trace("ActivePage redirect()",'System.Web.UI.ActiveControls.TActivePageAdapter');
+ $this->appendContentPart($this->getResponse(), self::CALLBACK_REDIRECT, $url);
+ //$this->getResponse()->appendHeader(self::CALLBACK_REDIRECT.': '.$url);
+ }
+
+ /**
+ * Renders the callback response by adding additional callback data and
+ * javascript actions in the header and page state if required.
+ * @param THtmlWriter html content writer.
+ */
+ protected function renderResponse($writer)
+ {
+ Prado::trace("ActivePage renderResponse()",'System.Web.UI.ActiveControls.TActivePageAdapter');
+ //renders all the defered render() calls.
+ foreach($this->_controlsToRender as $rid => $forRender)
+ $forRender[0]->render($forRender[1]);
+
+ $response = $this->getResponse();
+
+ //send response data in header
+ if($response->getHasAdapter())
+ {
+ $responseData = $response->getAdapter()->getResponseData();
+ if($responseData!==null)
+ {
+ $data = TJavaScript::jsonEncode($responseData);
+
+ $this->appendContentPart($response, self::CALLBACK_DATA_HEADER, $data);
+ //$response->appendHeader(self::CALLBACK_DATA_HEADER.': '.$data);
+ }
+ }
+
+ //sends page state in header
+ if(($handler = $this->getCallbackEventTarget()) !== null)
+ {
+ if($handler->getActiveControl()->getClientSide()->getEnablePageStateUpdate())
+ {
+ $pagestate = $this->getPage()->getClientState();
+ $this->appendContentPart($response, self::CALLBACK_PAGESTATE_HEADER, $pagestate);
+ //$response->appendHeader(self::CALLBACK_PAGESTATE_HEADER.': '.$pagestate);
+ }
+ }
+
+ //safari must receive at least 1 byte of data.
+ $writer->write(" ");
+
+ //output the end javascript
+ if($this->getPage()->getClientScript()->hasEndScripts())
+ {
+ $writer = $response->createHtmlWriter();
+ $this->getPage()->getClientScript()->renderEndScripts($writer);
+ $this->getPage()->getCallbackClient()->evaluateScript($writer);
+ }
+
+ //output the actions
+ $executeJavascript = $this->getCallbackClientHandler()->getClientFunctionsToExecute();
+ $actions = TJavaScript::jsonEncode($executeJavascript);
+ $this->appendContentPart($response, self::CALLBACK_ACTION_HEADER, $actions);
+ //$response->appendHeader(self::CALLBACK_ACTION_HEADER.': '.$actions);
+ }
+
+ /**
+ * Appends data or javascript code to the body content surrounded with delimiters
+ */
+ private function appendContentPart($response, $delimiter, $data)
+ {
+ $content = $response->createHtmlWriter();
+ $content->getWriter()->setBoundary($delimiter);
+ $content->write($data);
+ }
+
+ /**
+ * Trys to find the callback event handler and raise its callback event.
+ * @throws TInvalidCallbackException if call back target is not found.
+ * @throws TInvalidCallbackException if the requested target does not
+ * implement ICallbackEventHandler.
+ */
+ private function raiseCallbackEvent()
+ {
+ if(($callbackHandler=$this->getCallbackEventTarget())!==null)
+ {
+ if($callbackHandler instanceof ICallbackEventHandler)
+ {
+ $param = $this->getCallbackEventParameter();
+ $result = new TCallbackEventParameter($this->getResponse(), $param);
+ $callbackHandler->raiseCallbackEvent($result);
+ }
+ else
+ {
+ throw new TInvalidCallbackException(
+ 'callback_invalid_handler', $callbackHandler->getUniqueID());
+ }
+ }
+ else
+ {
+ $target = $this->getRequest()->itemAt(TPage::FIELD_CALLBACK_TARGET);
+ throw new TInvalidCallbackException('callback_invalid_target', $target);
+ }
+ }
+
+ /**
+ * @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;
+ }
+
+ /**
+ * Gets callback parameter. JSON encoding is assumed.
+ * @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);
+ }
+ return $this->_callbackEventParameter;
+ }
+
+ /**
+ * @param mixed postback event parameter
+ */
+ public function setCallbackEventParameter($value)
+ {
+ $this->_callbackEventParameter=$value;
+ }
+
+ /**
+ * Gets the callback client script handler. It handlers the javascript functions
+ * to be executed during the callback response.
+ * @return TCallbackClientScript callback client handler.
+ */
+ public function getCallbackClientHandler()
+ {
+ if($this->_callbackClient===null)
+ $this->_callbackClient = new TCallbackClientScript;
+ return $this->_callbackClient;
+ }
+}
+
+/**
+ * TCallbackErrorHandler class.
+ *
+ * Captures errors and exceptions and send them back during callback response.
+ * When the application is in debug mode, the error and exception stack trace
+ * are shown. A TJavascriptLogger must be present on the client-side to view
+ * the error stack trace.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Web.UI.ActiveControls
+ * @since 3.1
+ */
+class TCallbackErrorHandler extends TErrorHandler
+{
+ /**
+ * Displays the exceptions to the client-side TJavascriptLogger.
+ * A HTTP 500 status code is sent and the stack trace is sent as JSON encoded.
+ * @param Exception exception details.
+ */
+ protected function displayException($exception)
+ {
+ if($this->getApplication()->getMode()===TApplication::STATE_DEBUG)
+ {
+ $response = $this->getApplication()->getResponse();
+ $trace = TJavaScript::jsonEncode($this->getExceptionStackTrace($exception));
+ $response->appendHeader('HTTP/1.0 500 Internal Error');
+ $response->appendHeader(TActivePageAdapter::CALLBACK_ERROR_HEADER.': '.$trace);
+ }
+ else
+ {
+ error_log("Error happened while processing an existing error:\n".$exception->__toString());
+ header('HTTP/1.0 500 Internal Error');
+ }
+ $this->getApplication()->getResponse()->flush();
+ }
+
+ /**
+ * @param Exception exception details.
+ * @return array exception stack trace details.
+ */
+ private function getExceptionStackTrace($exception)
+ {
+ $data['code']=$exception->getCode() > 0 ? $exception->getCode() : 500;
+ $data['file']=$exception->getFile();
+ $data['line']=$exception->getLine();
+ $data['trace']=$exception->getTrace();
+ if($exception instanceof TPhpErrorException)
+ {
+ // if PHP exception, we want to show the 2nd stack level context
+ // because the 1st stack level is of little use (it's in error handler)
+ if(isset($trace[0]) && isset($trace[0]['file']) && isset($trace[0]['line']))
+ {
+ $data['file']=$trace[0]['file'];
+ $data['line']=$trace[0]['line'];
+ }
+ }
+ $data['type']=get_class($exception);
+ $data['message']=$exception->getMessage();
+ $data['version']=$_SERVER['SERVER_SOFTWARE'].' '.Prado::getVersion();
+ $data['time']=@strftime('%Y-%m-%d %H:%M',time());
+ return $data;
+ }
+}
+
+/**
+ * TInvalidCallbackException class.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Web.UI.ActiveControls
+ * @since 3.1
+ */
+class TInvalidCallbackException extends TException
+{
+}
+
diff --git a/framework/Web/UI/ActiveControls/TActivePanel.php b/framework/Web/UI/ActiveControls/TActivePanel.php index 51e9fb08..25a42eb7 100644 --- a/framework/Web/UI/ActiveControls/TActivePanel.php +++ b/framework/Web/UI/ActiveControls/TActivePanel.php @@ -4,7 +4,7 @@ *
* @author Wei Zhuo <weizhuo[at]gamil[dot]com>
* @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2008 PradoSoft + * @copyright Copyright © 2005-2008 PradoSoft
* @license http://www.pradosoft.com/license/
* @version $Id$
* @package System.Web.UI.ActiveControls
@@ -58,6 +58,15 @@ class TActivePanel extends TPanel implements IActiveControl }
/**
+ * Adds attribute id to the renderer.
+ * @param THtmlWriter the writer used for the rendering purpose
+ */
+ protected function addAttributesToRender($writer) {
+ $writer->addAttribute('id',$this->getClientID());
+ parent::addAttributesToRender($writer);
+ }
+
+ /**
* Renders and replaces the panel's content on the client-side.
* When render() is called before the OnPreRender event, such as when render()
* is called during a callback event handler, the rendering
@@ -74,16 +83,16 @@ class TActivePanel extends TPanel implements IActiveControl }
else
{
- $this->getPage()->getAdapter()->registerControlToRender($this,$writer); - if ($this->getHasControls()) - { - // If we update a TActivePanel on callback, - // We shouldn't update all childs, because the whole content will be replaced by - // the parent - foreach ($this->findControlsByType('IActiveControl', false) as $control) - { - $control->getActiveControl()->setEnableUpdate(false); - } + $this->getPage()->getAdapter()->registerControlToRender($this,$writer);
+ if ($this->getHasControls())
+ {
+ // If we update a TActivePanel on callback,
+ // We shouldn't update all childs, because the whole content will be replaced by
+ // the parent
+ foreach ($this->findControlsByType('IActiveControl', false) as $control)
+ {
+ $control->getActiveControl()->setEnableUpdate(false);
+ }
}
}
}
diff --git a/framework/Web/UI/ActiveControls/TActiveRadioButton.php b/framework/Web/UI/ActiveControls/TActiveRadioButton.php index f7e9a286..8e2195d3 100644 --- a/framework/Web/UI/ActiveControls/TActiveRadioButton.php +++ b/framework/Web/UI/ActiveControls/TActiveRadioButton.php @@ -120,12 +120,15 @@ class TActiveRadioButton extends TRadioButton implements IActiveControl, ICallba /** * Ensure that the ID attribute is rendered and registers the javascript code * for initializing the active control. + * Since 3.1.4, the javascript code is not rendered if {@link setAutoPostBack AutoPostBack} is false + * */ protected function renderInputTag($writer,$clientID,$onclick) { parent::renderInputTag($writer,$clientID,$onclick); - $this->getActiveControl()->registerCallbackClientScript( - $this->getClientClassName(), $this->getPostBackOptions()); + if ($this->getAutoPostBack()) + $this->getActiveControl()->registerCallbackClientScript( + $this->getClientClassName(), $this->getPostBackOptions()); } /** diff --git a/framework/Web/UI/ActiveControls/TAutoComplete.php b/framework/Web/UI/ActiveControls/TAutoComplete.php index 5a3633ec..13200e51 100644 --- a/framework/Web/UI/ActiveControls/TAutoComplete.php +++ b/framework/Web/UI/ActiveControls/TAutoComplete.php @@ -235,7 +235,7 @@ class TAutoComplete extends TActiveTextBox implements INamingContainer */
public function getResultPanel()
{
- if(is_null($this->_resultPanel))
+ if($this->_resultPanel===null)
$this->_resultPanel = $this->createResultPanel();
return $this->_resultPanel;
}
@@ -256,7 +256,7 @@ class TAutoComplete extends TActiveTextBox implements INamingContainer */
public function getSuggestions()
{
- if(is_null($this->_repeater))
+ if($this->_repeater===null)
$this->_repeater = $this->createRepeater();
return $this->_repeater;
}
diff --git a/framework/Web/UI/ActiveControls/TBaseActiveControl.php b/framework/Web/UI/ActiveControls/TBaseActiveControl.php index 8f55e27b..f301f2c3 100644 --- a/framework/Web/UI/ActiveControls/TBaseActiveControl.php +++ b/framework/Web/UI/ActiveControls/TBaseActiveControl.php @@ -58,8 +58,8 @@ class TBaseActiveControl extends TComponent */
protected function setOption($name,$value,$default=null)
{
- $value = is_null($value) ? $default : $value;
- if(!is_null($value))
+ $value = ($value===null) ? $default : $value;
+ if($value!==null)
$this->_options->add($name,$value);
}
@@ -73,7 +73,7 @@ class TBaseActiveControl extends TComponent protected function getOption($name,$default=null)
{
$item = $this->_options->itemAt($name);
- return is_null($item) ? $default : $item;
+ return ($item===null) ? $default : $item;
}
/**
@@ -168,7 +168,7 @@ class TBaseActiveCallbackControl extends TBaseActiveControl */
public function getClientSide()
{
- if(is_null($client = $this->getOption('ClientSide')))
+ if(($client = $this->getOption('ClientSide'))===null)
{
$client = $this->createClientSide();
$this->setOption('ClientSide', $client);
@@ -182,7 +182,7 @@ class TBaseActiveCallbackControl extends TBaseActiveControl */
public function setClientSide($client)
{
- if(is_null($this->getOption('ClientSide')))
+ if( $this->getOption('ClientSide')===null)
$this->setOption('ClientSide', $client);
else
throw new TConfigurationException(
diff --git a/framework/Web/UI/ActiveControls/TCallbackClientScript.php b/framework/Web/UI/ActiveControls/TCallbackClientScript.php index 8f275ef8..062f63dc 100644 --- a/framework/Web/UI/ActiveControls/TCallbackClientScript.php +++ b/framework/Web/UI/ActiveControls/TCallbackClientScript.php @@ -4,7 +4,7 @@ *
* @author Wei Zhuo <weizhuo[at]gamil[dot]com>
* @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2008 PradoSoft + * @copyright Copyright © 2005-2008 PradoSoft
* @license http://www.pradosoft.com/license/
* @version $Id$
* @package System.Web.UI.ActiveControls
@@ -104,7 +104,7 @@ class TCallbackClientScript extends TApplicationComponent {
$method = TPropertyValue::ensureEnum($method,
'Value', 'Index', 'Clear', 'Indices', 'Values', 'All', 'Invert');
- $type = is_null($type) ? $this->getSelectionControlType($control) : $type;
+ $type = ($type===null) ? $this->getSelectionControlType($control) : $type;
$total = $this->getSelectionControlIsListType($control) ? $control->getItemCount() : 1;
$this->callClientFunction('Prado.Element.select',
array($control, $type.$method, $value, $total));
@@ -164,8 +164,8 @@ class TCallbackClientScript extends TApplicationComponent */
public function setAttribute($control, $name, $value)
{
- if ($control instanceof ISurroundable) - $control=$control->getSurroundingTagID(); + if ($control instanceof ISurroundable)
+ $control=$control->getSurroundingTagID();
$this->callClientFunction('Prado.Element.setAttribute',array($control, $name, $value));
}
@@ -177,6 +177,18 @@ class TCallbackClientScript extends TApplicationComponent public function setListItems($control, $items)
{
$options = array();
+ if($control instanceof TListControl)
+ {
+ $promptText = $control->getPromptText();
+ $promptValue = $control->getPromptValue();
+
+ if($promptValue==='')
+ $promptValue = $promptText;
+
+ if($promptValue!=='')
+ $options[] = array($promptText, $promptValue);
+ }
+
foreach($items as $item)
{
if($item->getHasAttributes())
@@ -193,8 +205,8 @@ class TCallbackClientScript extends TApplicationComponent */
public function show($element)
{
- if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); + if ($element instanceof ISurroundable)
+ $element=$element->getSurroundingTagID();
$this->callClientFunction('Element.show', $element);
}
@@ -204,8 +216,8 @@ class TCallbackClientScript extends TApplicationComponent */
public function hide($element)
{
- if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); + if ($element instanceof ISurroundable)
+ $element=$element->getSurroundingTagID();
$this->callClientFunction('Element.hide', $element);
}
@@ -217,8 +229,8 @@ class TCallbackClientScript extends TApplicationComponent */
public function toggle($element, $effect=null, $options=array())
{
- if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); + if ($element instanceof ISurroundable)
+ $element=$element->getSurroundingTagID();
$this->callClientFunction('Element.toggle', array($element,$effect,$options));
}
@@ -228,8 +240,8 @@ class TCallbackClientScript extends TApplicationComponent */
public function remove($element)
{
- if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); + if ($element instanceof ISurroundable)
+ $element=$element->getSurroundingTagID();
$this->callClientFunction('Element.remove', $element);
}
@@ -246,8 +258,8 @@ class TCallbackClientScript extends TApplicationComponent */
public function update($element, $content)
{
- if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); + if ($element instanceof ISurroundable)
+ $element=$element->getSurroundingTagID();
$this->replace($element, $content, 'Element.update');
}
@@ -258,8 +270,8 @@ class TCallbackClientScript extends TApplicationComponent */
public function addCssClass($element, $cssClass)
{
- if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); + if ($element instanceof ISurroundable)
+ $element=$element->getSurroundingTagID();
$this->callClientFunction('Element.addClassName', array($element, $cssClass));
}
@@ -270,8 +282,8 @@ class TCallbackClientScript extends TApplicationComponent */
public function removeCssClass($element, $cssClass)
{
- if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); + if ($element instanceof ISurroundable)
+ $element=$element->getSurroundingTagID();
$this->callClientFunction('Element.removeClassName', array($element, $cssClass));
}
@@ -292,8 +304,8 @@ class TCallbackClientScript extends TApplicationComponent */
public function scrollTo($element)
{
- if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); + if ($element instanceof ISurroundable)
+ $element=$element->getSurroundingTagID();
$this->callClientFunction('Element.scrollTo', $element);
}
@@ -313,9 +325,9 @@ class TCallbackClientScript extends TApplicationComponent * @param array list of key-value pairs as style property and style value.
*/
public function setStyle($element, $styles)
- { - if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); + {
+ if ($element instanceof ISurroundable)
+ $element=$element->getSurroundingTagID();
$this->callClientFunction('Prado.Element.setStyle', array($element, $styles));
}
@@ -326,8 +338,8 @@ class TCallbackClientScript extends TApplicationComponent */
public function appendContent($element, $content)
{
- if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); + if ($element instanceof ISurroundable)
+ $element=$element->getSurroundingTagID();
$this->replace($element, $content, 'Prado.Element.Insert.append');
}
@@ -338,8 +350,8 @@ class TCallbackClientScript extends TApplicationComponent */
public function prependContent($element, $content)
{
- if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); + if ($element instanceof ISurroundable)
+ $element=$element->getSurroundingTagID();
$this->replace($element, $content, 'Prado.Element.Insert.prepend');
}
@@ -350,8 +362,8 @@ class TCallbackClientScript extends TApplicationComponent */
public function insertContentAfter($element, $content)
{
- if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); + if ($element instanceof ISurroundable)
+ $element=$element->getSurroundingTagID();
$this->replace($element, $content, 'Prado.Element.Insert.after');
}
@@ -362,8 +374,8 @@ class TCallbackClientScript extends TApplicationComponent */
public function insertContentBefore($element, $content)
{
- if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); + if ($element instanceof ISurroundable)
+ $element=$element->getSurroundingTagID();
$this->replace($element, $content, 'Prado.Element.Insert.before');
}
@@ -406,8 +418,8 @@ class TCallbackClientScript extends TApplicationComponent */
public function replaceContent($element,$content)
{
- if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); + if ($element instanceof ISurroundable)
+ $element=$element->getSurroundingTagID();
$this->replace($element, $content);
}
@@ -458,8 +470,8 @@ class TCallbackClientScript extends TApplicationComponent */
public function visualEffect($type, $element, $options=null)
{
- if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); + if ($element instanceof ISurroundable)
+ $element=$element->getSurroundingTagID();
$this->callClientFunction($type, array($element, $options));
}
@@ -470,8 +482,8 @@ class TCallbackClientScript extends TApplicationComponent */
public function appear($element, $options=null)
{
- if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); + if ($element instanceof ISurroundable)
+ $element=$element->getSurroundingTagID();
$this->visualEffect('Effect.Appear', $element, $options);
}
@@ -482,8 +494,8 @@ class TCallbackClientScript extends TApplicationComponent */
public function blindDown($element, $options=null)
{
- if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); + if ($element instanceof ISurroundable)
+ $element=$element->getSurroundingTagID();
$this->visualEffect('Effect.BlindDown', $element, $options);
}
@@ -494,8 +506,8 @@ class TCallbackClientScript extends TApplicationComponent */
public function blindUp($element, $options=null)
{
- if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); + if ($element instanceof ISurroundable)
+ $element=$element->getSurroundingTagID();
$this->visualEffect('Effect.BlindUp', $element, $options);
}
@@ -507,8 +519,8 @@ class TCallbackClientScript extends TApplicationComponent */
public function dropOut($element, $options=null)
{
- if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); + if ($element instanceof ISurroundable)
+ $element=$element->getSurroundingTagID();
$this->visualEffect('Effect.DropOut', $element, $options);
}
@@ -519,8 +531,8 @@ class TCallbackClientScript extends TApplicationComponent */
public function fade($element, $options=null)
{
- if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); + if ($element instanceof ISurroundable)
+ $element=$element->getSurroundingTagID();
$this->visualEffect('Effect.Fade', $element, $options);
}
@@ -531,8 +543,8 @@ class TCallbackClientScript extends TApplicationComponent */
public function fold($element, $options = null)
{
- if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); + if ($element instanceof ISurroundable)
+ $element=$element->getSurroundingTagID();
$this->visualEffect('Effect.Fold', $element, $options);
}
@@ -543,8 +555,8 @@ class TCallbackClientScript extends TApplicationComponent */
public function grow($element, $options=null)
{
- if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); + if ($element instanceof ISurroundable)
+ $element=$element->getSurroundingTagID();
$this->visualEffect('Effect.Grow', $element, $options);
}
@@ -555,8 +567,8 @@ class TCallbackClientScript extends TApplicationComponent */
public function puff($element, $options=null)
{
- if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); + if ($element instanceof ISurroundable)
+ $element=$element->getSurroundingTagID();
$this->visualEffect('Effect.Puff', $element, $options);
}
@@ -567,8 +579,8 @@ class TCallbackClientScript extends TApplicationComponent */
public function pulsate($element, $options=null)
{
- if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); + if ($element instanceof ISurroundable)
+ $element=$element->getSurroundingTagID();
$this->visualEffect('Effect.Pulsate', $element, $options);
}
@@ -579,8 +591,8 @@ class TCallbackClientScript extends TApplicationComponent */
public function shake($element, $options=null)
{
- if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); + if ($element instanceof ISurroundable)
+ $element=$element->getSurroundingTagID();
$this->visualEffect('Effect.Shake', $element, $options);
}
@@ -591,8 +603,8 @@ class TCallbackClientScript extends TApplicationComponent */
public function shrink($element, $options=null)
{
- if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); + if ($element instanceof ISurroundable)
+ $element=$element->getSurroundingTagID();
$this->visualEffect('Effect.Shrink', $element, $options);
}
@@ -603,8 +615,8 @@ class TCallbackClientScript extends TApplicationComponent */
public function slideDown($element, $options=null)
{
- if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); + if ($element instanceof ISurroundable)
+ $element=$element->getSurroundingTagID();
$this->visualEffect('Effect.SlideDown', $element, $options);
}
@@ -615,8 +627,8 @@ class TCallbackClientScript extends TApplicationComponent */
public function slideUp($element, $options=null)
{
- if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); + if ($element instanceof ISurroundable)
+ $element=$element->getSurroundingTagID();
$this->visualEffect('Effect.SlideUp', $element, $options);
}
@@ -627,8 +639,8 @@ class TCallbackClientScript extends TApplicationComponent */
public function squish($element, $options=null)
{
- if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); + if ($element instanceof ISurroundable)
+ $element=$element->getSurroundingTagID();
$this->visualEffect('Effect.Squish', $element, $options);
}
@@ -639,8 +651,8 @@ class TCallbackClientScript extends TApplicationComponent */
public function switchOff($element, $options=null)
{
- if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); + if ($element instanceof ISurroundable)
+ $element=$element->getSurroundingTagID();
$this->visualEffect('Effect.SwitchOff', $element, $options);
}
@@ -651,8 +663,8 @@ class TCallbackClientScript extends TApplicationComponent */
public function highlight($element, $options=null)
{
- if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); + if ($element instanceof ISurroundable)
+ $element=$element->getSurroundingTagID();
$this->visualEffect('Prado.Effect.Highlight', $element, $options);
}
@@ -663,8 +675,8 @@ class TCallbackClientScript extends TApplicationComponent */
public function setOpacity($element, $value)
{
- if ($element instanceof ISurroundable) - $element=$element->getSurroundingTagID(); + if ($element instanceof ISurroundable)
+ $element=$element->getSurroundingTagID();
$value = TPropertyValue::ensureFloat($value);
$this->callClientFunction('Element.setOpacity', array($element,$value));
}
diff --git a/framework/Web/UI/ActiveControls/TCallbackClientSide.php b/framework/Web/UI/ActiveControls/TCallbackClientSide.php index 57436278..31b806c3 100644 --- a/framework/Web/UI/ActiveControls/TCallbackClientSide.php +++ b/framework/Web/UI/ActiveControls/TCallbackClientSide.php @@ -4,7 +4,7 @@ *
* @author Wei Zhuo <weizhuo[at]gamil[dot]com>
* @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2008 PradoSoft + * @copyright Copyright © 2005-2008 PradoSoft
* @license http://www.pradosoft.com/license/
* @version $Id$
* @package System.Web.UI.ActiveControls
@@ -22,13 +22,13 @@ * - <b>onLoaded</b>* executed when callback request begins.
* - <b>onInteractive</b> executed when callback request is in progress.
* - <b>onComplete</b>executed when callback response returns.
- * - * * Note that theses 2 events are not fired correctly by Opera. To make - * them work in this browser, Prado will fire them just after onPreDispatch. - * - * In a general way, onUninitialized, onLoading, onLoaded and onInteractive events - * are not implemented consistently in all browsers.When cross browser compatibility is - * needed, it is best to avoid use them + *
+ * * Note that theses 2 events are not fired correctly by Opera. To make
+ * them work in this browser, Prado will fire them just after onPreDispatch.
+ *
+ * In a general way, onUninitialized, onLoading, onLoaded and onInteractive events
+ * are not implemented consistently in all browsers.When cross browser compatibility is
+ * needed, it is best to avoid use them
*
* The OnSuccess and OnFailure events are raised when the
* response is returned. A successful request/response will raise
@@ -247,7 +247,7 @@ class TCallbackClientSide extends TClientSideOptions public function getHasPriority()
{
$option = $this->getOption('HasPriority');
- return is_null($option) ? true : $option;
+ return ($option===null) ? true : $option;
}
/**
@@ -284,7 +284,7 @@ class TCallbackClientSide extends TClientSideOptions public function getEnablePageStateUpdate()
{
$option = $this->getOption('EnablePageStateUpdate');
- return is_null($option) ? true : $option;
+ return ($option===null) ? true : $option;
}
/**
diff --git a/framework/Web/UI/ActiveControls/TCallbackOptions.php b/framework/Web/UI/ActiveControls/TCallbackOptions.php index 7c48b795..c9b649d8 100644 --- a/framework/Web/UI/ActiveControls/TCallbackOptions.php +++ b/framework/Web/UI/ActiveControls/TCallbackOptions.php @@ -4,7 +4,7 @@ *
* @author Wei Zhuo <weizhuo[at]gamil[dot]com>
* @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2008 PradoSoft + * @copyright Copyright © 2005-2008 PradoSoft
* @license http://www.pradosoft.com/license/
* @version $Id$
* @package System.Web.UI.ActiveControls
@@ -37,7 +37,7 @@ class TCallbackOptions extends TControl */
public function getClientSide()
{
- if(is_null($this->_clientSide))
+ if($this->_clientSide===null)
$this->_clientSide = $this->createClientSide();
return $this->_clientSide;
}
diff --git a/framework/Web/UI/ActiveControls/TDropContainer.php b/framework/Web/UI/ActiveControls/TDropContainer.php index 5d090d95..75a80625 100755 --- a/framework/Web/UI/ActiveControls/TDropContainer.php +++ b/framework/Web/UI/ActiveControls/TDropContainer.php @@ -123,12 +123,8 @@ class TDropContainer extends TPanel implements IActiveControl, ICallbackEventHan { // Find the control // Warning, this will not work if you have a '_' in your control Id ! - $control=$this->getPage(); - $namingContainers=explode(TControl::CLIENT_ID_SEPARATOR, $dropControlId); - foreach ($namingContainers as $nc) - { - $control=$control->findControl($nc); - } + $dropControlId=str_replace(TControl::CLIENT_ID_SEPARATOR,TControl::ID_SEPARATOR,$dropControlId); + $control=$this->getPage()->findControl($dropControlId); $this->raiseEvent('OnDrop', $this, new TDropContainerEventParameter ($control)); } @@ -196,7 +192,7 @@ class TDropContainer extends TPanel implements IActiveControl, ICallbackEventHan if ($this->_container===null) { $this->_container=Prado::CreateComponent('System.Web.UI.ActiveControls.TActivePanel'); - $this->_container->setId($this->getId().'_content'); + $this->_container->setId($this->getId(false).'_content'); parent::getControls()->add($this->_container); } } diff --git a/framework/Web/UI/ActiveControls/TInPlaceTextBox.php b/framework/Web/UI/ActiveControls/TInPlaceTextBox.php index 6e1c6b7a..b8dd666b 100644 --- a/framework/Web/UI/ActiveControls/TInPlaceTextBox.php +++ b/framework/Web/UI/ActiveControls/TInPlaceTextBox.php @@ -4,7 +4,7 @@ *
* @author Wei Zhuo <weizhuo[at]gamil[dot]com>
* @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2008 PradoSoft + * @copyright Copyright © 2005-2008 PradoSoft
* @license http://www.pradosoft.com/license/
* @version $Id$
* @package System.Web.UI.ActiveControls
@@ -35,9 +35,9 @@ Prado::using('System.Web.UI.ActiveControls.TActiveTextBox'); * After the callback request returns sucessfully, the textbox is enabled.
* If the {@link setAutoHideTextBox AutoHideTextBox} property is true, then
* the textbox will be hidden and the label is then shown.
- * - * Since 3.1.2, you can set the {@link setReadOnly ReadOnly} property to make - * the control not editable. This property can be also changed on callback + *
+ * Since 3.1.2, you can set the {@link setReadOnly ReadOnly} property to make
+ * the control not editable. This property can be also changed on callback
*
* @author Wei Zhuo <weizhuo[at]gmail[dot]com>
* @version $Id$
@@ -124,7 +124,7 @@ class TInPlaceTextBox extends TActiveTextBox protected function getExternalControlID()
{
$extID = $this->getEditTriggerControlID();
- if(is_null($extID)) return '';
+ if($extID===null) return '';
if(($control = $this->findControl($extID))!==null)
return $control->getClientID();
return $extID;
@@ -144,21 +144,21 @@ class TInPlaceTextBox extends TActiveTextBox $client->update($this->getLabelClientID(), $value);
$client->setValue($this, $value);
}
- } - - /** - * Update ClientSide Readonly property - * @param boolean value - * @since 3.1.2 - */ - public function setReadOnly ($value) - { - $value=TPropertyValue::ensureBoolean($value); - TTextBox::setReadOnly($value); - if ($this->getActiveControl()->canUpdateClientSide()) - { - $this->callClientFunction('setReadOnly', $value); - } + }
+
+ /**
+ * Update ClientSide Readonly property
+ * @param boolean value
+ * @since 3.1.2
+ */
+ public function setReadOnly ($value)
+ {
+ $value=TPropertyValue::ensureBoolean($value);
+ TTextBox::setReadOnly($value);
+ if ($this->getActiveControl()->canUpdateClientSide())
+ {
+ $this->callClientFunction('setReadOnly', $value);
+ }
}
/**
@@ -231,8 +231,8 @@ class TInPlaceTextBox extends TActiveTextBox }
if($this->hasEventHandler('OnLoadingText'))
- $options['LoadTextOnEdit'] = true; - + $options['LoadTextOnEdit'] = true;
+
$options['ReadOnly']=$this->getReadOnly();
return $options;
}
diff --git a/framework/Web/UI/TClientScriptManager.php b/framework/Web/UI/TClientScriptManager.php index f0eb6157..200bcba5 100644 --- a/framework/Web/UI/TClientScriptManager.php +++ b/framework/Web/UI/TClientScriptManager.php @@ -76,7 +76,12 @@ class TClientScriptManager extends TApplicationComponent * @var array */ private static $_pradoScripts; - + /** + * Client-side javascript library packages, loads from SCRIPT_PATH.'/packages.php'; + * @var array + */ + private static $_pradoPackages; + /** * Constructor. * @param TPage page that owns this client script manager @@ -120,6 +125,7 @@ class TClientScriptManager extends TApplicationComponent $packageFile = Prado::getFrameworkPath().DIRECTORY_SEPARATOR.self::SCRIPT_PATH.'/packages.php'; list($packages,$deps)= include($packageFile); self::$_pradoScripts = $deps; + self::$_pradoPackages = $packages; } if(isset(self::$_pradoScripts[$name])) @@ -147,9 +153,31 @@ class TClientScriptManager extends TApplicationComponent { if(($packages=array_keys($this->_registeredPradoScripts))!==array()) { - $base = Prado::getFrameworkPath().DIRECTORY_SEPARATOR.self::SCRIPT_PATH; - $url = $this->registerJavascriptPackages($base, $packages); - $writer->write(TJavaScript::renderScriptFile($url)); + if (Prado::getApplication()->getMode()!==TApplicationMode::Debug) + { + $base = Prado::getFrameworkPath().DIRECTORY_SEPARATOR.self::SCRIPT_PATH; + $url = $this->registerJavascriptPackages($base, $packages); + $writer->write(TJavaScript::renderScriptFile($url)); + } + else + { + // In debug mode, we add 1 <script> line by file + $base = Prado::getFrameworkPath().DIRECTORY_SEPARATOR.self::SCRIPT_PATH; + list($path,$baseUrl)=$this->getPackagePathUrl($base); + $packagesUrl=array(); + foreach ($packages as $p) + { + foreach (self::$_pradoScripts[$p] as $dep) + { + foreach (self::$_pradoPackages[$dep] as $script) + { + if (!in_array($url=$baseUrl.'/'.$script,$packagesUrl)) + $packagesUrl[]=$url; + } + } + } + $writer->write(TJavaScript::renderScriptFiles($packagesUrl)); + } } } diff --git a/framework/Web/UI/THtmlWriter.php b/framework/Web/UI/THtmlWriter.php index 217b9663..9713d941 100644 --- a/framework/Web/UI/THtmlWriter.php +++ b/framework/Web/UI/THtmlWriter.php @@ -232,12 +232,12 @@ class THtmlWriter extends TApplicationComponent implements ITextWriter if(isset(self::$_simpleTags[$tagName]))
{
$str.=' />';
- array_push($this->_openTags,'');
+ $this->_openTags[] = '';
}
else
{
$str.='>';
- array_push($this->_openTags,$tagName);
+ $this->_openTags[] = $tagName;
}
$this->_writer->write($str);
$this->_attributes=array();
diff --git a/framework/Web/UI/TTemplateManager.php b/framework/Web/UI/TTemplateManager.php index f44d414c..6d44d7d7 100644 --- a/framework/Web/UI/TTemplateManager.php +++ b/framework/Web/UI/TTemplateManager.php @@ -608,7 +608,7 @@ class TTemplate extends TApplicationComponent implements ITemplate $tpl[$c++]=array($container,$type,$attributes);
if($str[strlen($str)-2]!=='/') // open tag
{
- array_push($stack,$type);
+ $stack[] = $type;
$container=$c-1;
}
}
@@ -695,7 +695,7 @@ class TTemplate extends TApplicationComponent implements ITemplate else // regular property
{
$prop=strtolower($match[3][0]);
- array_push($stack,'@'.$prop);
+ $stack[] = '@'.$prop;
if(!$expectPropEnd)
{
if($matchStart>$textStart)
diff --git a/framework/Web/UI/WebControls/TBaseValidator.php b/framework/Web/UI/WebControls/TBaseValidator.php index b418885c..6daae4d0 100644 --- a/framework/Web/UI/WebControls/TBaseValidator.php +++ b/framework/Web/UI/WebControls/TBaseValidator.php @@ -1,733 +1,733 @@ -<?php -/** - * TBaseValidator class file - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2008 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Web.UI.WebControls - */ - -/** - * Using TLabel class - */ -Prado::using('System.Web.UI.WebControls.TLabel'); - -/** - * TBaseValidator class - * - * TBaseValidator serves as the base class for validator controls. - * - * Validation is performed when a postback control, such as a TButton, a TLinkButton - * or a TTextBox (under AutoPostBack mode) is submitting the page and - * its <b>CausesValidation</b> property is true. - * You can also manually perform validation by calling {@link TPage::validate()}. - * The input control to be validated is specified by {@link setControlToValidate ControlToValidate}. - * - * Validator controls always validate the associated input control on the serve side. - * In addition, if {@link getEnableClientScript EnableClientScript} is true, - * validation will also be performed on the client-side using javascript. - * Client-side validation will validate user input before it is sent to the server. - * The form data will not be submitted if any error is detected. This avoids - * the round-trip of information necessary for server-side validation. - * - * You can use multiple validator controls to validate a single input control, - * each responsible for validating against a different criteria. - * For example, on a user registration form, you may want to make sure the user - * enters a value in the username text box, and the input must consist of only word - * characters. You can use a {@link TRequiredFieldValidator} to ensure the input - * of username and a {@link TRegularExpressionValidator} to ensure the proper input. - * - * If an input control fails validation, the text specified by the {@link setErrorMessage ErrorMessage} - * property is displayed in the validation control. However, if the {@link setText Text} - * property is set, it will be displayed instead. If both {@link setErrorMessage ErrorMessage} - * and {@link setText Text} are empty, the body content of the validator will - * be displayed. Error display is controlled by {@link setDisplay Display} property. - * - * You can also customized the client-side behaviour by adding javascript - * code to the subproperties of the {@link getClientSide ClientSide} - * property. See quickstart documentation for further details. - * - * You can also place a {@link TValidationSummary} control on a page to display error messages - * from the validators together. In this case, only the {@link setErrorMessage ErrorMessage} - * property of the validators will be displayed in the {@link TValidationSummary} control. - * - * Validators can be partitioned into validation groups by setting their - * {@link setValidationGroup ValidationGroup} property. If the control causing the - * validation also sets its ValidationGroup property, only those validators having - * the same ValidationGroup value will do input validation. - * - * Note, the {@link TPage::getIsValid IsValid} property of the current {@link TPage} - * instance will be automatically updated by the validation process which occurs - * after {@link TPage::onLoad onLoad} of {@link TPage} and before the postback events. - * Therefore, if you use the {@link TPage::getIsValid()} property in - * the {@link TPage::onLoad()} method, you must first explicitly call - * the {@link TPage::validate()} method. - * - * <b>Notes to Inheritors</b> When you inherit from TBaseValidator, you must - * override the method {@link evaluateIsValid}. - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -abstract class TBaseValidator extends TLabel implements IValidator -{ - /** - * @var boolean whether the validation succeeds - */ - private $_isValid=true; - /** - * @var boolean whether the validator has been registered with the page - */ - private $_registered=false; - /** - * @var TValidatorClientSide validator client-script options. - */ - private $_clientSide; - /** - * Controls for which the client-side validation3.js file needs to handle - * them specially. - * @var array list of control class names - */ - private static $_clientClass = array('THtmlArea', 'TDatePicker', 'TListBox', 'TCheckBoxList'); - - /** - * Constructor. - * This method sets the foreground color to red. - */ - public function __construct() - { - parent::__construct(); - $this->setForeColor('red'); - } - - /** - * Registers the validator with page. - * @param mixed event parameter - */ - public function onInit($param) - { - parent::onInit($param); - $this->getPage()->getValidators()->add($this); - $this->_registered=true; - } - - /** - * Unregisters the validator from page. - * @param mixed event parameter - */ - public function onUnload($param) - { - if($this->_registered && ($page=$this->getPage())!==null) - $page->getValidators()->remove($this); - $this->_registered=false; - parent::onUnload($param); - } - - /** - * Adds attributes to renderer. Calls parent implementation and renders the - * client control scripts. - * @param THtmlWriter the renderer - */ - protected function addAttributesToRender($writer) - { - $display=$this->getDisplay(); - $visible=$this->getEnabled(true) && !$this->getIsValid(); - if($display===TValidatorDisplayStyle::None || (!$visible && $display===TValidatorDisplayStyle::Dynamic)) - $writer->addStyleAttribute('display','none'); - else if(!$visible) - $writer->addStyleAttribute('visibility','hidden'); - $writer->addAttribute('id',$this->getClientID()); - parent::addAttributesToRender($writer); - $this->renderClientControlScript($writer); - } - - /** - * Returns an array of javascript validator options. - * @return array javascript validator options. - */ - protected function getClientScriptOptions() - { - $control = $this->getValidationTarget(); - $options['ID'] = $this->getClientID(); - $options['FormID'] = $this->getPage()->getForm()->getClientID(); - $options['Display'] = $this->getDisplay(); - $options['ErrorMessage'] = $this->getErrorMessage(); - if($this->getFocusOnError()) - { - $options['FocusOnError'] = $this->getFocusOnError(); - $options['FocusElementID'] = $this->getFocusElementID(); - } - $options['ValidationGroup'] = $this->getValidationGroup(); - $options['ControlToValidate'] = $control->getClientID(); - $options['ControlCssClass'] = $this->getControlCssClass(); - - $options['ControlType'] = $this->getClientControlClass($control); - - //get date format from date picker target control - if($control instanceof TDatePicker) - $options['DateFormat'] = $control->getDateFormat(); - - $options = array_merge($options,$this->getClientSide()->getOptions()->toArray()); - - return $options; - } - - /** - * Gets the Control type for client-side validation. If new cases exists in - * TBaseValidator::$_clientClass, be sure to update the corresponding - * "Javascript/validation3.js" file as well. - * @param TControl control to validate. - * @return string control type for client-side validation. - */ - private function getClientControlClass($control) - { - foreach(self::$_clientClass as $type) - if($control instanceof $type) - return $type; - return get_class($control); - } - - /** - * Gets the TValidatorClientSide that allows modification of the client- - * side validator events. - * - * The client-side validator supports the following events. - * # <tt>OnValidate</tt> -- raised before client-side validation is - * executed. - * # <tt>OnValidationSuccess</tt> -- raised after client-side validation is completed - * and is successfull, overrides default validator error messages updates. - * # <tt>OnValidationError</tt> -- raised after client-side validation is completed - * and failed, overrides default validator error message updates. - * - * You can attach custom javascript code to each of these events - * - * @return TValidatorClientSide javascript validator event options. - */ - public function getClientSide() - { - if(is_null($this->_clientSide)) - $this->_clientSide = $this->createClientSide(); - return $this->_clientSide; - } - - /** - * @return TValidatorClientSide javascript validator event options. - */ - protected function createClientSide() - { - return new TValidatorClientSide; - } - - /** - * Renders the javascript code to the end script. - * If you override this method, be sure to call the parent implementation - * so that the event handlers can be invoked. - * @param THtmlWriter the renderer - */ - public function renderClientControlScript($writer) - { - $scripts = $this->getPage()->getClientScript(); - $formID=$this->getPage()->getForm()->getClientID(); - $scriptKey = "TBaseValidator:$formID"; - if($this->getEnableClientScript() && !$scripts->isEndScriptRegistered($scriptKey)) - { - $manager['FormID'] = $formID; - $options = TJavaScript::encode($manager); - $scripts->registerPradoScript('validator'); - $scripts->registerEndScript($scriptKey, "new Prado.ValidationManager({$options});"); - } - if($this->getEnableClientScript() & $this->getEnabled(true)) - $this->registerClientScriptValidator(); - } - - /** - * Override parent implementation to update the control CSS Class before - * the validated control is rendered - */ - public function onPreRender ($param) - { - parent::onPreRender($param); - $this->updateControlCssClass(); - } - - /** - * Update the ControlToValidate component's css class depending - * if the ControlCssClass property is set, and whether this is valid. - * @return boolean true if change, false otherwise. - */ - protected function updateControlCssClass() - { - if(($cssClass=$this->getControlCssClass())!=='') - { - $control=$this->getValidationTarget(); - if($control instanceof TWebControl) - { - $class = preg_replace ('/ '.preg_quote($cssClass).'/', '',$control->getCssClass()); - if(!$this->getIsValid()) - { - $class .= ' '.$cssClass; - $control->setCssClass($class); - } elseif ($control->getIsValid()) - $control->setCssClass($class); - } - } - } - - /** - * Registers the individual validator client-side javascript code. - */ - protected function registerClientScriptValidator() - { - $key = 'prado:'.$this->getClientID(); - if(!$this->getPage()->getClientScript()->isEndScriptRegistered($key)) - { - $options = TJavaScript::encode($this->getClientScriptOptions()); - $script = 'new '.$this->getClientClassName().'('.$options.');'; - $this->getPage()->getClientScript()->registerEndScript($key, $script); - } - } - - /** - * Gets the name of the javascript class responsible for performing validation for this control. - * This method overrides the parent implementation. - * @return string the javascript class name - */ - abstract protected function getClientClassName(); - - /** - * This method overrides the parent implementation to forbid setting ForControl. - * @param string the associated control ID - * @throws TNotSupportedException whenever this method is called - */ - public function setForControl($value) - { - throw new TNotSupportedException('basevalidator_forcontrol_unsupported',get_class($this)); - } - - /** - * This method overrides parent's implementation by setting {@link setIsValid IsValid} to true if disabled. - * @param boolean whether the validator is enabled. - */ - public function setEnabled($value) - { - $value=TPropertyValue::ensureBoolean($value); - parent::setEnabled($value); - if(!$value) - $this->_isValid=true; - } - - /** - * @return TValidatorDisplayStyle the style of displaying the error message. Defaults to TValidatorDisplayStyle::Fixed. - */ - public function getDisplay() - { - return $this->getViewState('Display',TValidatorDisplayStyle::Fixed); - } - - /** - * @param TValidatorDisplayStyle the style of displaying the error message - */ - public function setDisplay($value) - { - $this->setViewState('Display',TPropertyValue::ensureEnum($value,'TValidatorDisplayStyle'),TValidatorDisplayStyle::Fixed); - } - - /** - * @return boolean whether client-side validation is enabled. - */ - public function getEnableClientScript() - { - return $this->getViewState('EnableClientScript',true); - } - - /** - * @param boolean whether client-side validation is enabled. - */ - public function setEnableClientScript($value) - { - $this->setViewState('EnableClientScript',TPropertyValue::ensureBoolean($value),true); - } - - /** - * @return string the text for the error message. - */ - public function getErrorMessage() - { - return $this->getViewState('ErrorMessage',''); - } - - /** - * Sets the text for the error message. - * @param string the error message - */ - public function setErrorMessage($value) - { - $this->setViewState('ErrorMessage',$value,''); - } - - /** - * @return string the ID path of the input control to validate - */ - public function getControlToValidate() - { - return $this->getViewState('ControlToValidate',''); - } - - /** - * Sets the ID path of the input control to validate. - * The ID path is the dot-connected IDs of the controls reaching from - * the validator's naming container to the target control. - * @param string the ID path - */ - public function setControlToValidate($value) - { - $this->setViewState('ControlToValidate',$value,''); - } - - /** - * @return boolean whether to set focus at the validating place if the validation fails. Defaults to false. - */ - public function getFocusOnError() - { - return $this->getViewState('FocusOnError',false); - } - - /** - * @param boolean whether to set focus at the validating place if the validation fails - */ - public function setFocusOnError($value) - { - $this->setViewState('FocusOnError',TPropertyValue::ensureBoolean($value),false); - } - - /** - * Gets the ID of the HTML element that will receive focus if validation fails and {@link getFocusOnError FocusOnError} is true. - * Defaults to the client ID of the {@link getControlToValidate ControlToValidate}. - * @return string the ID of the HTML element to receive focus - */ - public function getFocusElementID() - { - if(($id=$this->getViewState('FocusElementID',''))==='') - $id=$this->getValidationTarget()->getClientID(); - return $id; - } - - /** - * Sets the ID of the HTML element that will receive focus if validation fails and {@link getFocusOnError FocusOnError} is true. - * @param string the ID of the HTML element to receive focus - */ - public function setFocusElementID($value) - { - $this->setViewState('FocusElementID', $value, ''); - } - - /** - * @return string the group which this validator belongs to - */ - public function getValidationGroup() - { - return $this->getViewState('ValidationGroup',''); - } - - /** - * @param string the group which this validator belongs to - */ - public function setValidationGroup($value) - { - $this->setViewState('ValidationGroup',$value,''); - } - - /** - * @return boolean whether the validation succeeds - */ - public function getIsValid() - { - return $this->_isValid; - } - - /** - * Sets the value indicating whether the validation succeeds - * @param boolean whether the validation succeeds - */ - public function setIsValid($value) - { - $this->_isValid=TPropertyValue::ensureBoolean($value); - } - - /** - * @return TControl control to be validated. Null if no control is found. - * @throws TConfigurationException if {@link getControlToValidate - * ControlToValidate} is empty or does not point to a valid control - */ - protected function getValidationTarget() - { - if(($id=$this->getControlToValidate())!=='' && ($control=$this->findControl($id))!==null) - return $control; - else - throw new TConfigurationException('basevalidator_controltovalidate_invalid',get_class($this)); - } - - /** - * Retrieves the property value of the control being validated. - * @param TControl control being validated - * @return string property value to be validated - * @throws TInvalidDataTypeException if the control to be validated does not implement {@link IValidatable}. - */ - protected function getValidationValue($control) - { - if($control instanceof IValidatable) - return $control->getValidationPropertyValue(); - else - throw new TInvalidDataTypeException('basevalidator_validatable_required',get_class($this)); - } - - /** - * Validates the specified control. - * Do not override this method. Override {@link evaluateIsValid} instead. - * @return boolean whether the validation succeeds - */ - public function validate() - { - $this->setIsValid(true); - $this->onValidate(); - if($this->getVisible(true) && $this->getEnabled(true)) - { - // if the target is not a disabled web control - if(($target=$this->getValidationTarget())!==null && !($target instanceof TWebControl && !$target->getEnabled(true))) - { - if($this->evaluateIsValid()) - { - $this->setIsValid(true); - $this->onValidationSuccess(); - } - else - { - $target->setIsValid(false); - $this->setIsValid(false); - $this->onValidationError(); - } - } - else - { - $this->evaluateIsValid(); - $this->setIsValid(true); - $this->onValidationSuccess(); - } - } - return $this->getIsValid(); - } - - /** - * @return string the css class that is applied to the control being validated in case the validation fails - */ - public function getControlCssClass() - { - return $this->getViewState('ControlCssClass',''); - } - - /** - * @param string the css class that is applied to the control being validated in case the validation fails - */ - public function setControlCssClass($value) - { - $this->setViewState('ControlCssClass',$value,''); - } - - /** - * This is the major method for validation. - * Derived classes should implement this method to provide customized validation. - * @return boolean whether the validation succeeds - */ - abstract protected function evaluateIsValid(); - - /** - * This event is raised when the validator succeeds in validation. - */ - public function onValidationSuccess() - { - $this->raiseEvent('OnValidationSuccess',$this,null); - } - - /** - * This event is raised when the validator fails in validation. - */ - public function onValidationError() - { - $this->raiseEvent('OnValidationError',$this,null); - } - - /** - * This event is raised right before the validator starts to perform validation. - * You may use this event to change the behavior of validation. - * For example, you may disable the validator if certain condition is satisfied. - * Note, the event will NOT be raised if the validator is invisible. - */ - public function onValidate() - { - $this->raiseEvent('OnValidate',$this,null); - } - - /** - * Renders the validator control. - * @param THtmlWriter writer for the rendering purpose - */ - public function renderContents($writer) - { - if(($text=$this->getText())!=='') - $writer->write($text); - else if(($text=$this->getErrorMessage())!=='') - $writer->write($text); - else - parent::renderContents($writer); - } -} - -/** - * TValidatorClientSide class. - * - * Client-side validator events can be modified through the {@link - * TBaseValidator::getClientSide ClientSide} property of a validator. The - * subproperties of ClientSide are those of the TValidatorClientSide - * properties. The client-side validator supports the following events. - * - * The <tt>OnValidate</tt> event is raise before the validator validation - * functions are called. - * - * The <tt>OnValidationSuccess</tt> event is raised after the validator has successfully - * validate the control. - * - * The <tt>OnValidationError</tt> event is raised after the validator fails validation. - * - * See the quickstart documentation for further details. - * - * @author Wei Zhuo <weizhuo[at]gmail[dot]com> - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0 - */ -class TValidatorClientSide extends TClientSideOptions -{ - /** - * @return string javascript code for client-side OnValidate event. - */ - public function getOnValidate() - { - return $this->getOption('OnValidate'); - } - - /** - * Client-side OnValidate validator event is raise before the validators - * validation functions are called. - * @param string javascript code for client-side OnValidate event. - */ - public function setOnValidate($javascript) - { - $this->setFunction('OnValidate', $javascript); - } - - /** - * Client-side OnSuccess event is raise after validation is successfull. - * This will override the default client-side validator behaviour. - * @param string javascript code for client-side OnSuccess event. - */ - public function setOnValidationSuccess($javascript) - { - $this->setFunction('OnValidationSuccess', $javascript); - } - - /** - * @return string javascript code for client-side OnSuccess event. - */ - public function getOnValidationSuccess() - { - return $this->getOption('OnValidationSuccess'); - } - - /** - * Client-side OnError event is raised after validation failure. - * This will override the default client-side validator behaviour. - * @param string javascript code for client-side OnError event. - */ - public function setOnValidationError($javascript) - { - $this->setFunction('OnValidationError', $javascript); - } - - /** - * @return string javascript code for client-side OnError event. - */ - public function getOnValidationError() - { - return $this->getOption('OnValidationError'); - } - - /** - * @param boolean true to revalidate when the control to validate changes value. - */ - public function setObserveChanges($value) - { - $this->setOption('ObserveChanges', TPropertyValue::ensureBoolean($value)); - } - - /** - * @return boolean true to observe changes. - */ - public function getObserveChanges() - { - $changes = $this->getOption('ObserveChanges'); - return is_null($changes) ? true : $changes; - } -} - - -/** - * TValidatorDisplayStyle class. - * TValidatorDisplayStyle defines the enumerable type for the possible styles - * that a validator control can display the error message. - * - * The following enumerable values are defined: - * - None: the error message is not displayed - * - Dynamic: the error message dynamically appears when the validator fails validation - * - Fixed: Similar to Dynamic except that the error message physically occupies the page layout (even though it may not be visible) - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0.4 - */ -class TValidatorDisplayStyle extends TEnumerable -{ - const None='None'; - const Dynamic='Dynamic'; - const Fixed='Fixed'; -} - -/** - * TValidationDataType class. - * TValidationDataType defines the enumerable type for the possible data types that - * a comparison validator can validate upon. - * - * The following enumerable values are defined: - * - Integer - * - Float - * - Date - * - String - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @version $Id$ - * @package System.Web.UI.WebControls - * @since 3.0.4 - */ -class TValidationDataType extends TEnumerable -{ - const Integer='Integer'; - const Float='Float'; - const Date='Date'; - const String='String'; -} - +<?php
+/**
+ * TBaseValidator class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005-2008 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * Using TLabel class
+ */
+Prado::using('System.Web.UI.WebControls.TLabel');
+
+/**
+ * TBaseValidator class
+ *
+ * TBaseValidator serves as the base class for validator controls.
+ *
+ * Validation is performed when a postback control, such as a TButton, a TLinkButton
+ * or a TTextBox (under AutoPostBack mode) is submitting the page and
+ * its <b>CausesValidation</b> property is true.
+ * You can also manually perform validation by calling {@link TPage::validate()}.
+ * The input control to be validated is specified by {@link setControlToValidate ControlToValidate}.
+ *
+ * Validator controls always validate the associated input control on the serve side.
+ * In addition, if {@link getEnableClientScript EnableClientScript} is true,
+ * validation will also be performed on the client-side using javascript.
+ * Client-side validation will validate user input before it is sent to the server.
+ * The form data will not be submitted if any error is detected. This avoids
+ * the round-trip of information necessary for server-side validation.
+ *
+ * You can use multiple validator controls to validate a single input control,
+ * each responsible for validating against a different criteria.
+ * For example, on a user registration form, you may want to make sure the user
+ * enters a value in the username text box, and the input must consist of only word
+ * characters. You can use a {@link TRequiredFieldValidator} to ensure the input
+ * of username and a {@link TRegularExpressionValidator} to ensure the proper input.
+ *
+ * If an input control fails validation, the text specified by the {@link setErrorMessage ErrorMessage}
+ * property is displayed in the validation control. However, if the {@link setText Text}
+ * property is set, it will be displayed instead. If both {@link setErrorMessage ErrorMessage}
+ * and {@link setText Text} are empty, the body content of the validator will
+ * be displayed. Error display is controlled by {@link setDisplay Display} property.
+ *
+ * You can also customized the client-side behaviour by adding javascript
+ * code to the subproperties of the {@link getClientSide ClientSide}
+ * property. See quickstart documentation for further details.
+ *
+ * You can also place a {@link TValidationSummary} control on a page to display error messages
+ * from the validators together. In this case, only the {@link setErrorMessage ErrorMessage}
+ * property of the validators will be displayed in the {@link TValidationSummary} control.
+ *
+ * Validators can be partitioned into validation groups by setting their
+ * {@link setValidationGroup ValidationGroup} property. If the control causing the
+ * validation also sets its ValidationGroup property, only those validators having
+ * the same ValidationGroup value will do input validation.
+ *
+ * Note, the {@link TPage::getIsValid IsValid} property of the current {@link TPage}
+ * instance will be automatically updated by the validation process which occurs
+ * after {@link TPage::onLoad onLoad} of {@link TPage} and before the postback events.
+ * Therefore, if you use the {@link TPage::getIsValid()} property in
+ * the {@link TPage::onLoad()} method, you must first explicitly call
+ * the {@link TPage::validate()} method.
+ *
+ * <b>Notes to Inheritors</b> When you inherit from TBaseValidator, you must
+ * override the method {@link evaluateIsValid}.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id$
+ * @package System.Web.UI.WebControls
+ * @since 3.0
+ */
+abstract class TBaseValidator extends TLabel implements IValidator
+{
+ /**
+ * @var boolean whether the validation succeeds
+ */
+ private $_isValid=true;
+ /**
+ * @var boolean whether the validator has been registered with the page
+ */
+ private $_registered=false;
+ /**
+ * @var TValidatorClientSide validator client-script options.
+ */
+ private $_clientSide;
+ /**
+ * Controls for which the client-side validation3.js file needs to handle
+ * them specially.
+ * @var array list of control class names
+ */
+ private static $_clientClass = array('THtmlArea', 'TDatePicker', 'TListBox', 'TCheckBoxList');
+
+ /**
+ * Constructor.
+ * This method sets the foreground color to red.
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ $this->setForeColor('red');
+ }
+
+ /**
+ * Registers the validator with page.
+ * @param mixed event parameter
+ */
+ public function onInit($param)
+ {
+ parent::onInit($param);
+ $this->getPage()->getValidators()->add($this);
+ $this->_registered=true;
+ }
+
+ /**
+ * Unregisters the validator from page.
+ * @param mixed event parameter
+ */
+ public function onUnload($param)
+ {
+ if($this->_registered && ($page=$this->getPage())!==null)
+ $page->getValidators()->remove($this);
+ $this->_registered=false;
+ parent::onUnload($param);
+ }
+
+ /**
+ * Adds attributes to renderer. Calls parent implementation and renders the
+ * client control scripts.
+ * @param THtmlWriter the renderer
+ */
+ protected function addAttributesToRender($writer)
+ {
+ $display=$this->getDisplay();
+ $visible=$this->getEnabled(true) && !$this->getIsValid();
+ if($display===TValidatorDisplayStyle::None || (!$visible && $display===TValidatorDisplayStyle::Dynamic))
+ $writer->addStyleAttribute('display','none');
+ else if(!$visible)
+ $writer->addStyleAttribute('visibility','hidden');
+ $writer->addAttribute('id',$this->getClientID());
+ parent::addAttributesToRender($writer);
+ $this->renderClientControlScript($writer);
+ }
+
+ /**
+ * Returns an array of javascript validator options.
+ * @return array javascript validator options.
+ */
+ protected function getClientScriptOptions()
+ {
+ $control = $this->getValidationTarget();
+ $options['ID'] = $this->getClientID();
+ $options['FormID'] = $this->getPage()->getForm()->getClientID();
+ $options['Display'] = $this->getDisplay();
+ $options['ErrorMessage'] = $this->getErrorMessage();
+ if($this->getFocusOnError())
+ {
+ $options['FocusOnError'] = $this->getFocusOnError();
+ $options['FocusElementID'] = $this->getFocusElementID();
+ }
+ $options['ValidationGroup'] = $this->getValidationGroup();
+ $options['ControlToValidate'] = $control->getClientID();
+ $options['ControlCssClass'] = $this->getControlCssClass();
+
+ $options['ControlType'] = $this->getClientControlClass($control);
+
+ //get date format from date picker target control
+ if($control instanceof TDatePicker)
+ $options['DateFormat'] = $control->getDateFormat();
+
+ $options = array_merge($options,$this->getClientSide()->getOptions()->toArray());
+
+ return $options;
+ }
+
+ /**
+ * Gets the Control type for client-side validation. If new cases exists in
+ * TBaseValidator::$_clientClass, be sure to update the corresponding
+ * "Javascript/validation3.js" file as well.
+ * @param TControl control to validate.
+ * @return string control type for client-side validation.
+ */
+ private function getClientControlClass($control)
+ {
+ foreach(self::$_clientClass as $type)
+ if($control instanceof $type)
+ return $type;
+ return get_class($control);
+ }
+
+ /**
+ * Gets the TValidatorClientSide that allows modification of the client-
+ * side validator events.
+ *
+ * The client-side validator supports the following events.
+ * # <tt>OnValidate</tt> -- raised before client-side validation is
+ * executed.
+ * # <tt>OnValidationSuccess</tt> -- raised after client-side validation is completed
+ * and is successfull, overrides default validator error messages updates.
+ * # <tt>OnValidationError</tt> -- raised after client-side validation is completed
+ * and failed, overrides default validator error message updates.
+ *
+ * You can attach custom javascript code to each of these events
+ *
+ * @return TValidatorClientSide javascript validator event options.
+ */
+ public function getClientSide()
+ {
+ if($this->_clientSide===null)
+ $this->_clientSide = $this->createClientSide();
+ return $this->_clientSide;
+ }
+
+ /**
+ * @return TValidatorClientSide javascript validator event options.
+ */
+ protected function createClientSide()
+ {
+ return new TValidatorClientSide;
+ }
+
+ /**
+ * Renders the javascript code to the end script.
+ * If you override this method, be sure to call the parent implementation
+ * so that the event handlers can be invoked.
+ * @param THtmlWriter the renderer
+ */
+ public function renderClientControlScript($writer)
+ {
+ $scripts = $this->getPage()->getClientScript();
+ $formID=$this->getPage()->getForm()->getClientID();
+ $scriptKey = "TBaseValidator:$formID";
+ if($this->getEnableClientScript() && !$scripts->isEndScriptRegistered($scriptKey))
+ {
+ $manager['FormID'] = $formID;
+ $options = TJavaScript::encode($manager);
+ $scripts->registerPradoScript('validator');
+ $scripts->registerEndScript($scriptKey, "new Prado.ValidationManager({$options});");
+ }
+ if($this->getEnableClientScript() & $this->getEnabled(true))
+ $this->registerClientScriptValidator();
+ }
+
+ /**
+ * Override parent implementation to update the control CSS Class before
+ * the validated control is rendered
+ */
+ public function onPreRender ($param)
+ {
+ parent::onPreRender($param);
+ $this->updateControlCssClass();
+ }
+
+ /**
+ * Update the ControlToValidate component's css class depending
+ * if the ControlCssClass property is set, and whether this is valid.
+ * @return boolean true if change, false otherwise.
+ */
+ protected function updateControlCssClass()
+ {
+ if(($cssClass=$this->getControlCssClass())!=='')
+ {
+ $control=$this->getValidationTarget();
+ if($control instanceof TWebControl)
+ {
+ $class = preg_replace ('/ '.preg_quote($cssClass).'/', '',$control->getCssClass());
+ if(!$this->getIsValid())
+ {
+ $class .= ' '.$cssClass;
+ $control->setCssClass($class);
+ } elseif ($control->getIsValid())
+ $control->setCssClass($class);
+ }
+ }
+ }
+
+ /**
+ * Registers the individual validator client-side javascript code.
+ */
+ protected function registerClientScriptValidator()
+ {
+ $key = 'prado:'.$this->getClientID();
+ if(!$this->getPage()->getClientScript()->isEndScriptRegistered($key))
+ {
+ $options = TJavaScript::encode($this->getClientScriptOptions());
+ $script = 'new '.$this->getClientClassName().'('.$options.');';
+ $this->getPage()->getClientScript()->registerEndScript($key, $script);
+ }
+ }
+
+ /**
+ * Gets the name of the javascript class responsible for performing validation for this control.
+ * This method overrides the parent implementation.
+ * @return string the javascript class name
+ */
+ abstract protected function getClientClassName();
+
+ /**
+ * This method overrides the parent implementation to forbid setting ForControl.
+ * @param string the associated control ID
+ * @throws TNotSupportedException whenever this method is called
+ */
+ public function setForControl($value)
+ {
+ throw new TNotSupportedException('basevalidator_forcontrol_unsupported',get_class($this));
+ }
+
+ /**
+ * This method overrides parent's implementation by setting {@link setIsValid IsValid} to true if disabled.
+ * @param boolean whether the validator is enabled.
+ */
+ public function setEnabled($value)
+ {
+ $value=TPropertyValue::ensureBoolean($value);
+ parent::setEnabled($value);
+ if(!$value)
+ $this->_isValid=true;
+ }
+
+ /**
+ * @return TValidatorDisplayStyle the style of displaying the error message. Defaults to TValidatorDisplayStyle::Fixed.
+ */
+ public function getDisplay()
+ {
+ return $this->getViewState('Display',TValidatorDisplayStyle::Fixed);
+ }
+
+ /**
+ * @param TValidatorDisplayStyle the style of displaying the error message
+ */
+ public function setDisplay($value)
+ {
+ $this->setViewState('Display',TPropertyValue::ensureEnum($value,'TValidatorDisplayStyle'),TValidatorDisplayStyle::Fixed);
+ }
+
+ /**
+ * @return boolean whether client-side validation is enabled.
+ */
+ public function getEnableClientScript()
+ {
+ return $this->getViewState('EnableClientScript',true);
+ }
+
+ /**
+ * @param boolean whether client-side validation is enabled.
+ */
+ public function setEnableClientScript($value)
+ {
+ $this->setViewState('EnableClientScript',TPropertyValue::ensureBoolean($value),true);
+ }
+
+ /**
+ * @return string the text for the error message.
+ */
+ public function getErrorMessage()
+ {
+ return $this->getViewState('ErrorMessage','');
+ }
+
+ /**
+ * Sets the text for the error message.
+ * @param string the error message
+ */
+ public function setErrorMessage($value)
+ {
+ $this->setViewState('ErrorMessage',$value,'');
+ }
+
+ /**
+ * @return string the ID path of the input control to validate
+ */
+ public function getControlToValidate()
+ {
+ return $this->getViewState('ControlToValidate','');
+ }
+
+ /**
+ * Sets the ID path of the input control to validate.
+ * The ID path is the dot-connected IDs of the controls reaching from
+ * the validator's naming container to the target control.
+ * @param string the ID path
+ */
+ public function setControlToValidate($value)
+ {
+ $this->setViewState('ControlToValidate',$value,'');
+ }
+
+ /**
+ * @return boolean whether to set focus at the validating place if the validation fails. Defaults to false.
+ */
+ public function getFocusOnError()
+ {
+ return $this->getViewState('FocusOnError',false);
+ }
+
+ /**
+ * @param boolean whether to set focus at the validating place if the validation fails
+ */
+ public function setFocusOnError($value)
+ {
+ $this->setViewState('FocusOnError',TPropertyValue::ensureBoolean($value),false);
+ }
+
+ /**
+ * Gets the ID of the HTML element that will receive focus if validation fails and {@link getFocusOnError FocusOnError} is true.
+ * Defaults to the client ID of the {@link getControlToValidate ControlToValidate}.
+ * @return string the ID of the HTML element to receive focus
+ */
+ public function getFocusElementID()
+ {
+ if(($id=$this->getViewState('FocusElementID',''))==='')
+ $id=$this->getValidationTarget()->getClientID();
+ return $id;
+ }
+
+ /**
+ * Sets the ID of the HTML element that will receive focus if validation fails and {@link getFocusOnError FocusOnError} is true.
+ * @param string the ID of the HTML element to receive focus
+ */
+ public function setFocusElementID($value)
+ {
+ $this->setViewState('FocusElementID', $value, '');
+ }
+
+ /**
+ * @return string the group which this validator belongs to
+ */
+ public function getValidationGroup()
+ {
+ return $this->getViewState('ValidationGroup','');
+ }
+
+ /**
+ * @param string the group which this validator belongs to
+ */
+ public function setValidationGroup($value)
+ {
+ $this->setViewState('ValidationGroup',$value,'');
+ }
+
+ /**
+ * @return boolean whether the validation succeeds
+ */
+ public function getIsValid()
+ {
+ return $this->_isValid;
+ }
+
+ /**
+ * Sets the value indicating whether the validation succeeds
+ * @param boolean whether the validation succeeds
+ */
+ public function setIsValid($value)
+ {
+ $this->_isValid=TPropertyValue::ensureBoolean($value);
+ }
+
+ /**
+ * @return TControl control to be validated. Null if no control is found.
+ * @throws TConfigurationException if {@link getControlToValidate
+ * ControlToValidate} is empty or does not point to a valid control
+ */
+ public function getValidationTarget()
+ {
+ if(($id=$this->getControlToValidate())!=='' && ($control=$this->findControl($id))!==null)
+ return $control;
+ else
+ throw new TConfigurationException('basevalidator_controltovalidate_invalid',get_class($this));
+ }
+
+ /**
+ * Retrieves the property value of the control being validated.
+ * @param TControl control being validated
+ * @return string property value to be validated
+ * @throws TInvalidDataTypeException if the control to be validated does not implement {@link IValidatable}.
+ */
+ protected function getValidationValue($control)
+ {
+ if($control instanceof IValidatable)
+ return $control->getValidationPropertyValue();
+ else
+ throw new TInvalidDataTypeException('basevalidator_validatable_required',get_class($this));
+ }
+
+ /**
+ * Validates the specified control.
+ * Do not override this method. Override {@link evaluateIsValid} instead.
+ * @return boolean whether the validation succeeds
+ */
+ public function validate()
+ {
+ $this->setIsValid(true);
+ $this->onValidate();
+ if($this->getVisible(true) && $this->getEnabled(true))
+ {
+ // if the target is not a disabled web control
+ if(($target=$this->getValidationTarget())!==null && !($target instanceof TWebControl && !$target->getEnabled(true)))
+ {
+ if($this->evaluateIsValid())
+ {
+ $this->setIsValid(true);
+ $this->onValidationSuccess();
+ }
+ else
+ {
+ $target->setIsValid(false);
+ $this->setIsValid(false);
+ $this->onValidationError();
+ }
+ }
+ else
+ {
+ $this->evaluateIsValid();
+ $this->setIsValid(true);
+ $this->onValidationSuccess();
+ }
+ }
+ return $this->getIsValid();
+ }
+
+ /**
+ * @return string the css class that is applied to the control being validated in case the validation fails
+ */
+ public function getControlCssClass()
+ {
+ return $this->getViewState('ControlCssClass','');
+ }
+
+ /**
+ * @param string the css class that is applied to the control being validated in case the validation fails
+ */
+ public function setControlCssClass($value)
+ {
+ $this->setViewState('ControlCssClass',$value,'');
+ }
+
+ /**
+ * This is the major method for validation.
+ * Derived classes should implement this method to provide customized validation.
+ * @return boolean whether the validation succeeds
+ */
+ abstract protected function evaluateIsValid();
+
+ /**
+ * This event is raised when the validator succeeds in validation.
+ */
+ public function onValidationSuccess()
+ {
+ $this->raiseEvent('OnValidationSuccess',$this,null);
+ }
+
+ /**
+ * This event is raised when the validator fails in validation.
+ */
+ public function onValidationError()
+ {
+ $this->raiseEvent('OnValidationError',$this,null);
+ }
+
+ /**
+ * This event is raised right before the validator starts to perform validation.
+ * You may use this event to change the behavior of validation.
+ * For example, you may disable the validator if certain condition is satisfied.
+ * Note, the event will NOT be raised if the validator is invisible.
+ */
+ public function onValidate()
+ {
+ $this->raiseEvent('OnValidate',$this,null);
+ }
+
+ /**
+ * Renders the validator control.
+ * @param THtmlWriter writer for the rendering purpose
+ */
+ public function renderContents($writer)
+ {
+ if(($text=$this->getText())!=='')
+ $writer->write($text);
+ else if(($text=$this->getErrorMessage())!=='')
+ $writer->write($text);
+ else
+ parent::renderContents($writer);
+ }
+}
+
+/**
+ * TValidatorClientSide class.
+ *
+ * Client-side validator events can be modified through the {@link
+ * TBaseValidator::getClientSide ClientSide} property of a validator. The
+ * subproperties of ClientSide are those of the TValidatorClientSide
+ * properties. The client-side validator supports the following events.
+ *
+ * The <tt>OnValidate</tt> event is raise before the validator validation
+ * functions are called.
+ *
+ * The <tt>OnValidationSuccess</tt> event is raised after the validator has successfully
+ * validate the control.
+ *
+ * The <tt>OnValidationError</tt> event is raised after the validator fails validation.
+ *
+ * See the quickstart documentation for further details.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Web.UI.WebControls
+ * @since 3.0
+ */
+class TValidatorClientSide extends TClientSideOptions
+{
+ /**
+ * @return string javascript code for client-side OnValidate event.
+ */
+ public function getOnValidate()
+ {
+ return $this->getOption('OnValidate');
+ }
+
+ /**
+ * Client-side OnValidate validator event is raise before the validators
+ * validation functions are called.
+ * @param string javascript code for client-side OnValidate event.
+ */
+ public function setOnValidate($javascript)
+ {
+ $this->setFunction('OnValidate', $javascript);
+ }
+
+ /**
+ * Client-side OnSuccess event is raise after validation is successfull.
+ * This will override the default client-side validator behaviour.
+ * @param string javascript code for client-side OnSuccess event.
+ */
+ public function setOnValidationSuccess($javascript)
+ {
+ $this->setFunction('OnValidationSuccess', $javascript);
+ }
+
+ /**
+ * @return string javascript code for client-side OnSuccess event.
+ */
+ public function getOnValidationSuccess()
+ {
+ return $this->getOption('OnValidationSuccess');
+ }
+
+ /**
+ * Client-side OnError event is raised after validation failure.
+ * This will override the default client-side validator behaviour.
+ * @param string javascript code for client-side OnError event.
+ */
+ public function setOnValidationError($javascript)
+ {
+ $this->setFunction('OnValidationError', $javascript);
+ }
+
+ /**
+ * @return string javascript code for client-side OnError event.
+ */
+ public function getOnValidationError()
+ {
+ return $this->getOption('OnValidationError');
+ }
+
+ /**
+ * @param boolean true to revalidate when the control to validate changes value.
+ */
+ public function setObserveChanges($value)
+ {
+ $this->setOption('ObserveChanges', TPropertyValue::ensureBoolean($value));
+ }
+
+ /**
+ * @return boolean true to observe changes.
+ */
+ public function getObserveChanges()
+ {
+ $changes = $this->getOption('ObserveChanges');
+ return ($changes===null) ? true : $changes;
+ }
+}
+
+
+/**
+ * TValidatorDisplayStyle class.
+ * TValidatorDisplayStyle defines the enumerable type for the possible styles
+ * that a validator control can display the error message.
+ *
+ * The following enumerable values are defined:
+ * - None: the error message is not displayed
+ * - Dynamic: the error message dynamically appears when the validator fails validation
+ * - Fixed: Similar to Dynamic except that the error message physically occupies the page layout (even though it may not be visible)
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id$
+ * @package System.Web.UI.WebControls
+ * @since 3.0.4
+ */
+class TValidatorDisplayStyle extends TEnumerable
+{
+ const None='None';
+ const Dynamic='Dynamic';
+ const Fixed='Fixed';
+}
+
+/**
+ * TValidationDataType class.
+ * TValidationDataType defines the enumerable type for the possible data types that
+ * a comparison validator can validate upon.
+ *
+ * The following enumerable values are defined:
+ * - Integer
+ * - Float
+ * - Date
+ * - String
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id$
+ * @package System.Web.UI.WebControls
+ * @since 3.0.4
+ */
+class TValidationDataType extends TEnumerable
+{
+ const Integer='Integer';
+ const Float='Float';
+ const Date='Date';
+ const String='String';
+}
+
diff --git a/framework/Web/UI/WebControls/TColorPicker.php b/framework/Web/UI/WebControls/TColorPicker.php index 67a4ccfe..0fc7eef4 100644 --- a/framework/Web/UI/WebControls/TColorPicker.php +++ b/framework/Web/UI/WebControls/TColorPicker.php @@ -112,7 +112,7 @@ class TColorPicker extends TTextBox */
public function getClientSide()
{
- if(is_null($this->_clientSide))
+ if($this->_clientSide===null)
$this->_clientSide = $this->createClientSide();
return $this->_clientSide;
}
diff --git a/framework/Web/UI/WebControls/TCustomValidator.php b/framework/Web/UI/WebControls/TCustomValidator.php index 0b82ac5c..7fed2b84 100644 --- a/framework/Web/UI/WebControls/TCustomValidator.php +++ b/framework/Web/UI/WebControls/TCustomValidator.php @@ -112,11 +112,11 @@ class TCustomValidator extends TBaseValidator else
return $param->getIsValid();
}
-
+
/**
* @return TControl control to be validated. Null if no control is found.
*/
- protected function getValidationTarget()
+ public function getValidationTarget()
{
if(($id=$this->getControlToValidate())!=='' && ($control=$this->findControl($id))!==null)
return $control;
diff --git a/framework/Web/UI/WebControls/TDatePicker.php b/framework/Web/UI/WebControls/TDatePicker.php index 770099eb..e678b046 100644 --- a/framework/Web/UI/WebControls/TDatePicker.php +++ b/framework/Web/UI/WebControls/TDatePicker.php @@ -366,7 +366,7 @@ class TDatePicker extends TTextBox */
public function getClientSide()
{
- if(is_null($this->_clientScript))
+ if($this->_clientScript===null)
$this->_clientScript = $this->createClientScript();
return $this->_clientScript;
}
@@ -529,7 +529,7 @@ class TDatePicker extends TTextBox $options['PositionMode'] = $this->getPositionMode();
$options = array_merge($options, $this->getCulturalOptions());
- if(!is_null($this->_clientScript))
+ if($this->_clientScript!==null)
$options = array_merge($options,
$this->_clientScript->getOptions()->toArray());
return $options;
@@ -610,7 +610,7 @@ class TDatePicker extends TTextBox {
$formatter = Prado::createComponent('System.Util.TSimpleDateFormatter',
$this->getDateFormat());
- return !is_null($formatter->getDayPattern());
+ return ($formatter->getDayPattern()!==null);
}
/**
diff --git a/framework/Web/UI/WebControls/THead.php b/framework/Web/UI/WebControls/THead.php index 62ee46d3..36b81648 100644 --- a/framework/Web/UI/WebControls/THead.php +++ b/framework/Web/UI/WebControls/THead.php @@ -29,13 +29,6 @@ * <com:TMetaTag Name="keywords" Content="Prado" />
* </com:THead>
* </code>
- *
- * A MetaTag for "generator" is added by default if you haven't specified your own generator meta tag. You can override
- * the property by adding the following code to your template:
- * <com:THead>
- * <com:TMetaTag Name="generator" Content="Custom name" />
- * </com:THead>
- * </code>
*
* Note, {@link TPage} has a property {@link TPage::getHead Head} that refers to
* the THead control currently on the page. A page can have at most once THead
@@ -162,24 +155,14 @@ class THead extends TControl if(($icon=$this->getShortcutIcon())!=='')
$writer->write('<link rel="shortcut icon" href="'.$icon."\" />\n");
- $generatorAdded = false;
if(($metaTags=$this->getMetaTags())!==null)
{
foreach($metaTags as $metaTag)
{
- if(strtolower($metaTag->getName()) == 'generator')
- $generatorAdded = true;
$metaTag->render($writer);
$writer->writeLine();
}
}
- if(!$generatorAdded)
- {
- $metaTag = new TMetaTag();
- $metaTag->setName('generator');
- $metaTag->setContent(Prado::metaGenerator());
- $metaTag->render($writer);
- }
$cs=$page->getClientScript();
$cs->renderStyleSheetFiles($writer);
$cs->renderStyleSheets($writer);
diff --git a/framework/Web/UI/WebControls/THtmlArea.php b/framework/Web/UI/WebControls/THtmlArea.php index 7fea5862..8d55c074 100644 --- a/framework/Web/UI/WebControls/THtmlArea.php +++ b/framework/Web/UI/WebControls/THtmlArea.php @@ -4,7 +4,7 @@ *
* @author Wei Zhuo <weizhuo[at]gmail[dot]com>
* @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2008 PradoSoft + * @copyright Copyright © 2005-2008 PradoSoft
* @license http://www.pradosoft.com/license/
* @version $Id$
* @package System.Web.UI
@@ -334,7 +334,7 @@ class THtmlArea extends TTextBox $options['languages'] = $this->getLanguageSuffix($this->getCulture());
$options['disk_cache'] = true;
$options['debug'] = false;
- $js = TJavaScript::encode($options);
+ $js = TJavaScript::encode($options,true,true);
$script = "if(typeof(tinyMCE_GZ)!='undefined'){ tinyMCE_GZ.init({$js}); }";
$scripts->registerBeginScript($key, $script);
}
@@ -353,7 +353,7 @@ class THtmlArea extends TTextBox protected function registerEditorClientScript($writer)
{
$scripts = $this->getPage()->getClientScript();
- $options = TJavaScript::encode($this->getEditorOptions());
+ $options = TJavaScript::encode($this->getEditorOptions(),true,true); // Force encoding of empty strings
$script = "if(typeof(tinyMCE)!='undefined'){ tinyMCE.init($options); }";
$scripts->registerEndScript('prado:THtmlArea'.$this->ClientID,$script);
}
@@ -436,7 +436,14 @@ class THtmlArea extends TTextBox $option = explode(":",$bits,2);
if(count($option) == 2)
- $options[trim($option[0])] = trim(preg_replace('/\'|"/','', $option[1]));
+ {
+ $value=trim(preg_replace('/\'|"/','', $option[1]));
+ if (($s=strtolower($value))==='false')
+ $value=false;
+ elseif ($s==='true')
+ $value=true;
+ $options[trim($option[0])] = $value;
+ }
}
return $options;
}
@@ -447,10 +454,10 @@ class THtmlArea extends TTextBox protected function getLanguageSuffix($culture)
{
$app = $this->getApplication()->getGlobalization();
- if(empty($culture) && !is_null($app))
+ if(empty($culture) && ($app!==null))
$culture = $app->getCulture();
$variants = array();
- if(!is_null($app))
+ if($app!==null)
$variants = $app->getCultureVariants($culture);
foreach($variants as $variant)
diff --git a/framework/Web/UI/WebControls/TListControl.php b/framework/Web/UI/WebControls/TListControl.php index 84c23f88..c69b387e 100644 --- a/framework/Web/UI/WebControls/TListControl.php +++ b/framework/Web/UI/WebControls/TListControl.php @@ -487,8 +487,6 @@ abstract class TListControl extends TDataBoundControl implements IDataRenderer $this->clearSelection();
if($index>=0 && $index<$this->_items->getCount())
$this->_items->itemAt($index)->setSelected(true);
- else if($index!==-1)
- throw new TInvalidDataValueException('listcontrol_selectedindex_invalid',get_class($this),$index);
}
$this->_cachedSelectedIndex=$index;
if($this->getAdapter() instanceof IListControlAdapter)
@@ -601,7 +599,7 @@ abstract class TListControl extends TDataBoundControl implements IDataRenderer $item->setSelected(true);
}
else
- throw new TInvalidDataValueException('listcontrol_selectedvalue_invalid',get_class($this),$value);
+ $this->clearSelection();
}
$this->_cachedSelectedValue=$value;
if($this->getAdapter() instanceof IListControlAdapter)
@@ -643,8 +641,6 @@ abstract class TListControl extends TDataBoundControl implements IDataRenderer {
if(isset($lookup["$value"]))
$lookup["$value"]->setSelected(true);
- else
- throw new TInvalidDataValueException('listcontrol_selectedvalue_invalid',get_class($this),$value);
}
}
$this->_cachedSelectedValues=$values;
@@ -917,7 +913,7 @@ class TListItemCollection extends TList protected function createNewListItem($text=null)
{
$item = new TListItem;
- if(!is_null($text))
+ if($text!==null)
$item->setText($text);
return $item;
}
diff --git a/framework/Web/UI/WebControls/TStyle.php b/framework/Web/UI/WebControls/TStyle.php index 7cb1d8c8..1b94cc8e 100644 --- a/framework/Web/UI/WebControls/TStyle.php +++ b/framework/Web/UI/WebControls/TStyle.php @@ -4,7 +4,7 @@ *
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2008 PradoSoft + * @copyright Copyright © 2005-2008 PradoSoft
* @license http://www.pradosoft.com/license/
* @version $Id$
* @package System.Web.UI.WebControls
@@ -63,7 +63,7 @@ class TStyle extends TComponent */
public function __clone()
{
- if(!is_null($this->_font))
+ if($this->_font!==null)
$this->_font = clone($this->_font);
}
@@ -157,7 +157,7 @@ class TStyle extends TComponent */
public function hasCssClass()
{
- return !is_null($this->_class);
+ return ($this->_class!==null);
}
/**
diff --git a/framework/Web/UI/WebControls/TValidationSummary.php b/framework/Web/UI/WebControls/TValidationSummary.php index 95679e15..ab066f78 100644 --- a/framework/Web/UI/WebControls/TValidationSummary.php +++ b/framework/Web/UI/WebControls/TValidationSummary.php @@ -4,7 +4,7 @@ *
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2008 PradoSoft + * @copyright Copyright © 2005-2008 PradoSoft
* @license http://www.pradosoft.com/license/
* @version $Id$
* @package System.Web.UI.WebControls
@@ -261,7 +261,7 @@ class TValidationSummary extends TWebControl $options['ValidationGroup'] = $this->getValidationGroup();
$options['Display'] = $this->getDisplay();
- if(!is_null($this->_clientSide))
+ if($this->_clientSide!==null)
$options = array_merge($options,$this->_clientSide->getOptions()->toArray());
return $options;
@@ -273,7 +273,7 @@ class TValidationSummary extends TWebControl */
public function getClientSide()
{
- if(is_null($this->_clientSide))
+ if($this->_clientSide===null)
$this->_clientSide = $this->createClientScript();
return $this->_clientSide;
}
@@ -324,6 +324,9 @@ class TValidationSummary extends TWebControl case TValidationSummaryDisplayMode::BulletList:
$this->renderBulletList($writer);
break;
+ case TValidationSummaryDisplayMode::HeaderOnly:
+ $this->renderHeaderOnly($writer);
+ break;
}
}
}
@@ -382,6 +385,15 @@ class TValidationSummary extends TWebControl }
$writer->write($content);
}
+
+ /**
+ * Render the validation summary header text only.
+ * @param THtmlWriter the writer used for the rendering purpose
+ */
+ protected function renderHeaderOnly($writer)
+ {
+ $writer->write($this->getHeaderText());
+ }
}
/**
@@ -468,6 +480,7 @@ class TClientSideValidationSummaryOptions extends TClientSideOptions * - SimpleList: the error messages are displayed as a list without any decorations.
* - SingleParagraph: the error messages are concatenated together into a paragraph.
* - BulletList: the error messages are displayed as a bulleted list.
+ * - HeaderOnly: only the HeaderText will be display.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @version $Id$
@@ -479,6 +492,7 @@ class TValidationSummaryDisplayMode extends TEnumerable const SimpleList='SimpleList';
const SingleParagraph='SingleParagraph';
const BulletList='BulletList';
+ const HeaderOnly='HeaderOnly';
}
diff --git a/framework/Xml/TXmlDocument.php b/framework/Xml/TXmlDocument.php index d7ddf187..7d506852 100644 --- a/framework/Xml/TXmlDocument.php +++ b/framework/Xml/TXmlDocument.php @@ -396,8 +396,27 @@ class TXmlDocument extends TXmlElement $attributes=$this->getAttributes(); $elements->clear(); $attributes->clear(); + + static $bSimpleXml; + if($bSimpleXml === null) + $bSimpleXml = (boolean)function_exists('simplexml_load_string'); + + if($bSimpleXml) + { + $simpleDoc = simplexml_load_string($string); + $docNamespaces = $simpleDoc->getDocNamespaces(false); + $simpleDoc = null; + foreach($docNamespaces as $prefix => $uri) + { + if($prefix === '') + $attributes->add('xmlns', $uri); + else + $attributes->add('xmlns:'.$prefix, $uri); + } + } + foreach($element->attributes as $name=>$attr) - $attributes->add($name,$attr->value); + $attributes->add(($attr->prefix === '' ? '' : $attr->prefix . ':') .$name,$attr->value); foreach($element->childNodes as $child) { if($child instanceof DOMElement) @@ -464,7 +483,8 @@ class TXmlDocument extends TXmlElement $element=new TXmlElement($node->tagName); $element->setValue($node->nodeValue); foreach($node->attributes as $name=>$attr) - $element->getAttributes()->add($name,$attr->value); + $element->getAttributes()->add(($attr->prefix === '' ? '' : $attr->prefix . ':') . $name,$attr->value); + foreach($node->childNodes as $child) { if($child instanceof DOMElement) diff --git a/framework/prado-cli b/framework/prado-cli new file mode 100755 index 00000000..0d504299 --- /dev/null +++ b/framework/prado-cli @@ -0,0 +1,4 @@ +#!/usr/bin/env php +<?php +//execute the command line tool +include(dirname(__FILE__).'/prado-cli.php'); diff --git a/framework/prado-cli.bat b/framework/prado-cli.bat new file mode 100644 index 00000000..38d02850 --- /dev/null +++ b/framework/prado-cli.bat @@ -0,0 +1,39 @@ +@echo off + +rem ************************************************************* +rem ** CLI for Windows based systems (based on phing.bat) +rem ************************************************************* + +rem This script will do the following: +rem - check for PHP_COMMAND env, if found, use it. +rem - if not found detect php, if found use it, otherwise err and terminate + +if "%OS%"=="Windows_NT" @setlocal + +rem %~dp0 is expanded pathname of the current script under NT +set PRADO_DIR=%~dp0 + +goto init + +:init + +if "%PHP_COMMAND%" == "" goto no_phpcommand + +IF EXIST ".\prado-cli.php" ( + %PHP_COMMAND% -d html_errors=off -d open_basedir= -q ".\prado-cli.php" %1 %2 %3 %4 %5 %6 %7 %8 %9 +) ELSE ( + %PHP_COMMAND% -d html_errors=off -d open_basedir= -q "%PRADO_DIR%\prado-cli.php" %1 %2 %3 %4 %5 %6 %7 %8 %9 +) +goto cleanup + +:no_phpcommand +rem echo ------------------------------------------------------------------------ +rem echo WARNING: Set environment var PHP_COMMAND to the location of your php.exe +rem echo executable (e.g. C:\PHP\php.exe). (assuming php.exe on PATH) +rem echo ------------------------------------------------------------------------ +set PHP_COMMAND=php.exe +goto init + +:cleanup +if "%OS%"=="Windows_NT" @endlocal +rem pause |