* Copyright 2001-2012 Strangecode, LLC * * This file is part of The Strangecode Codebase. * * The Strangecode Codebase is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your option) * any later version. * * The Strangecode Codebase is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * The Strangecode Codebase. If not, see . */ /** * JS.inc.php * * Dynamically outputs minified JS data. * * @author Quinn Comendant * @version 1.2 */ class JS { // Include these style sheets. protected $_js_files = array('default' => array()); // JS object parameters. protected $_params = array( 'character_set' => 'utf-8', 'cache_enable' => false, 'cache_js' => null, // Same as above; kept for backwards compatibility. 'cache_seconds' => 7776000, // 90 days. 'strip_whitespace' => false, 'output_compression' => false, ); /** * Set (or overwrite existing) parameters by passing an array of new parameters. * * @access public * @param array $params Array of parameters (key => val pairs). */ public function setParam($params) { $app =& App::getInstance(); if (isset($params) && is_array($params)) { // Merge new parameters with old overriding only those passed. $this->_params = array_merge($this->_params, $params); // Maintaining backwards compatibility. if (isset($params['cache_js']) && !isset($params['cache_enable'])) { $this->_params['cache_enable'] = $params['cache_js']; } } else { $app->logMsg(sprintf('Parameters are not an array: %s', $params), LOG_ERR, __FILE__, __LINE__); } } /** * Return the value of a parameter, if it exists. * * @access public * @param string $param Which parameter to return. * @return mixed Configured parameter value. */ public function getParam($param) { $app =& App::getInstance(); if (array_key_exists($param, $this->_params)) { return $this->_params[$param]; } else { $app->logMsg(sprintf('Parameter is not set: %s', $param), LOG_DEBUG, __FILE__, __LINE__); return null; } } /** * Add a file-path to the array of files to include as JS. * * @access public * @param string $file Include path to JS files. * @param mixed $realms Realm name string or array of realm names. * @return bool True on success, false on failure. */ public function setFile($file, $realms='') { $app =& App::getInstance(); if (!is_array($realms)) { $realms = array($realms); } if ($fp = fopen($file, 'r', true)) { foreach ($realms as $realm) { $realm = '' == $realm ? 'default' : $realm; $this->_js_files[$realm][] = $file; } fclose($fp); return true; } else { $app->logMsg(sprintf('JS file non-existent: %s', $file), LOG_ERR, __FILE__, __LINE__); return false; } } /** * Output headers for JS. * * @access public * * @return bool False if no files have been set. */ public function headers($realm='') { $app =& App::getInstance(); $realm = '' == $realm ? 'default' : $realm; if (empty($this->_js_files[$realm])) { $app->logMsg(sprintf('JS::headers called without specifying any files.', null), LOG_WARNING, __FILE__, __LINE__); return false; } // Get time of latest modified file, including this class file. $files_mtime = array(); foreach (array_merge($this->_js_files[$realm], array(__FILE__)) as $file) { $files_mtime[] = statIncludePath($file, 'mtime'); } sort($files_mtime, SORT_NUMERIC); $latest_mtime = array_pop($files_mtime); if ($this->getParam('output_compression') && extension_loaded('zlib')) { ob_start('ob_gzhandler'); } header(sprintf('Content-Type: application/javascript; charset=%s', $this->getParam('character_set'))); header(sprintf('Last-Modified: %s GMT', gmdate('D, d M Y H:i:s', $latest_mtime), null)); if ($this->getParam('cache_enable')) { header(sprintf('Cache-Control: private, max-age=%s', $this->getParam('cache_seconds'))); header(sprintf('Expires: %s GMT', gmdate('D, d M Y H:i:s', $latest_mtime + $this->getParam('cache_seconds')))); header('Pragma: private'); header('Vary: Accept-Encoding'); } else { // Disallow HTTP caching entirely. http://stackoverflow.com/a/2068407 header('Cache-Control: no-cache, no-store, must-revalidate'); // HTTP 1.1. header('Pragma: no-cache'); // HTTP 1.0. header('Expires: 0'); // Proxies. } } /** * Include JS files specified by setFile(). * * @access public * * @return bool False if no files have been set. */ public function output($realm='') { $app =& App::getInstance(); $realm = '' == $realm ? 'default' : $realm; if (empty($this->_js_files[$realm])) { $app->logMsg(sprintf('JS::output called without specifying any files.', null), LOG_WARNING, __FILE__, __LINE__); return false; } foreach ($this->_js_files[$realm] as $file) { if ($this->getParam('strip_whitespace')) { // Strip whitespace and print file. echo preg_replace( array('/(?<=^|;|{)\s*\/\/.*$/m' . $app->getParam('preg_u'), '/(?<=^|;|{)\s*\/\*.*?\*\//ms' . $app->getParam('preg_u'), '/[\n\r]+/' . $app->getParam('preg_u'), '/[ \t]+}[ \t]+/' . $app->getParam('preg_u'), '/[ \t]+{[ \t]+/' . $app->getParam('preg_u'), '/\s+=\s+/' . $app->getParam('preg_u'), '/^[ \t]+/m' . $app->getParam('preg_u'), '/[ \t]+$/m' . $app->getParam('preg_u')), array('', '', "\n", '}', '{', '=', '', ''), file_get_contents($file, true) ); } else { // Include file as is. include $file; } // Make sure there's at least one new-line between files. echo "\n"; } if ($this->getParam('output_compression') && extension_loaded('zlib')) { ob_end_flush(); } } }