* @license PHP License * @package WB * @subpackage base */ WBClass::load('WBHtml'); /** * Insert HTML * * Basic interface to insert some HTML * * @version 0.5.1 * @package WB * @subpackage html */ class WBHtml_JS extends WBHtml { /** * file type: any */ const TYPE_ANY = 0; /** * file type: js */ const TYPE_JS = 1; /** * file type: css */ const TYPE_CSS = 2; /** * JavaScript Area: include a file */ const AREA_INCLUDE = 1; /** * JavaScript Area: things to do on load */ const AREA_ONLOAD = 2; /** * JavaScript Area: include some lines of code */ const AREA_CODE = 4; /** * JavaScript Area: some code below content */ const AREA_FOOT = 8; /** * JavaScript Area: include a file, but defered */ const AREA_INCLUDE_DEFER = 16; /** * template to include files */ const FMT_INCLUDE = ''; /** * template to preload files */ const FMT_INCLUDE_PRELOAD = ''; /** * template to include code snippets */ const FMT_CODE = ''; /** * Template for loaded javascript */ const FMT_LOADED = 'WB.Class.loaded["%s"] = 1;'; /** * list of included files * @var array */ private static $included = array(); /** * List of include files defered or normal * @var array */ private static $inc = array(); /** * complet javascript buffer * @see finalize() * @var array */ private static $bufferCode = array(); /** * complet javascript buffer * @see finalize() * @var array */ private static $bufferFoot = array(); /** * static constructor * * make sure that basic WB class is always loaded */ public static function staticConstruct() { self::$inc[self::AREA_INCLUDE] = array(); self::$inc[self::AREA_INCLUDE_DEFER] = array(); self::$bufferCode[self::AREA_INCLUDE] = array(); self::$bufferCode[self::AREA_INCLUDE_DEFER] = array(); $file = WBClass::create('WBFile'); $file->mkdir('var/cache/javascript'); WBHtml::addFinalizer(array(__CLASS__, 'finalize')); } /** * Get List of Included Files * * If flush is true, empty list of included files, as well as there script-tag-areas * * @param bool flush * @return array */ public static function getIncluded($flush = false) { $inc = self::$included; if ($flush) { self::$included = array(); self::$inc[self::AREA_INCLUDE] = array(); self::$inc[self::AREA_INCLUDE_DEFER] = array(); } return $inc; } /** * Add Javascript * * Add string that will be included in HTML at the end * * @param string $html * @param in $area */ public static function add($js, $area = self::AREA_INCLUDE) { switch ($area) { case self::AREA_INCLUDE_DEFER: case self::AREA_INCLUDE: $js = ltrim($js, '/'); // include only once if (isset(self::$included[$js])) { ++self::$included[$js]; return; } self::$included[$js] = 1; self::$inc[$area][] = $js; return; break; case self::AREA_CODE: if (WBParam::get('wb/html/js/include', 'bulk')) { self::$bufferCode[self::AREA_INCLUDE] = array( 'type' => 'code', 'load' => $js ); return; } $html = sprintf(self::FMT_CODE, $js); $des = parent::AREA_HEAD; break; case self::AREA_ONLOAD: $html = $js; $des = parent::AREA_JS_ONLOAD; break; case self::AREA_FOOT: self::$bufferFoot[] = $js; return; break; default: parent::checkArea(null); return; break; } parent::add($html, $des); } /** * finalize * * Build bulk file if requzired. Append all included JavaScript files * and code snippets to a single bulk. */ public static function finalize() { foreach (self::$inc[self::AREA_INCLUDE] as $js) { if (WBParam::get('wb/html/js/include', 'bulk') == 'bulk') { self::$bufferCode[self::AREA_INCLUDE][] = array( 'type' => 'file', 'load' => $js ); continue; } // actually load class parent::add(sprintf(self::FMT_INCLUDE_PRELOAD, $js), parent::AREA_HEAD); parent::add(sprintf(self::FMT_INCLUDE, '', $js), parent::AREA_HEAD); // tell class loader that file was loaded $js = str_replace('/', '.', $js); array_unshift(self::$bufferFoot, sprintf(self::FMT_LOADED, $js)); } foreach (self::$inc[self::AREA_INCLUDE_DEFER] as $js) { if (WBParam::get('wb/html/js/include', 'bulk') == 'bulk') { self::$bufferCode[self::AREA_INCLUDE_DEFER][] = array( 'type' => 'file', 'load' => $js ); continue; } // actually load class parent::add(sprintf(self::FMT_INCLUDE, 'defer ', $js), parent::AREA_HEAD); // tell class loader that file was loaded $js = str_replace('/', '.', $js); array_unshift(self::$bufferFoot, sprintf(self::FMT_LOADED, $js)); } // add foot if (!empty(self::$bufferFoot)) { $html = sprintf(self::FMT_CODE, implode("\n", self::$bufferFoot)); parent::add($html, parent::AREA_FOOT); } self::$bufferFoot = array(); // normal include if (WBParam::get('wb/html/js/include', 'bulk') != 'bulk') { return; } if (!empty(self::$bufferCode[self::AREA_INCLUDE])) { $md5 = md5(serialize(self::$bufferCode[self::AREA_INCLUDE])); $file = self::makeBulk($md5, self::$bufferCode[self::AREA_INCLUDE]); parent::add(sprintf(self::FMT_INCLUDE_PRELOAD, '?cs=' . $md5), parent::AREA_HEAD); parent::add(sprintf(self::FMT_INCLUDE, '', '?cs=' . $md5), parent::AREA_HEAD); self::$bufferCode[self::AREA_INCLUDE] = array(); } if (!empty(self::$bufferCode[self::AREA_INCLUDE_DEFER])) { $md5 = md5(serialize(self::$bufferCode[self::AREA_INCLUDE_DEFER])); $file = self::makeBulk($md5, self::$bufferCode[self::AREA_INCLUDE_DEFER]); parent::add(sprintf(self::FMT_INCLUDE, 'defer ', '?cs=' . $md5), parent::AREA_HEAD); self::$bufferCode[self::AREA_INCLUDE_DEFER] = array(); } } /** * Make Bulk for List of JS-Code and Files * * @param string $name * @param array $list of JS-files and Code * @return string $file path to bulk file */ public static function makeBulk($name, $list) { $dir = WBParam::get('wb/dir/base') . '/var/cache/javascript/bulk'; $file = $dir . '/' . $name; // in case file exists, cache file must be younger than youngest file if (file_exists($file)) { $maxMTime = 0; foreach ($list as $b) { if ($b['type'] != 'file') { continue; } $maxMTime = max($maxMTime, filemtime(self::realpath($b['load']))); } if (filemtime($file) > $maxMTime) { return $file; } // remove cache file unlink($file); // in case file does not exists, make sure folder exists } else if (!is_dir($dir)) { mkdir($dir, 0777, true); chmod($dir, 0777); } // create bulk JavaScript file touch($file); // first run: create actual bulk file foreach ($list as $b) { switch ($b['type']) { case 'file': $real = self::realpath($b['load']); if (empty($real)) { // echo 'leer
' . var_export($b, true) . "
\n"; } file_put_contents($file, '// file: ' . $b['load'] . "\n", FILE_APPEND); $js = file_get_contents(self::realpath($b['load'])); break; case 'code': file_put_contents($file, '// code: ' . strlen($b['load']) . "\n", FILE_APPEND); $js = $b['load']; break; } file_put_contents($file, $js . "\n", FILE_APPEND); } // second run: tell loaded that files where included foreach ($list as $b) { switch ($b['type']) { case 'file': $js = str_replace('/', '.', $b['load']); file_put_contents($file, sprintf('WB.Class.loaded["%s"] = 1;' . "\n", $js), FILE_APPEND); break; case 'code': break; } } chmod($file, 0666); return $file; } /** * resolve path of JavaScript file * * Like the PHP funktion realpath(), it resolves the actual path * of a JavaScript file. In case the requested file is neither in SYSTEMDIR * nor in BASEDIR, the result will be empty. * * @see realpath() * @param $js * @param $type * @return string */ public static function realpath($js, &$type = null) { // avoid to break out of sysem/base dir if (strstr($js, '..')) { WBClass::load('WBException_Argument'); throw new WBException_Argument('".." are not allowed in JavaScript path', 2, __CLASS__); } // remove optional ".js" $ext = 'js'; $type = self::TYPE_JS; if (strpos($js, '.')) { $tmp = explode('.', $js); if (end($tmp) == 'js') { array_pop($tmp); } else if (end($tmp) == 'css') { $ext = array_pop($tmp); $type = self::TYPE_CSS; } else { $ext = array_pop($tmp); $type = self::TYPE_ANY; } $js = implode('.', $tmp); } if ($js[0] != '/') { $js = '/' . $js; } // try to locate file in base dir and in system dir $dir = realpath(WBParam::get('wb/dir/base') . '/resource/js'); $file = realpath($dir . $js . '.' . $ext); if (!file_exists($file)) { $res = WBParam::get('wb/dir/resource', array()); foreach ($res as $r) { $dir = realpath($r . '/resource/js'); $file = realpath($dir . $js . '.' . $ext); if (!empty($file)) { break; } } } if(empty($file)) { return ''; } return $file; } /** * receive path of buld file * * @param String $md5 * @return string */ public static function getBulkPath($md5) { $dir = WBParam::get('wb/dir/base') . '/var/cache/javascript/bulk'; $file = $dir . '/' . $md5; if (file_exists($file)) { return $file; } return ''; } }