* @link http://www.yiiframework.com/ * @copyright Copyright © 2008 Yii Software LLC * @license http://www.yiiframework.com/license/ */ /** * CClientScript manages JavaScript and CSS stylesheets for views. * * @author Qiang Xue * @version $Id$ * @package system.web * @since 1.0 */ class CClientScript extends CComponent { /** * Name of the hidden field storing persistent page states. */ const STATE_INPUT_NAME='YII_PAGE_STATE'; /** * The HTML code for the hidden field storing persistent page states. */ const STATE_INPUT_FIELD=''; /** * @var boolean whether JavaScript should be enabled. Defaults to true. */ public $enableJavaScript=true; private $_controller; private $_packages; private $_dependencies; private $_baseUrl; private $_coreScripts=array(); private $_cssFiles=array(); private $_css=array(); private $_scriptFiles=array(); private $_scripts=array(); private $_bodyScriptFiles=array(); private $_bodyScripts=array(); private $_pageStates; /** * Constructor. * @param CController controller */ public function __construct($controller) { $this->_controller=$controller; } /** * Cleans all registered scripts. */ public function reset() { $this->_coreScripts=array(); $this->_cssFiles=array(); $this->_css=array(); $this->_scriptFiles=array(); $this->_scripts=array(); $this->_bodyScriptFiles=array(); $this->_bodyScripts=array(); $this->_controller->recordCachingAction('clientScript','reset',array()); } /** * Renders the registered scripts. * This method is called in {@link CController::render} when it finishes * rendering content. CClientScript thus gets a chance to insert script tags * at head and body sections in the HTML output. * @param string the existing output that needs to be inserted with script tags * @return string the modified output */ public function render($output) { $html=''; $html2=''; foreach($this->_cssFiles as $url=>$media) $html.=CHtml::cssFile($url,$media)."\n"; foreach($this->_css as $css) $html.=CHtml::cssFile($css[0],$css[1])."\n"; if($this->enableJavaScript) { foreach($this->_coreScripts as $name) { if(is_string($name)) $html.=$this->renderCoreScript($name); } foreach($this->_scriptFiles as $scriptFile) $html.=CHtml::scriptFile($scriptFile)."\n"; if(!empty($this->_scripts)) $html.=CHtml::script(implode("\n",$this->_scripts))."\n"; foreach($this->_bodyScriptFiles as $scriptFile) $html2.=CHtml::scriptFile($scriptFile)."\n"; if(!empty($this->_bodyScripts)) $html2.=CHtml::script(implode("\n",$this->_bodyScripts))."\n"; } if(($states=$this->savePageStates())!=='') { $input=''; $output=str_replace(self::STATE_INPUT_FIELD,$input,$output); } if($html!=='') $output=preg_replace('/(.*?)(<\\/head\s*>)/is','$1'.$html.'$2',$output,1); if($html2!=='') { $output=preg_replace('/(<\\/body\s*>.*?<\/html\s*>)/is',$html2.'$1',$output,1,$count); if(!$count) $output.=$html2; } return $output; } /** * @return string the base URL of all core javascript files */ public function getCoreScriptUrl() { if($this->_baseUrl!==null) return $this->_baseUrl; else return $this->_baseUrl=Yii::app()->getAssetManager()->publish(YII_PATH.'/web/js/source'); } /** * Renders the specified core javascript library. * Any dependent libraries will also be rendered. * @param string name of core javascript library. See framework/web/js/packages.php * for valid names. * @return string the rendering result */ public function renderCoreScript($name) { if(isset($this->_coreScripts[$name]) && $this->_coreScripts[$name]===true || !$this->enableJavaScript) return ''; $this->_coreScripts[$name]=true; if($this->_packages===null) { $config=require(YII_PATH.'/web/js/packages.php'); $this->_packages=$config[0]; $this->_dependencies=$config[1]; } $baseUrl=$this->getCoreScriptUrl(); $html=''; if(isset($this->_dependencies[$name])) { foreach($this->_dependencies[$name] as $depName) $html.=$this->renderCoreScript($depName); } if(isset($this->_packages[$name])) { foreach($this->_packages[$name] as $path) { if(substr($path,-4)==='.css') $html.=CHtml::cssFile($baseUrl.'/'.$path)."\n"; else $html.=CHtml::scriptFile($baseUrl.'/'.$path)."\n"; } } return $html; } /** * Registers a core javascript library. * @param string the core javascript library name * @see renderCoreScript */ public function registerCoreScript($name) { if(!isset($this->_coreScripts[$name])) { $this->_coreScripts[$name]=$name; $params=func_get_args(); $this->_controller->recordCachingAction('clientScript','registerCoreScript',$params); } } /** * Registers a CSS file * @param string ID that uniquely identifies this CSS file * @param string URL of the CSS file * @param string media that the CSS file should be applied to. If empty, it means all media types. */ public function registerCssFile($url,$media='') { $this->_cssFiles[$url]=$media; $params=func_get_args(); $this->_controller->recordCachingAction('clientScript','registerCssFile',$params); } /** * Registers a piece of CSS code. * @param string ID that uniquely identifies this piece of CSS code * @param string the CSS code * @param string media that the CSS code should be applied to. If empty, it means all media types. */ public function registerCss($id,$css,$media='') { $this->_css[$id]=array($css,$media); $params=func_get_args(); $this->_controller->recordCachingAction('clientScript','registerCss',$params); } /** * Registers a javascript file that should be inserted in the head section * @param string URL of the javascript file */ public function registerScriptFile($url) { if(!isset($this->_scriptFiles[$url])) { $this->_scriptFiles[$url]=$url; $params=func_get_args(); $this->_controller->recordCachingAction('clientScript','registerScriptFile',$params); } } /** * Registers a piece of javascript code that should be inserted in the head section * @param string ID that uniquely identifies this piece of JavaScript code * @param string the javascript code */ public function registerScript($id,$script) { $this->_scripts[$id]=$script; $params=func_get_args(); $this->_controller->recordCachingAction('clientScript','registerScript',$params); } /** * Registers a javascript file that should be inserted in the body section * @param string URL of the javascript file */ public function registerBodyScriptFile($url) { if(!isset($this->_bodyScriptFiles[$url])) { $this->_bodyScriptFiles[$url]=$url; $params=func_get_args(); $this->_controller->recordCachingAction('clientScript','registerBodyScriptFile',$params); } } /** * Registers a piece of javascript code that should be inserted in the body section * @param string ID that uniquely identifies this piece of JavaScript code * @param string the javascript code */ public function registerBodyScript($id,$script) { $this->_bodyScripts[$id]=$script; $params=func_get_args(); $this->_controller->recordCachingAction('clientScript','registerBodyScript',$params); } /** * Checks whether the CSS file has been registered. * @param string URL of the CSS file * @return boolean whether the CSS file is already registered */ public function isCssFileRegistered($url) { return isset($this->_cssFiles[$url]); } /** * Checks whether the CSS code has been registered. * @param string ID that uniquely identifies the CSS code * @return boolean whether the CSS code is already registered */ public function isCssRegistered($id) { return isset($this->_css[$id]); } /** * Checks whether the CSS code has been registered in the head section. * @param string URL of the javascript file * @return boolean whether the javascript file is already registered */ public function isScriptFileRegistered($url) { return isset($this->_scriptFiles[$url]); } /** * Checks whether the JavaScript code has been registered in the head section. * @param string ID that uniquely identifies the JavaScript code * @return boolean whether the javascript code is already registered */ public function isScriptRegistered($id) { return isset($this->_scripts[$id]); } /** * Checks whether the JavaScript file has been registered in the body section. * @param string URL of the javascript file * @return boolean whether the javascript file is already registered */ public function isBodyScriptFileRegistered($url) { return isset($this->_bodyScriptFiles[$url]); } /** * Checks whether the JavaScript code has been registered in the body section. * @param string ID that uniquely identifies the JavaScript code * @return boolean whether the javascript code is already registered */ public function isBodyScriptRegistered($id) { return isset($this->_bodyScripts[$id]); } /** * Returns a persistent page state value. * @param string the state name * @param mixed the value to be returned if the named state is not found * @return mixed the page state value * @see setPageState */ public function getPageState($name,$defaultValue=null) { if($this->_pageStates===null) $this->loadPageStates(); return isset($this->_pageStates[$name])?$this->_pageStates[$name]:$defaultValue; } /** * Saves a persistent page state value. * A page state value will remain accessible when the page is submitted * via a post action. * @param string the state name * @param mixed the page state value * @param mixed the default page state value. If this is the same as * the given value, the state will be removed from persistent storage. * @see getPageState */ public function setPageState($name,$value,$defaultValue=null) { if($this->_pageStates===null) $this->loadPageStates(); if($value===$defaultValue) unset($this->_pageStates[$name]); else $this->_pageStates[$name]=$value; $params=func_get_args(); $this->_controller->recordCachingAction('clientScript','setPageState',$params); } /** * Loads page states from a hidden input. */ protected function loadPageStates() { if(isset($_POST[self::STATE_INPUT_NAME]) && !empty($_POST[self::STATE_INPUT_NAME])) { if(($data=base64_decode($_POST[self::STATE_INPUT_NAME]))!==false) { if(extension_loaded('zlib')) $data=@gzuncompress($data); if(($data=Yii::app()->getSecurityManager()->validateData($data))!==false) { $this->_pageStates=unserialize($data); return; } } } $this->_pageStates=array(); } /** * Saves page states as a base64 string. */ protected function savePageStates() { if($this->_pageStates===null) $this->loadPageStates(); if(empty($this->_pageStates)) return ''; else { $data=Yii::app()->getSecurityManager()->hashData(serialize($this->_pageStates)); if(extension_loaded('zlib')) $data=gzcompress($data); return base64_encode($data); } } }