summaryrefslogtreecommitdiff
path: root/app/php/web/ClientScriptManager.php
diff options
context:
space:
mode:
Diffstat (limited to 'app/php/web/ClientScriptManager.php')
-rw-r--r--app/php/web/ClientScriptManager.php243
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);
+ }
+
+}
+
+?>