* 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();
}
}
}