diff options
-rw-r--r-- | app/php/application.xml | 4 | ||||
-rw-r--r-- | app/php/components/ClientScriptManager.php | 168 |
2 files changed, 171 insertions, 1 deletions
diff --git a/app/php/application.xml b/app/php/application.xml index 8b76f74..7c976e5 100644 --- a/app/php/application.xml +++ b/app/php/application.xml @@ -9,7 +9,9 @@ <include file="Application.controls.config" /> <services> - <service id="page" class="TPageService" /> + <service id="page" + class="TPageService" + ClientScriptManagerClass="Application.components.ClientScriptManager" /> </services> <modules> diff --git a/app/php/components/ClientScriptManager.php b/app/php/components/ClientScriptManager.php new file mode 100644 index 0000000..64e5b58 --- /dev/null +++ b/app/php/components/ClientScriptManager.php @@ -0,0 +1,168 @@ +<?php + +class ClientScriptManager extends TClientScriptManager { + + private $_page; + public function __construct(TPage $page) { + $this->_page = $page; + parent::__construct($page); + } + + private function _getBasePath() { + $assetManager = $this->Application->AssetManager; + return preg_replace( + '#' . preg_quote($assetManager->BaseUrl) . '$#', + '', + $assetManager->BasePath + ); + } + + 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 _getRenderedScripts() { + if ($this->_page->IsCallBack || $this->_renderedScriptsInitialized) { + return $this->Session->itemAt('RenderedScripts') ?: []; + } else { + $this->Session->remove('RenderedScripts'); + $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->Session->add('RenderedScripts', $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('file_get_contents', $paths) + ); + file_put_contents($cacheFile, $scriptContent); + } + return $this->Application->AssetManager->publishFilePath($cacheFile); + } + + public function renderScriptFiles($writer, Array $files) { + if ($this->getApplication()->getMode() !== TApplicationMode::Debug) { + if ($files) { + $assetPath = $this->_compileFiles($files); + $writer->write(TJavascript::renderScriptFile($assetPath)); + } + } else { + parent::renderScriptFiles($writer, $files); + } + } + + public function getScriptUrls() { + if ($this->getApplication()->getMode() !== TApplicationMode::Debug) { + $registeredScripts = array_unique( + array_merge( + $this->_scripts, $this->_headScripts + ) + ); + $renderedScripts = $this->_getRenderedScripts(); + $scriptUrls = []; + $newScripts = []; + foreach ($registeredScripts as $registeredScript) { + $found = FALSE; + foreach ($renderedScripts as $compiledFile => $scripts) { + if (in_array($registeredScript, $scripts)) { + $scriptUrls[] = $this->Application->AssetManager->getPublishedUrl( + $compiledFile + ); + $found = TRUE; + } + } + if (!$found) { + $newScripts[] = $registeredScript; + } + } + if ($newScripts) { + $scriptUrls[] = $this->_compileFiles($newScripts); + } + 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); + } + +} + +?> |