_page = $page; parent::__construct($page); } private function _getBasePath() { return Prado::getPathOfNamespace('Web') . DIRECTORY_SEPARATOR; } private function _getBasePaths($urls) { $basePath = $this->_getBasePath(); return array_combine( $urls, array_map( function($path) use($basePath) { return $basePath . DIRECTORY_SEPARATOR . $path; }, $urls ) ); } private function _getCachePath($subdir = 'assets') { $cachePath = $this->Application->RuntimePath . DIRECTORY_SEPARATOR . $subdir; if (!is_dir($cachePath)) { if (file_exists($cachePath)) { throw new TIOException( 'Client script manager cache path "' . $cachePath . ' "exists and is not a directory' ); } else { mkdir($cachePath); } } return $cachePath; } private function _getCacheFilePath($path, $type) { return $this->_getCachePath($type) . DIRECTORY_SEPARATOR . $path; } private function _getFileCollectionCacheKey($files) { sort($files); return md5(implode(PHP_EOL, $files)); } private function _getFileCollectionMTime($files) { return max(array_map('filemtime', $files)); } private function _getRenderedAssetsStoreKey($type) { $template = $this->_page->Master; if (!$template instanceof Layout) { throw new TNotSupportedException( 'Compiled assets may only be used within Layout master class controls' ); } return 'Rendered' . $type . '.' . $template->generateViewID(); } private function _getRenderedScriptsStoreKey() { return $this->_getRenderedAssetsStoreKey('Scripts'); } private function _getRenderedSheetsStoreKey() { return $this->_getRenderedAssetsStoreKey('Sheets'); } private function _getCache() { $cache = $this->Application->Cache; if (!$cache) { throw new TNotSupportedException( 'Compiled assets require cache to be configured' ); } return $cache; } // Scripts private $_renderedScriptsInitialized = FALSE; private function _getRenderedScripts() { $sessionKey = $this->_getRenderedScriptsStoreKey(); if ($this->_page->IsCallBack || $this->_renderedScriptsInitialized) { return $this->_getCache()->get($sessionKey) ?: []; } else { $this->_getCache()->delete($sessionKey); $this->_renderedScriptsInitialized = TRUE; return []; } } private function _appendRenderedScripts(array $newScripts, $compiledFileKey) { $scripts = $this->_getRenderedScripts(); if (!isset($scripts[$compiledFileKey])) { $scripts[$compiledFileKey] = []; } $scripts[$compiledFileKey] = array_unique( array_merge( $scripts[$compiledFileKey], $newScripts ) ); $this->_getCache()->set( $this->_getRenderedScriptsStoreKey(), $scripts ); } private function _compileScriptFiles($files) { foreach ($files as $file) { $this->markScriptFileAsRendered($file); } $paths = $this->_getBasePaths($files); $cacheKey = $this->_getFileCollectionCacheKey($paths); $cacheFile = $this->_getCacheFilePath($cacheKey . '.js', 'scripts'); $this->_appendRenderedScripts($files, $cacheFile); if (!file_exists($cacheFile) || (filemtime($cacheFile) < $this->_getFileCollectionMTime($paths))) { $scriptContent = implode( PHP_EOL, array_map( function($path) { return trim( TJavaScript::JSMin( file_get_contents($path) ) ); }, $paths ) ); file_put_contents($cacheFile, $scriptContent); } return $this->Application->AssetManager->publishFilePath($cacheFile); } private function _determineLocalScriptFiles($files) { $basePath = $this->_getBasePath(); return array_filter( $files, function($file) use($basePath) { return file_exists($basePath . DIRECTORY_SEPARATOR . $file); } ); } private function _renderLocalScriptFiles($writer, $localFiles) { if ($localFiles) { $assetPath = $this->_compileScriptFiles($localFiles); $writer->write(TJavascript::renderScriptFile($assetPath)); } } private function _renderExternalScriptFiles($writer, $externalFiles) { if ($externalFiles) { foreach ($externalFiles as $file) { $this->markScriptFileAsRendered($file); $this->_appendRenderedScripts([$file], $file); } parent::renderScriptFiles($writer, $externalFiles); } } public function renderScriptFiles($writer, Array $files) { if ($this->getApplication()->getMode() !== TApplicationMode::Debug) { if ($files) { $localFiles = $this->_determineLocalScriptFiles($files); $this->_renderLocalScriptFiles($writer, $localFiles); $externalFiles = array_diff($files, $localFiles); $this->_renderExternalScriptFiles($writer, $externalFiles); } } else { parent::renderScriptFiles($writer, $files); } } private function _getRenderedScriptUrl($registeredScript) { $renderedScripts = $this->_getRenderedScripts(); foreach ($renderedScripts as $compiledFile => $scripts) { if (in_array($registeredScript, $scripts)) { if (file_exists($compiledFile)) { return $this->Application->AssetManager->getPublishedUrl( $compiledFile ); } else { return $registeredScript; } } } return FALSE; } public function getScriptUrls() { if ($this->getApplication()->getMode() !== TApplicationMode::Debug) { $registeredScripts = array_unique( array_merge( $this->_scripts, $this->_headScripts ) ); $scriptUrls = []; $newScripts = []; foreach ($registeredScripts as $registeredScript) { $renderedScriptUrl = $this->_getRenderedScriptUrl( $registeredScript ); if ($renderedScriptUrl) { $scriptUrls[] = $renderedScriptUrl; } else { $newScripts[] = $registeredScript; } } $newLocalScripts = $this->_determineLocalScriptFiles($newScripts); $newRemoteScripts = array_diff($newScripts, $newLocalScripts); if ($newLocalScripts) { $scriptUrls[] = $this->_compileScriptFiles($newLocalScripts); } $scriptUrls = array_values( array_unique(array_merge($scriptUrls, $newRemoteScripts)) ); return $scriptUrls; } return parent::getScriptUrls(); } private $_scripts = []; private $_headScripts = []; public function registerScriptFile($key, $file) { $this->_scripts[$key] = $file; return parent::registerScriptFile($key, $file); } public function registerHeadScriptFile($key, $file) { $this->_headScripts[$key] = $file; return parent::registerHeadScriptFile($key, $file); } // Stylesheets private $_renderedSheetsInitialized = FALSE; private function _getRenderedSheets() { $sessionKey = $this->_getRenderedSheetsStoreKey(); if ($this->_page->IsCallBack || $this->_renderedSheetsInitialized) { return $this->_getCache()->get($sessionKey) ?: []; } else { $this->_getCache()->delete($sessionKey); $this->_renderedSheetsInitialized = TRUE; return []; } } private function _appendRenderedSheets(array $newSheets, $compiledFileKey) { $sheets = $this->_getRenderedSheets(); if (!isset($sheets[$compiledFileKey])) { $sheets[$compiledFileKey] = []; } $sheets[$compiledFileKey] = array_merge( $sheets[$compiledFileKey], $newSheets ); $this->_getCache()->set( $this->_getRenderedSheetsStoreKey(), $sheets ); } private function _fixStyleSheetPaths($content, $originalUrl) { $originalDir = dirname($originalUrl); return preg_replace_callback( '/url\s*\([\'"]?(.*?)[\'"]?\)/', function($matches) use($originalDir) { $url = parse_url($matches[1]); if (isset($url['scheme']) || isset($url['host'])) { return $matches[0]; } return str_replace( $matches[1], $originalDir . '/' . $matches[1], $matches[0] ); }, $content ); } private function _compileSheetFiles($files) { $paths = $this->_getBasePaths( array_map('reset', $files) ); $cacheKey = $this->_getFileCollectionCacheKey($paths); $cacheFile = $this->_getCacheFilePath($cacheKey . '.css', 'styles'); $this->_appendRenderedSheets($files, $cacheFile); if (!file_exists($cacheFile) || (filemtime($cacheFile) < $this->_getFileCollectionMTime($paths))) { Prado::using('Lib.cssmin.CssMin'); $styleContent = implode( PHP_EOL, array_map( function($origPath, $path) { return trim( CssMin::minify( $this->_fixStyleSheetPaths( file_get_contents($path), $origPath ) ) ); }, array_keys($paths), $paths ) ); file_put_contents($cacheFile, $styleContent); } return $this->Application->AssetManager->publishFilePath($cacheFile); } private function _determineLocalSheetFiles($files) { $basePath = $this->_getBasePath(); return array_filter( $files, function($file) use($basePath) { return file_exists($basePath . DIRECTORY_SEPARATOR . $file[0]); } ); } private function _renderSheetFileTag(THtmlWriter $writer, $href, $media) { $writer->addAttribute('rel', 'stylesheet'); $writer->addAttribute('type', 'text/css'); $writer->addAttribute('media', $media); $writer->addAttribute('href', $href); $writer->renderBeginTag('link'); $writer->write(PHP_EOL); } private function _renderLocalSheetFiles(THtmlWriter $writer, $localFiles) { if ($localFiles) { $fileTypes = []; foreach ($localFiles as $file) { $type = $file[1] ?: 'all'; if (!isset($fileTypes[$type])) { $fileTypes[$type] = []; } $fileTypes[$type][] = $file; } foreach ($fileTypes as $type => $files) { $assetPath = $this->_compileSheetFiles($files); $this->_renderSheetFileTag($writer, $assetPath, $type); } } } private function _renderExternalSheetFiles(THtmlWriter $writer, $externalFiles) { if ($externalFiles) { foreach ($externalFiles as $file) { $this->_appendRenderedSheets([$file], $file[0]); $this->_renderSheetFileTag($writer, $file[0], $file[1] ?: 'all'); } } } public function renderStyleSheetFiles($writer) { if ($this->getApplication()->getMode() !== TApplicationMode::Debug) { $files = $this->_styles; if ($files) { $localFiles = $this->_determineLocalSheetFiles($files); $this->_renderLocalSheetFiles($writer, $localFiles); $externalFiles = array_diff_key($files, $localFiles); $this->_renderExternalSheetFiles($writer, $externalFiles); } } else { parent::renderStyleSheetFiles($writer); } } private function _getRenderedSheetUrl($registeredSheet) { $renderedSheets = $this->_getRenderedSheets(); foreach ($renderedSheets as $compiledFile => $sheets) { foreach ($sheets as $sheet) { if ($registeredSheet[0] == $sheet[0]) { if (file_exists($compiledFile)) { return $this->Application->AssetManager->getPublishedUrl( $compiledFile ); } else { return $registeredSheet[0]; } } } } return FALSE; } public function getStyleSheetUrls() { if ($this->getApplication()->getMode() !== TApplicationMode::Debug) { $registeredSheets = $this->_styles; $sheetUrls = []; $newSheets = []; foreach ($registeredSheets as $registeredSheet) { $renderedSheetUrl = $this->_getRenderedSheetUrl( $registeredSheet ); if ($renderedSheetUrl) { $sheetUrls[] = $renderedSheetUrl; } else { $newSheets[] = $registeredSheet; } } $newLocalSheets = $this->_determineLocalSheetFiles($newSheets); $newLocalUrls = array_map('reset', $newLocalSheets); $newRemoteSheets = array_filter( $newSheets, function($sheet) use($newLocalUrls) { return !in_array($sheet[0], $newLocalUrls); } ); $newRemoteUrls = array_map('reset', $newRemoteSheets); if ($newLocalSheets) { $sheetUrls[] = $this->_compileSheetFiles($newLocalSheets); } $sheetUrls = array_values( array_unique(array_merge($sheetUrls, $newRemoteUrls)) ); return $sheetUrls; } return parent::getStyleSheetUrls(); } private $_styles = []; private $_themeStyles = []; public function registerStyleSheetFile($key, $file, $media = '') { $this->_styles[$key] = [$file, $media]; return parent::registerStyleSheetFile($key, $file, $media); } public function registerThemeStyleSheetFile($key, $file, $media = '') { $this->_themeStyles[$key] = $file; return $this->registerStyleSheetFile($key, $file, $media); } } ?>