diff options
Diffstat (limited to 'app/php/web')
-rw-r--r-- | app/php/web/AssetManager.php | 29 | ||||
-rw-r--r-- | app/php/web/ClientScriptManager.php | 243 | ||||
-rw-r--r-- | app/php/web/TemplateControl.php | 53 | ||||
-rw-r--r-- | app/php/web/config.xml | 7 |
4 files changed, 332 insertions, 0 deletions
diff --git a/app/php/web/AssetManager.php b/app/php/web/AssetManager.php new file mode 100644 index 0000000..1094917 --- /dev/null +++ b/app/php/web/AssetManager.php @@ -0,0 +1,29 @@ +<?php + +class AssetManager extends TAssetManager { + + public function init($config) { + if ($this->BaseUrl === NULL) { + $appWebPath = preg_replace( + '#' . $this->Application->Request->ApplicationUrl . '$#', + '', + $this->Application->Request->ApplicationFilePath + ); + $appBaseUrl = preg_replace( + '#^' . $appWebPath . '#', + '', + dirname($this->Application->Request->ApplicationFilePath) + ); + $this->BaseUrl = $appBaseUrl + . preg_replace( + '#^' . Prado::getPathOfNamespace('Web') . '#', + '', + $this->BasePath + ); + } + parent::init($config); + } + +} + +?> 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); + } + +} + +?> diff --git a/app/php/web/TemplateControl.php b/app/php/web/TemplateControl.php new file mode 100644 index 0000000..e3c9e26 --- /dev/null +++ b/app/php/web/TemplateControl.php @@ -0,0 +1,53 @@ +<?php + +class TemplateControl extends TTemplateControl { + + private function _getControlScriptPath($className) { + return Prado::getPathOfNamespace('Application.controls.scripts') + . DIRECTORY_SEPARATOR + . $className + . '.js'; + } + + public function onPreRender($param) { + parent::onPreRender($param); + $scriptFile = $this->_getControlScriptPath(get_class($this)); + if (file_exists($scriptFile)) { + foreach ($this->getPradoScriptDependencies() as $dependency) { + $this->Page->ClientScript->registerPradoScript($dependency); + } + foreach ($this->getControlScriptDependencies() as $dependency) { + $this->Page->ClientScript->registerScriptFile( + 'TemplateControl.' . $dependency, + $this->Application->AssetManager->publishFilePath( + $this->_getControlScriptPath($dependency) + ) + ); + } + foreach ($this->getExternalScriptDependencies() as $dependency) { + $this->Page->ClientScript->registerHeadScriptFile( + $dependency, $dependency + ); + } + $this->Page->ClientScript->registerScriptFile( + 'TemplateControl.' . get_class($this), + $this->Application->AssetManager->publishFilePath($scriptFile) + ); + } + } + + protected function getPradoScriptDependencies() { + return []; + } + + protected function getControlScriptDependencies() { + return []; + } + + protected function getExternalScriptDependencies() { + return []; + } + +} + +?> diff --git a/app/php/web/config.xml b/app/php/web/config.xml new file mode 100644 index 0000000..49bbcc6 --- /dev/null +++ b/app/php/web/config.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<configuration> + <modules> + <module id="asset" class="Application.web.AssetManager" + BasePath="Web._assets" /> + </modules> +</configuration> |