/ * @todo absolutize @import statements */ class P4Cms_View_Helper_HeadScript extends Zend_View_Helper_HeadScript { protected $_aggregate = false; protected $_aggregated = false; protected $_assetHandler = null; protected $_documentRoot = null; /** * Enable or disable JS aggregation. * * @param bool $aggregate set to true to enable, false to disable. * @return P4Cms_View_Helper_HeadScript provides fluent interface. */ public function setAggregateJs($aggregate) { $this->_aggregate = (bool) $aggregate; return $this; } /** * Set the file-system path to the document root. * * @param string $path the location of the public folder. * @return P4Cms_View_Helper_HeadScript provides fluent interface. */ public function setDocumentRoot($path) { $this->_documentRoot = rtrim($path, '/\\'); return $this; } /** * Get the file-system path to the document root. * * @return string the location of the public folder. */ public function getDocumentRoot() { return $this->_documentRoot; } /** * Set the asset handler used to store the aggregated JS file(s). * * @param P4Cms_AssetHandlerInterface|null $handler The handler to use or null * @return P4Cms_View_Helper_HeadScript provides fluent interface. */ public function setAssetHandler(P4Cms_AssetHandlerInterface $handler = null) { $this->_assetHandler = $handler; return $this; } /** * Get the asset handler used to store the aggregated JS file(s). * * @return P4Cms_AssetHandlerInterface|null the handler to use or null */ public function getAssetHandler() { return $this->_assetHandler; } /** * Extend toString to aggregate JS files where possible. * * @param string|int $indent Zend provides no documentation for this param. * @return string */ public function toString($indent = null) { // aggregate JS files (but only once). if ($this->_aggregate && !$this->_aggregated) { $this->_aggregateJs(); } return parent::toString(); } /** * Aggregate local unconditional JS files. * Rebuilds whenever a file changes. */ protected function _aggregateJs() { // bail out if no asset handler is configured if (!$this->getAssetHandler()) { P4Cms_Log::log( "Failed to aggregate JS. Asset Handler is unset.", P4Cms_Log::ERR ); return; } // bail out if document root is unset. if (!$this->getDocumentRoot()) { P4Cms_Log::log( "Failed to aggregate JS. Document root is unset.", P4Cms_Log::ERR ); return; } // identify JS we can aggregate. $time = 0; $files = array(); $ignore = array(); $this->getContainer()->ksort(); foreach ($this as $item) { if (!$this->_isValid($item)) { continue; } // normalize the attributes. $attributes = $item->attributes + array( 'src' => null, 'conditional' => null, 'charset' => null, 'defer' => null ); // only aggregate scripts that: // - are type text/javascript // - have a 'src' attribute // - are local // - are not conditional // - have no explicit charset // - are not deferred // - exist in the document root $file = $this->getDocumentRoot() . $attributes['src']; if (($item->type !== 'text/javascript' || !$attributes['src'] || P4Cms_Uri::hasScheme($attributes['src'])) || $attributes['conditional'] || $attributes['charset'] || $attributes['defer'] || !file_exists($file) ) { $ignore[] = $item; continue; } $files[] = $file; $time = max($time, filemtime($file)); } // nothing to do if there are no files to aggregate. if (!$files) { return; } // determine if compression should be enabled $compressed = $this->_canGzipCompress() && $this->_clientAcceptsGzip(); // generate build filename. $buildFile = md5(implode(',', $files) . $time) . ($compressed ? '.jsgz' : '.js'); // rebuild if file does not exist. if (!$this->getAssetHandler()->exists($buildFile)) { $js = ""; foreach ($files as $file) { $js .= file_get_contents($file) . "\n"; } // also compress if possible. if ($compressed) { $js = gzencode($js, 9); } // write out aggregate file - if it fails, abort aggregation. if (!$this->getAssetHandler()->put($buildFile, $js)) { return; } } // if we made it this far aggregation worked; update the // list to only contain ignored plus the aggregated scripts. $this->getContainer()->exchangeArray($ignore); $this->appendFile($this->getAssetHandler()->uri($buildFile)); $this->_aggregated = true; } /** * Check if this PHP can generate gzip compressed data. * * @return bool true if this PHP has gzip support. */ protected function _canGzipCompress() { return function_exists('gzencode'); } /** * Check if the client can accept gzip encoded content. * * @return bool true if the client supports gzip; false otherwise. */ protected function _clientAcceptsGzip() { $front = Zend_Controller_Front::getInstance(); $request = $front->getRequest(); $accepts = isset($request) ? $request->getHeader('Accept-Encoding') : ''; return strpos($accepts, 'gzip') !== false; } }