diff --git a/administrator/templates/atum/Service/HTML/Atum.php b/administrator/templates/atum/Service/HTML/Atum.php
index a28602f055e17..18c4c034596f7 100644
--- a/administrator/templates/atum/Service/HTML/Atum.php
+++ b/administrator/templates/atum/Service/HTML/Atum.php
@@ -123,7 +123,9 @@ public static function rootcolors(Registry $params): void
if (count($root))
{
- Factory::getDocument()->addStyleDeclaration(':root {' . implode($root) . '}');
+ /** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
+ $wa = Factory::getApplication()->getDocument()->getWebAssetManager();
+ $wa->addInlineStyle(':root {' . implode($root) . '}');
}
}
diff --git a/administrator/templates/atum/error_full.php b/administrator/templates/atum/error_full.php
index f4a77b1941b2a..2e624008da494 100644
--- a/administrator/templates/atum/error_full.php
+++ b/administrator/templates/atum/error_full.php
@@ -57,7 +57,8 @@
$this->setMetaData('viewport', 'width=device-width, initial-scale=1');
// @TODO sync with _variables.scss
$this->setMetaData('theme-color', '#1c3d5c');
-$this->addScriptDeclaration('cssVars();');
+$this->getWebAssetManager()
+ ->addInlineScript('cssVars();', ['position' => 'after'], ['type' => 'module'], ['css-vars-ponyfill']);
// Opacity must be set before displaying the DOM, so don't move to a CSS file
$css = '
@@ -69,7 +70,8 @@
}
';
-$this->addStyleDeclaration($css);
+$this->getWebAssetManager()
+ ->addInlineStyle($css);
$monochrome = (bool) $this->params->get('monochrome');
diff --git a/administrator/templates/atum/error_login.php b/administrator/templates/atum/error_login.php
index 648a04bc86ef4..e223110f95e3b 100644
--- a/administrator/templates/atum/error_login.php
+++ b/administrator/templates/atum/error_login.php
@@ -56,7 +56,8 @@
$this->setMetaData('viewport', 'width=device-width, initial-scale=1');
// @TODO sync with _variables.scss
$this->setMetaData('theme-color', '#1c3d5c');
-$this->addScriptDeclaration('cssVars();');
+$this->getWebAssetManager()
+ ->addInlineScript('cssVars();', ['position' => 'after'], ['type' => 'module'], ['css-vars-ponyfill']);
// Opacity must be set before displaying the DOM, so don't move to a CSS file
$css = '
@@ -68,7 +69,8 @@
}
';
-$this->addStyleDeclaration($css);
+$this->getWebAssetManager()
+ ->addInlineStyle($css);
$monochrome = (bool) $this->params->get('monochrome');
diff --git a/administrator/templates/atum/index.php b/administrator/templates/atum/index.php
index 23eab61eac586..df3dbe79778a4 100644
--- a/administrator/templates/atum/index.php
+++ b/administrator/templates/atum/index.php
@@ -63,7 +63,8 @@
$this->setMetaData('viewport', 'width=device-width, initial-scale=1');
// @TODO sync with _variables.scss
$this->setMetaData('theme-color', '#1c3d5c');
-$this->addScriptDeclaration('cssVars();');
+$this->getWebAssetManager()
+ ->addInlineScript('cssVars();', ['position' => 'after'], ['type' => 'module'], ['css-vars-ponyfill']);
// Opacity must be set before displaying the DOM, so don't move to a CSS file
$css = '
@@ -75,7 +76,8 @@
}
';
-$this->addStyleDeclaration($css);
+$this->getWebAssetManager()
+ ->addInlineStyle($css);
$monochrome = (bool) $this->params->get('monochrome');
diff --git a/administrator/templates/atum/login.php b/administrator/templates/atum/login.php
index 9b3d978b6aac1..6a76902f30bc1 100644
--- a/administrator/templates/atum/login.php
+++ b/administrator/templates/atum/login.php
@@ -59,7 +59,8 @@
$this->setMetaData('viewport', 'width=device-width, initial-scale=1');
// @TODO sync with _variables.scss
$this->setMetaData('theme-color', '#1c3d5c');
-$this->addScriptDeclaration('cssVars();');
+$this->getWebAssetManager()
+ ->addInlineScript('cssVars();', ['position' => 'after'], ['type' => 'module'], ['css-vars-ponyfill']);
// Opacity must be set before displaying the DOM, so don't move to a CSS file
$css = '
@@ -71,7 +72,8 @@
}
';
-$this->addStyleDeclaration($css);
+$this->getWebAssetManager()
+ ->addInlineStyle($css);
$monochrome = (bool) $this->params->get('monochrome');
diff --git a/libraries/src/Document/Document.php b/libraries/src/Document/Document.php
index f8852bf9c6aad..4a07f4a363e17 100644
--- a/libraries/src/Document/Document.php
+++ b/libraries/src/Document/Document.php
@@ -150,6 +150,8 @@ class Document
*
* @var array
* @since 1.7.0
+ *
+ * @deprecated 5.0 Use WebAssetManager
*/
public $_script = array();
@@ -175,6 +177,8 @@ class Document
*
* @var array
* @since 1.7.0
+ *
+ * @deprecated 5.0 Use WebAssetManager
*/
public $_style = array();
@@ -545,6 +549,8 @@ public function addScript($url, $options = array(), $attribs = array())
* @return Document instance of $this to allow chaining
*
* @since 1.7.0
+ *
+ * @deprecated 5.0 Use WebAssetManager
*/
public function addScriptDeclaration($content, $type = 'text/javascript')
{
@@ -655,6 +661,8 @@ public function addStyleSheet($url, $options = array(), $attribs = array())
* @return Document instance of $this to allow chaining
*
* @since 1.7.0
+ *
+ * @deprecated 5.0 Use WebAssetManager
*/
public function addStyleDeclaration($content, $type = 'text/css')
{
diff --git a/libraries/src/Document/Renderer/Html/MetasRenderer.php b/libraries/src/Document/Renderer/Html/MetasRenderer.php
index 7ff33e91080ab..84634947921de 100644
--- a/libraries/src/Document/Renderer/Html/MetasRenderer.php
+++ b/libraries/src/Document/Renderer/Html/MetasRenderer.php
@@ -49,11 +49,6 @@ public function render($head, $params = array(), $content = null)
$wa = $this->_doc->getWebAssetManager();
$wc = $this->_doc->getScriptOptions('webcomponents');
- if ($this->_doc->getScriptOptions())
- {
- $wa->useScript('core');
- }
-
// Check for AttachBehavior and web components
foreach ($wa->getAssets('script', true) as $asset)
{
@@ -68,11 +63,31 @@ public function render($head, $params = array(), $content = null)
}
}
- $this->_doc->addScriptOptions('webcomponents', $wc);
+ if ($wc)
+ {
+ $this->_doc->addScriptOptions('webcomponents', array_unique($wc));
+ }
// Trigger the onBeforeCompileHead event
$app->triggerEvent('onBeforeCompileHead');
+ // Add Script Options as inline asset
+ $scriptOptions = $this->_doc->getScriptOptions();
+
+ if ($scriptOptions)
+ {
+ $prettyPrint = (JDEBUG && \defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : false);
+ $jsonOptions = json_encode($scriptOptions, $prettyPrint);
+ $jsonOptions = $jsonOptions ? $jsonOptions : '{}';
+
+ $wa->addInlineScript(
+ $jsonOptions,
+ ['name' => 'joomla.script.options', 'position' => 'before'],
+ ['type' => 'application/json', 'class' => 'joomla-script-options new'],
+ ['core']
+ );
+ }
+
// Lock the AssetManager
$wa->lock();
diff --git a/libraries/src/Document/Renderer/Html/ScriptsRenderer.php b/libraries/src/Document/Renderer/Html/ScriptsRenderer.php
index a1a38f864dc0c..dc764b10c36d8 100644
--- a/libraries/src/Document/Renderer/Html/ScriptsRenderer.php
+++ b/libraries/src/Document/Renderer/Html/ScriptsRenderer.php
@@ -21,6 +21,15 @@
*/
class ScriptsRenderer extends DocumentRenderer
{
+ /**
+ * List of already rendered src
+ *
+ * @var array
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ private $renderedSrc = [];
+
/**
* Renders the document script tags and returns the results as a string
*
@@ -38,176 +47,295 @@ public function render($head, $params = array(), $content = null)
$lnEnd = $this->_doc->_getLineEnd();
$tab = $this->_doc->_getTab();
$buffer = '';
- $mediaVersion = $this->_doc->getMediaVersion();
$wam = $this->_doc->getWebAssetManager();
$assets = $wam->getAssets('script', true);
- $assets = array_merge(array_values($assets), $this->_doc->_scripts);
- $renderedUrls = [];
- $defaultJsMimes = array('text/javascript', 'application/javascript', 'text/x-javascript', 'application/x-javascript');
- $html5NoValueAttributes = array('defer', 'async');
+ // Get a list of inline assets and their relation with regular assets
+ $inlineAssets = $wam->filterOutInlineAssets($assets);
+ $inlineRelation = $wam->getInlineRelation($inlineAssets);
+
+ // Merge with existing scripts, for rendering
+ $assets = array_merge(array_values($assets), $this->_doc->_scripts);
// Generate script file links
foreach ($assets as $key => $item)
{
+ // Check whether we have an Asset instance, or old array with attributes
$asset = $item instanceof WebAssetItemInterface ? $item : null;
- if ($asset && $asset->getOption('webcomponent'))
+ // Add src attribute for non Asset item
+ if (!$asset)
{
- continue;
+ $item['src'] = $key;
}
- if ($asset)
+ // Check for inline content "before"
+ if ($asset && !empty($inlineRelation[$asset->getName()]['before']))
{
- $src = $asset->getUri();
-
- if (!$src)
+ foreach ($inlineRelation[$asset->getName()]['before'] as $itemBefore)
{
- continue;
- }
+ $buffer .= $this->renderInlineElement($itemBefore);
- $attribs = $asset->getAttributes();
- $version = $asset->getVersion();
- $conditional = $asset->getOption('conditional');
+ // Remove this item from inline queue
+ unset($inlineAssets[$itemBefore->getName()]);
+ }
}
- else
- {
- $src = $key;
- $attribs = $item;
- $version = isset($attribs['options']['version']) ? $attribs['options']['version'] : '';
- // Check if stylesheet uses IE conditional statements.
- $conditional = !empty($attribs['options']['conditional']) ? $attribs['options']['conditional'] : null;
- }
+ $buffer .= $this->renderElement($item);
- // Prevent double rendering
- if (!empty($renderedUrls[$src]))
+ // Check for inline content "after"
+ if ($asset && !empty($inlineRelation[$asset->getName()]['after']))
{
- continue;
+ foreach ($inlineRelation[$asset->getName()]['after'] as $itemBefore)
+ {
+ $buffer .= $this->renderInlineElement($itemBefore);
+
+ // Remove this item from inline queue
+ unset($inlineAssets[$itemBefore->getName()]);
+ }
}
+ }
- $renderedUrls[$src] = true;
+ // Generate script declarations for assets
+ foreach ($inlineAssets as $item)
+ {
+ $buffer .= $this->renderInlineElement($item);
+ }
- // Check if script uses media version.
- if ($version && strpos($src, '?') === false && ($mediaVersion || $version !== 'auto'))
+ // Generate script declarations for old scripts
+ foreach ($this->_doc->_script as $type => $contents)
+ {
+ // Test for B.C. in case someone still store script declarations as single string
+ if (\is_string($contents))
{
- $src .= '?' . ($version === 'auto' ? $mediaVersion : $version);
+ $contents = [$contents];
}
- $buffer .= $tab;
-
- // This is for IE conditional statements support.
- if (!\is_null($conditional))
+ foreach ($contents as $content)
{
- $buffer .= '';
- }
+ // Check if script uses media version.
+ if ($version && strpos($src, '?') === false && ($mediaVersion || $version !== 'auto'))
+ {
+ $src .= '?' . ($version === 'auto' ? $mediaVersion : $version);
+ }
+
+ $buffer .= $tab;
- $buffer .= $lnEnd;
+ // This is for IE conditional statements support.
+ if (!\is_null($conditional))
+ {
+ $buffer .= '';
+ }
- foreach ($contents as $content)
- {
- $buffer .= $tab . '' . $lnEnd;
- }
+ // This is for full XHTML support.
+ if ($this->_doc->_mime !== 'text/html')
+ {
+ $buffer .= $tab . $tab . '//_doc->_custom) as $custom)
+ $buffer .= $content . $lnEnd;
+
+ // See above note
+ if ($this->_doc->_mime !== 'text/html')
{
- $buffer .= $tab . $custom . $lnEnd;
+ $buffer .= $tab . $tab . '//]]>' . $lnEnd;
}
- return ltrim($buffer, $tab);
+ $buffer .= $tab . '' . $lnEnd;
+
+ return $buffer;
+ }
+
+ /**
+ * Renders the element attributes
+ *
+ * @param array $attributes The element attributes
+ *
+ * @return string The attributes string
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ private function renderAttributes(array $attributes) : string
+ {
+ $buffer = '';
+
+ $defaultJsMimes = array('text/javascript', 'application/javascript', 'text/x-javascript', 'application/x-javascript');
+ $html5NoValueAttributes = array('defer', 'async');
+
+ foreach ($attributes as $attrib => $value)
+ {
+ // Don't add the 'options' attribute. This attribute is for internal use (version, conditional, etc).
+ if ($attrib === 'options' || $attrib === 'src')
+ {
+ continue;
+ }
+
+ // Don't add type attribute if document is HTML5 and it's a default mime type. 'mime' is for B/C.
+ if (\in_array($attrib, array('type', 'mime')) && $this->_doc->isHtml5() && \in_array($value, $defaultJsMimes))
+ {
+ continue;
+ }
+
+ // B/C: If defer and async is false or empty don't render the attribute.
+ if (\in_array($attrib, array('defer', 'async')) && !$value)
+ {
+ continue;
+ }
+
+ // Don't add type attribute if document is HTML5 and it's a default mime type. 'mime' is for B/C.
+ if ($attrib === 'mime')
+ {
+ $attrib = 'type';
+ }
+ // B/C defer and async can be set to yes when using the old method.
+ elseif (\in_array($attrib, array('defer', 'async')) && $value === true)
+ {
+ $value = $attrib;
+ }
+
+ // Add attribute to script tag output.
+ $buffer .= ' ' . htmlspecialchars($attrib, ENT_COMPAT, 'UTF-8');
+
+ if (!($this->_doc->isHtml5() && \in_array($attrib, $html5NoValueAttributes)))
+ {
+ // Json encode value if it's an array.
+ $value = !is_scalar($value) ? json_encode($value) : $value;
+
+ $buffer .= '="' . htmlspecialchars($value, ENT_COMPAT, 'UTF-8') . '"';
+ }
+ }
+
+ return $buffer;
}
}
diff --git a/libraries/src/Document/Renderer/Html/StylesRenderer.php b/libraries/src/Document/Renderer/Html/StylesRenderer.php
index 2fe4ddd722ca6..7d34872664309 100644
--- a/libraries/src/Document/Renderer/Html/StylesRenderer.php
+++ b/libraries/src/Document/Renderer/Html/StylesRenderer.php
@@ -21,6 +21,15 @@
*/
class StylesRenderer extends DocumentRenderer
{
+ /**
+ * List of already rendered src
+ *
+ * @var array
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ private $renderedSrc = [];
+
/**
* Renders the document stylesheets and style tags and returns the results as a string
*
@@ -34,177 +43,278 @@ class StylesRenderer extends DocumentRenderer
*/
public function render($head, $params = array(), $content = null)
{
- // Get line endings
- $lnEnd = $this->_doc->_getLineEnd();
$tab = $this->_doc->_getTab();
- $tagEnd = ' />';
$buffer = '';
- $mediaVersion = $this->_doc->getMediaVersion();
$wam = $this->_doc->getWebAssetManager();
$assets = $wam->getAssets('style', true);
- $assets = array_merge(array_values($assets), $this->_doc->_styleSheets);
- $renderedUrls = [];
- $defaultCssMimes = array('text/css');
+ // Get a list of inline assets and their relation with regular assets
+ $inlineAssets = $wam->filterOutInlineAssets($assets);
+ $inlineRelation = $wam->getInlineRelation($inlineAssets);
+
+ // Merge with existing styleSheets, for rendering
+ $assets = array_merge(array_values($assets), $this->_doc->_styleSheets);
// Generate stylesheet links
foreach ($assets as $key => $item)
{
$asset = $item instanceof WebAssetItemInterface ? $item : null;
- if ($asset)
+ // Add href attribute for non Asset item
+ if (!$asset)
{
- $src = $asset->getUri();
+ $item['href'] = $key;
+ }
- if (!$src)
+ // Check for inline content "before"
+ if ($asset && !empty($inlineRelation[$asset->getName()]['before']))
+ {
+ foreach ($inlineRelation[$asset->getName()]['before'] as $itemBefore)
{
- continue;
- }
+ $buffer .= $this->renderInlineElement($itemBefore);
- $attribs = $asset->getAttributes();
- $version = $asset->getVersion();
- $conditional = $asset->getOption('conditional');
+ // Remove this item from inline queue
+ unset($inlineAssets[$itemBefore->getName()]);
+ }
}
- else
- {
- $src = $key;
- $attribs = $item;
- $version = isset($attribs['options']['version']) ? $attribs['options']['version'] : '';
- // Check if stylesheet uses IE conditional statements.
- $conditional = !empty($attribs['options']['conditional']) ? $attribs['options']['conditional'] : null;
- }
+ $buffer .= $this->renderElement($item);
- // Prevent double rendering
- if (!empty($renderedUrls[$src]))
+ // Check for inline content "after"
+ if ($asset && !empty($inlineRelation[$asset->getName()]['after']))
{
- continue;
+ foreach ($inlineRelation[$asset->getName()]['after'] as $itemBefore)
+ {
+ $buffer .= $this->renderInlineElement($itemBefore);
+
+ // Remove this item from inline queue
+ unset($inlineAssets[$itemBefore->getName()]);
+ }
}
+ }
- $renderedUrls[$src] = true;
+ // Generate script declarations for assets
+ foreach ($inlineAssets as $item)
+ {
+ $buffer .= $this->renderInlineElement($item);
+ }
- // Check if script uses media version.
- if ($version && strpos($src, '?') === false && ($mediaVersion || $version !== 'auto'))
+ // Generate stylesheet declarations
+ foreach ($this->_doc->_style as $type => $contents)
+ {
+ // Test for B.C. in case someone still store stylesheet declarations as single string
+ if (\is_string($contents))
{
- $src .= '?' . ($version === 'auto' ? $mediaVersion : $version);
+ $contents = [$contents];
}
- $buffer .= $tab;
-
- // This is for IE conditional statements support.
- if (!\is_null($conditional))
+ foreach ($contents as $content)
{
- $buffer .= '';
- }
+ // Render the element with attributes
+ $buffer .= 'renderAttributes($attribs);
+ $buffer .= ' />';
- $buffer .= $lnEnd;
+ // This is for IE conditional statements support.
+ if (!\is_null($conditional))
+ {
+ $buffer .= '';
}
- // Generate stylesheet declarations
- foreach ($this->_doc->_style as $type => $contents)
- {
- // Test for B.C. in case someone still store stylesheet declarations as single string
- if (\is_string($contents))
- {
- $contents = [$contents];
- }
+ $buffer .= $lnEnd;
- foreach ($contents as $content)
- {
- $buffer .= $tab . '' . $lnEnd;
- }
+ // This is for full XHTML support.
+ if ($this->_doc->_mime != 'text/html')
+ {
+ $buffer .= $tab . $tab . '/*_doc->getScriptOptions();
+ $buffer .= $content . $lnEnd;
+
+ // See above note
+ if ($this->_doc->_mime != 'text/html')
+ {
+ $buffer .= $tab . $tab . '/*]]>*/' . $lnEnd;
+ }
+
+ $buffer .= $tab . '' . $lnEnd;
+
+ return $buffer;
+ }
+
+ /**
+ * Renders the element attributes
+ *
+ * @param array $attributes The element attributes
+ *
+ * @return string The attributes string
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ private function renderAttributes(array $attributes) : string
+ {
+ $buffer = '';
+
+ $defaultCssMimes = array('text/css');
- if (!empty($scriptOptions))
+ foreach ($attributes as $attrib => $value)
{
- $nonce = '';
+ // Don't add the 'options' attribute. This attribute is for internal use (version, conditional, etc).
+ if ($attrib === 'options' || $attrib === 'href')
+ {
+ continue;
+ }
- if ($this->_doc->cspNonce)
+ // Don't add type attribute if document is HTML5 and it's a default mime type. 'mime' is for B/C.
+ if (\in_array($attrib, array('type', 'mime')) && $this->_doc->isHtml5() && \in_array($value, $defaultCssMimes))
{
- $nonce = ' nonce="' . $this->_doc->cspNonce . '"';
+ continue;
}
- $buffer .= $tab . '' . $lnEnd;
+ $buffer .= '="' . htmlspecialchars($value, ENT_COMPAT, 'UTF-8') . '"';
}
- return ltrim($buffer, $tab);
+ return $buffer;
}
}
diff --git a/libraries/src/WebAsset/WebAssetManager.php b/libraries/src/WebAsset/WebAssetManager.php
index 1e0f2d05fe953..ab6027c6aac56 100644
--- a/libraries/src/WebAsset/WebAssetManager.php
+++ b/libraries/src/WebAsset/WebAssetManager.php
@@ -22,11 +22,13 @@
* @method WebAssetManager registerAndUseStyle(WebAssetItem|string $asset, string $uri = '', $options = [], $attributes = [], $dependencies = [])
* @method WebAssetManager useStyle($name)
* @method WebAssetManager disableStyle($name)
+ * @method WebAssetManager addInlineStyle(WebAssetItem|string $content, $options = [], $attributes = [], $dependencies = [])
*
* @method WebAssetManager registerScript(WebAssetItem|string $asset, string $uri = '', $options = [], $attributes = [], $dependencies = [])
* @method WebAssetManager registerAndUseScript(WebAssetItem|string $asset, string $uri = '', $options = [], $attributes = [], $dependencies = [])
* @method WebAssetManager useScript($name)
* @method WebAssetManager disableScript($name)
+ * @method WebAssetManager addInlineScript(WebAssetItem|string $content, $options = [], $attributes = [], $dependencies = [])
*
* @method WebAssetManager registerPreset(WebAssetItem|string $asset, string $uri = '', $options = [], $attributes = [], $dependencies = [])
* @method WebAssetManager registerAndUsePreset(WebAssetItem|string $asset, string $uri = '', $options = [], $attributes = [], $dependencies = [])
@@ -165,9 +167,11 @@ public function getRegistry(): WebAssetRegistry
*/
public function __call($method, $arguments)
{
+ $method = strtolower($method);
+
if (0 === strpos($method, 'use'))
{
- $type = strtolower(substr($method, 3));
+ $type = substr($method, 3);
if (empty($arguments[0]))
{
@@ -177,9 +181,21 @@ public function __call($method, $arguments)
return $this->useAsset($type, $arguments[0]);
}
+ if (0 === strpos($method, 'addinline'))
+ {
+ $type = substr($method, 9);
+
+ if (empty($arguments[0]))
+ {
+ throw new \BadMethodCallException('A content are required');
+ }
+
+ return $this->addInline($type, ...$arguments);
+ }
+
if (0 === strpos($method, 'disable'))
{
- $type = strtolower(substr($method, 7));
+ $type = substr($method, 7);
if (empty($arguments[0]))
{
@@ -192,10 +208,10 @@ public function __call($method, $arguments)
if (0 === strpos($method, 'register'))
{
// Check for registerAndUse
- $andUse = strtolower(substr($method, 8, 6)) === 'anduse';
+ $andUse = substr($method, 8, 6) === 'anduse';
// Extract the type
- $type = $andUse ? strtolower(substr($method, 14)) : strtolower(substr($method, 8));
+ $type = $andUse ? substr($method, 14) : substr($method, 8);
if (empty($arguments[0]))
{
@@ -458,7 +474,7 @@ public function isAssetActive(string $type, string $name): bool
}
/**
- * Check whether the asset exists in the registry.
+ * Helper method to check whether the asset exists in the registry.
*
* @param string $type Asset type, script or style
* @param string $name Asset name
@@ -509,7 +525,24 @@ public function registerAsset(string $type, $asset, string $uri = '', array $opt
}
/**
- * Get all active assets
+ * Helper method to get the asset from the registry.
+ *
+ * @param string $type Asset type, script or style
+ * @param string $name Asset name
+ *
+ * @return WebAssetItemInterface
+ *
+ * @throws UnknownAssetException When Asset cannot be found
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ public function getAsset(string $type, string $name): WebAssetItemInterface
+ {
+ return $this->registry->get($type, $name);
+ }
+
+ /**
+ * Get all active assets, optionally sort them to follow the dependency Graph
*
* @param string $type The asset type, script or style
* @param bool $sort Whether need to sort the assets to follow the dependency Graph
@@ -552,6 +585,127 @@ public function getAssets(string $type, bool $sort = false): array
return $assets;
}
+ /**
+ * Helper method to calculate inline to non inline relation (before/after positions).
+ * Return associated array, which contain dependency (handle) name as key, and list of inline items for each position.
+ * Example: ['handle.name' => ['before' => ['inline1', 'inline2'], 'after' => ['inline3', 'inline4']]]
+ *
+ * Note: If inline asset have a multiple dependencies, then will be used last one from the list for positioning
+ *
+ * @param WebAssetItem[] $assets The assets list
+ *
+ * @return array
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ public function getInlineRelation(array $assets): array
+ {
+ $inlineRelation = [];
+
+ // Find an inline assets and their relations to non inline
+ foreach ($assets as $k => $asset)
+ {
+ if (!$asset->getOption('inline'))
+ {
+ continue;
+ }
+
+ // Add to list of inline assets
+ $inlineAssets[$asset->getName()] = $asset;
+
+ // Check whether position are requested with dependencies
+ $position = $asset->getOption('position');
+ $position = $position === 'before' || $position === 'after' ? $position : null;
+ $deps = $asset->getDependencies();
+
+ if ($position && $deps)
+ {
+ // If inline asset have a multiple dependencies, then use last one from the list for positioning
+ $handle = end($deps);
+ $inlineRelation[$handle][$position][$asset->getName()] = $asset;
+ }
+ }
+
+ return $inlineRelation;
+ }
+
+ /**
+ * Helper method to filter an inline assets
+ *
+ * @param WebAssetItem[] $assets Reference to a full list of active assets
+ *
+ * @return WebAssetItem[] Array of inline assets
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ public function filterOutInlineAssets(array &$assets): array
+ {
+ $inlineAssets = [];
+
+ foreach ($assets as $k => $asset)
+ {
+ if (!$asset->getOption('inline'))
+ {
+ continue;
+ }
+
+ // Remove inline assets from assets list, and add to list of inline
+ unset($assets[$k]);
+
+ $inlineAssets[$asset->getName()] = $asset;
+ }
+
+ return $inlineAssets;
+ }
+
+ /**
+ * Add a new inline content asset.
+ * Allow to register WebAssetItem instance in the registry, by call addInline($type, $assetInstance)
+ * Or create an asset on fly (from name and Uri) and register in the registry, by call addInline($type, $content, $options ....)
+ *
+ * @param string $type The asset type, script or style
+ * @param WebAssetItem|string $content The content to of inline asset
+ * @param array $options Additional options for the asset
+ * @param array $attributes Attributes for the asset
+ * @param array $dependencies Asset dependencies
+ *
+ * @return self
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ public function addInline(string $type, string $content, array $options = [], array $attributes = [], array $dependencies = []): self
+ {
+ if ($content instanceof WebAssetItemInterface)
+ {
+ $assetInstance = $content;
+ }
+ elseif (is_string($content))
+ {
+ $name = $options['name'] ?? ('inline.' . md5($content));
+ $assetInstance = $this->registry->createAsset($name, '', $options, $attributes, $dependencies);
+ }
+ else
+ {
+ throw new \BadMethodCallException('The $content variable should be either WebAssetItemInterface or a string');
+ }
+
+ // Get the name
+ $asset = $assetInstance->getName();
+
+ // Set required options
+ $assetInstance->setOption('type', $type);
+ $assetInstance->setOption('inline', true);
+ $assetInstance->setOption('content', $content);
+
+ // Add to registry
+ $this->registry->add($type, $assetInstance);
+
+ // And make active
+ $this->useAsset($type, $asset);
+
+ return $this;
+ }
+
/**
* Lock the manager to prevent further modifications
*