* @copyright Copyright (c) 2008, Net Perspective, LLC * @license http://www.opensource.org/licenses/mit-license.php MIT License * * @package VanishingPoint * @subpackage Modules */ class VP_JS implements VP_Module { /** * Holds the object's version * * @var String */ private $version = ""; /** * Holds the objects' file name for output, modified by render() and combine() * * @var String */ private $file = ""; /** * Holds the objects' file name, not modified by render() and combine() * * @var String */ private $unaltered = ""; /** * Holds if the file is to be included in a combination file when possible * * @var boolean */ private $combine = ""; /** * Holds the object's mod (0 -7) * *
	* Mode     Version     Move     Pack
	* 
	* 0   	   No          No       No
	* 1   	   No          No       Yes
	* 2   	   Yes         No       No  
	* 3   	   Yes         No       Yes  
	* 4   	   No          Yes      No  
	* 5   	   No          Yes      Yes  
	* 6   	   Yes         Yes      No  
	* 7   	   Yes         Yes      Yes  
	* 
* @var integer */ private $mode = ""; /** * Holds in array form the options (pack, version, move) for the file * * @var Array */ private $options = array(); /** * The default HTTP path. */ public static $HTTPPath = "/scripts/"; /** * The HTTP path to prepend to the file name after the file has been moved. */ public static $HTTPPath_cache = "/Vanishing%20Point/scripts/VP_Cache/"; /** * The default path to prepend to the file name for writing to it on the server. */ public static $serverPath = "scripts/"; /** * The path to prepend to the file name for writing to it on the server if the file has to be moved. */ public static $serverPath_cache = "scripts/VP_Cache/"; /** * The default extension for files processed by VP_JS. */ const BASE_EXTENSION = ".js"; /** * The extension to be added to packed files. */ const PACKED_EXTENSION = ".pack.js"; /** * The extension to be added to combined files. */ const COMBINE_EXTENSION= ".comb.js"; /** * The option to be passed to the VP_JS constructor for packing. */ const PACK = 1; /** * The option to be passed to the VP_JS constructor for versioning. */ const VERSION = 2; /** * The option to be passed to the VP_JS constructor for moving. */ const MOVE = 4; /** * $file and $version are required for the contructor. $file should be a path that php can use to * access the file. $version is the current version of the javascript file. * * $mode is set to default to 7. The integer value of 7 corseponds to a file that is packed and minified, * moved to the cache directory, and has a version appeneded to it. * While $mode can be set by an integer 0 through 7, VP_JS provides class constants that allow for more * readable control of these modes. By using a bitwise OR mode can be combined. * * $combine takes a boolean for if the file should be included in the combination file for that group. In order to be * combined a file must have both $combine set to true and a $mode of 7. Combination of files is where VP_JS does the * majority of the optimazation and should be used wherever possible. * * * * * * @param string $file * @param string $version * @param int $mode * @param boolean $combine */ public function __construct($file, $version, $mode = 7, $combine = true) { $this->file = $this->unaltered = $file; $this->version = $version; $this->combine = $combine; $this->mode = $mode; //convert mode into binary, reverse it, pad it up to 3 places (needed for numbers less than 4), re-reverse it(set it straight) //then distribute into the options array $modeBin = strrev(str_pad(strrev(decbin($mode)),3,0)); $this->options['pack'] = $modeBin[2]; $this->options['version'] = $modeBin[1]; $this->options['move'] = $modeBin[0]; } /** * Returns the unaltered file name. */ public function getFileName() { return $this->unaltered; } public function setServerPath($serverPath, $serverPath_cache="") { self::$serverPath = $serverPath; if($serverPath_cache == "") self::$serverPath_cache = $serverPath . "/VP_Cache"; else self::$serverPath_cache = $serverPath_cache; } public function setHTTPPath($HTTPPath, $HTTPPath_cache="") { self::$HTTPPath = $HTTPPath; if($HTTPPath_cache == "") self::$HTTPPath_cache = $HTTPPath . "/VP_Cache"; else self::$HTTPPath_cache = $HTTPPath_cache; } /** * Returns the ' . "\n"; } /** * Returns the ' . "\n"; } /** * combine() takes a group of files and makes a single file out of them to optimize HTTP Request times. * * $files is the array of VP_JS objects passed into combine() to be combined into a single file. This is * handled completely by the Vanishing Point framework. * * &$rendered is a pointer to the array of already rendered files. combine() adds the unaltered file names to this * when it adds a file to the combination file so as to keep it from being repeated as a packed or regular javascript * file. * * @param Array $files an array of VP_JS objects * @param Array &$rendered an array of strings passed by reference */ public static function combine(Array $files, Array &$rendered ) { $path = ""; $names = array(); $tmp = ""; //iterate through all the files foreach($files as $file) { //if it can be combined if($file->combine && $file->mode == 7 && !in_array($file->file, $rendered)) { //get the name $names[] = basename($file->file, self::BASE_EXTENSION) . $file->version; //build a regex for cleaining purposes $regexes[] = preg_replace("/\./","\.", basename($file->file, self::BASE_EXTENSION) ) . ".[a-zA-Z0-9\-]+"; //add to the rendered list $rendered[] = $file->file; } } //if there is something to do if(count($names) > 0 ) { //create the actual path $path = self::$serverPath_cache . implode("_",$names) . self::COMBINE_EXTENSION; //create the actual regular expression $regex = preg_replace("/\./","\.", self::$serverPath_cache) . implode("_",$regexes) . preg_replace("/\./","\.", self::COMBINE_EXTENSION ); //no need to remake the file if(!file_exists($path)) { //iterate again foreach($files as $file) { //re-check if($file->combine && $file->mode == 7 ) { //pack and get the contents $file->pack(true); $tmp .= $file->contents; } } //all done, write it out and throw an exception if we have an issue if( !file_put_contents($path,$tmp )) { throw new Exception("Cannot save file ('".$path."')"); } } //cleaning time, see documentation for clean() foreach( new DirectoryIterator( dirname($regex) ) as $f ) { $fileDir = $f->getPath(); $fileName = $f->getFilename(); if( $f->isFile() ) { if(preg_match("=" . "^" . basename($regex) . "=",$fileName) > 0 && $fileName != basename($path) /*&& preg_match("/".$this->version."/", $fileName) == 0*/) { @unlink($fileDir . "/" . $fileName); } } } $path = self::$HTTPPath_cache . implode("_",$names) . self::COMBINE_EXTENSION; return '' . "\n"; } } /** * Copies the file to the cache directory and returns the correct filename and path for HTTP requests. */ private function move() { //create the new filename $newfile = basename(str_replace('/','-',$this->file), self::BASE_EXTENSION) . self::BASE_EXTENSION; //copy it copy($this->file, self::$serverPath_cache . $newfile); //modify our var $this->file = self::$serverPath_cache . $newfile; //return return self::$HTTPPath_cache. $newfile; } /** * Adds the version to the filename for caching purposes and returns the correct filename and path for HTTP requests. */ private function version() { //genereate the new filename $newfile = preg_replace("/js/i", 'v'. $this->version . ".js", $this->file); //if the file exists, we would be simply replacing like with like (since the version is the same) //so this only is executed if the file doesnt exist //if it was moved, rename it since it was copied to the cache dir //if it wasn't moved, the original needs to be preserved, so copy it if($this->options['move'] == 1 && !file_exists($newfile)) rename($this->file, $newfile); else if(!file_exists($newfile)) copy($this->file, $newfile); //modify the filename $this->file = $newfile; //if was moved we need a new path if($this->options['move']) $newfile = self::HTTPPATH_CACHE . basename($newfile); else $newfile = "/" . $this->file; //return return $newfile; } /** * Run's Dean Edwards' JSPacker on the file to minify the Javascript source and returns the correct filename and path for HTTP requests. * * Functionality is provided for encoding and further minifying the Javascript, however; this has been shown to increase page * load time do to the extra processing of Javascript client side. The code for this has been commented out and may be uncommented * for use. This encoding includes an algorithm to use all 4 methods and find the smallest. */ private function pack($combine = false) { if($this->load()) { //get the packer, no use for a global require... let's be efficient! require_once('jsPacker/class.JavaScriptPacker.php'); //if not managed by combine(), create the filename if(!$combine) $this->file = preg_replace("/\.js/i",self::PACKED_EXTENSION,$this->file); $newfile = ""; //if it was cached if(preg_match("-".self::$serverPath_cache."-",$this->file) > 0) { //build the new filename and path $newfile = self::HTTPPATH_CACHE . preg_replace("-".self::SERVERPATH_CACHE."-","",$this->file); } //if it ws not cached else if(preg_match("-".self::$serverPath."-",$this->file) > 0) { //build the new filename and path $newfile = self::$HTTPPath . preg_replace("-".self::$serverPath."-","",$this->file); } //hmm... neither. we'll skip that else { //looks like we were already good to go $newfile = $this->file; } //if we arent combining it, and the file already exists, return and save processor time if(!$combine && file_exists($this->file)) { return $newfile; } //non-encoded minification $packerNone = new JavaScriptPacker($this->contents, 'None', true, false); $packedNone = $packerNone->pack(); //encoded minification, uncomment the algorithms you would like to employ /* $packer10 = new JavaScriptPacker($this->contents, 10 , true, false); $packed10 = $packer10->pack(); $packer62 = new JavaScriptPacker($this->contents, 62 , true, false); $packed62 = $packer62->pack(); $packer95 = new JavaScriptPacker($this->contents, 95 , true, false); $packed95 = $packer95->pack(); */ //create an array based on length of output file to find the smallest $sizes[strlen($packedNone)] = "packedNone"; /* $sizes[strlen($packed10)] = "packed10"; $sizes[strlen($packed62)] = "packed62"; $sizes[strlen($packed95)] = "packed95"; */ //Reverse sort by key to order by filesizes krsort($sizes); $tmp = array_pop($sizes); $this->contents = $$tmp; //if it isn't combining, save it, combine() takes care of this for us if(!$combine) $this->save(); } return $newfile; } /** * Checks for existance of the file and loads the contents into a variable. Throws an exception if the file does not exist. */ private function load() { //make sure it exists if( file_exists($this->file) ) { //get the contents $this->contents = file_get_contents($this->file); return true; } else { throw new Exception("File not found ('".$this->file."')"); } } /** * Puts the data from the contents variable into a file. Throws an exception if this fails. */ private function save() { //write the file and throw an exception if we have an issue if( !file_put_contents($this->file,$this->contents) ) { throw new Exception("Cannot save file ('".$this->file."')"); } } /** * Removes old versions of files that have been versioned. Any non-versioned file will be omited due to inability to * resolve if it is old or not. */ private function clean() { //get original info $path = dirname($this->file); $file = basename($this->unaltered, self::BASE_EXTENSION); //build regular expressions based on what has been done to the file $regex = "=" . preg_replace("/\./","\.", $file . ".v") . ".*"; $file .= ".v" . $this->version; if($this->options['pack']) { $regex .= "[^comb]\.pack"; $file .= ".pack"; } else { $regex .= "[^comb][^pack]"; } $regex .= "\.js="; $file .= ".js"; //iterate through the directory and search for possible matches to remove. foreach( new DirectoryIterator( $path ) as $f ) { $fileDir = $f->getPath(); $fileName = $f->getFilename(); if( $f->isFile() ) { //check to see if the file is versioned and should be removed if(preg_match($regex,$fileName) > 0 && $fileName != $file && preg_match("/".$this->version."/", $fileName) == 0) { //remove any file that matches @unlink($fileDir . "/" . $fileName); } } } } } ?>