diff options
Diffstat (limited to 'app/php/web/ClientScriptManager.php')
-rw-r--r-- | app/php/web/ClientScriptManager.php | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/app/php/web/ClientScriptManager.php b/app/php/web/ClientScriptManager.php new file mode 100644 index 0000000..84b9d6f --- /dev/null +++ b/app/php/web/ClientScriptManager.php @@ -0,0 +1,243 @@ +<?php + +Prado::using('Application.layouts.Layout'); + +class ClientScriptManager extends TClientScriptManager { + + private $_page; + public function __construct(TPage $page) { + $this->_page = $page; + parent::__construct($page); + } + + private function _getBasePath() { + return Prado::getPathOfNamespace('Web') . DIRECTORY_SEPARATOR; + } + + private function _getBasePaths($urls) { + $basePath = $this->_getBasePath(); + return 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 $_renderedScriptsInitialized = FALSE; + + private function _getRenderedScriptsStoreKey() { + $template = $this->_page->Master; + if (!$template instanceof Layout) { + throw new TNotSupportedException( + 'Compiled assets may only be used within Layout master class controls' + ); + } + return 'RenderedScripts.' . $template->generateViewID(); + } + + private function _getCache() { + $cache = $this->Application->Cache; + if (!$cache) { + throw new TNotSupportedException( + 'Compiled assets require cache to be configured' + ); + } + return $cache; + } + + 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 _compileFiles($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->_compileFiles($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->_compileFiles($newLocalScripts); + } + $scriptUrls = array_values( + array_unique(array_merge($scriptUrls, $newRemoteScripts)) + ); + return $scriptUrls; + } else { + 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); + } + +} + +?> |