name = $name;
}
/**
* Root asset path.
*
* @param string $uri
* @return string
*/
public function originUrl($uri)
{
return $this->configAssetUrl($uri);
}
/**
* Generate a URL to an application asset.
*
* @param string $path
* @return string
*/
protected function configAssetUrl($path)
{
return asset($path);
}
/**
* Return asset path with current theme path.
*
* @param string $uri
* @return string
*/
public function url($uri)
{
// If path is full, so we just return.
if (preg_match('#^http|//:#', $uri)) {
return $uri;
}
$path = $this->getCurrentPath() . $uri;
return $this->configAssetUrl($path);
}
/**
* Get path from asset.
*
* @return string
*/
public function getCurrentPath()
{
return Asset::$path;
}
/**
* Add an asset to the container.
*
* The extension of the asset source will be used to determine the type of
* asset being registered (CSS or JavaScript). When using a non-standard
* extension, the style/script methods may be used to register assets.
*
*
* // Add an asset to the container
* Asset::container()->add('jquery', 'js/jquery.js');
*
* // Add an asset that has dependencies on other assets
* Asset::add('jquery', 'js/jquery.js', 'jquery-ui');
*
* // Add an asset that should have attributes applied to its tags
* Asset::add('jquery', 'js/jquery.js', null, ['defer']);
*
*
* @param string $name
* @param string $source
* @param array $dependencies
* @param array $attributes
* @param null $version
* @return AssetContainer
*/
public function add($name, $source, $dependencies = [], $attributes = [], $version = null)
{
if (is_array($source)) {
foreach ($source as $path) {
$name = $name . '-' . md5($path);
$this->add($name, $path, $dependencies, $attributes);
}
return $this;
}
$type = File::extension($source) == 'css' ? 'style' : 'script';
// Remove unnecessary slashes from internal path.
if (!preg_match('|^//|', $source)) {
$source = ltrim($source, '/');
}
if ($version) {
$source .= '?v=' . $version;
}
return $this->$type($name, $source, $dependencies, $attributes);
}
/**
* @param string|array $name
* @return $this
*/
public function remove($name): self
{
if (!is_array($name)) {
$name = [$name];
}
foreach ($name as $item) {
foreach ($this->assets as $typeKey => $type) {
foreach ($type as $assetKey => $asset) {
if ($assetKey == $item) {
Arr::forget($this->assets, $typeKey . '.' . $assetKey);
break;
}
}
}
}
return $this;
}
/**
* Write a script to the container.
*
* @param string $name
* @param string $source
* @param array $dependencies
* @return AssetContainer
*/
public function writeScript($name, $source, $dependencies = [])
{
$source = '';
return $this->write($name, 'script', $source, $dependencies);
}
/**
* Write a content to the container.
*
* @param string $name
* @param string $type
* @param string $source
* @param array $dependencies
* @return AssetContainer
*/
protected function write($name, $type, $source, $dependencies = [])
{
$types = [
'script' => 'script',
'style' => 'style',
'js' => 'script',
'css' => 'style',
];
if (array_key_exists($type, $types)) {
$type = $types[$type];
$this->register($type, $name, $source, $dependencies, []);
}
return $this;
}
/**
* Add an asset to the array of registered assets.
*
* @param string $type
* @param string $name
* @param string $source
* @param array $dependencies
* @param array $attributes
* @return void
*/
protected function register($type, $name, $source, $dependencies, $attributes)
{
$dependencies = (array)$dependencies;
$attributes = (array)$attributes;
$this->assets[$type][$name] = compact('source', 'dependencies', 'attributes');
}
/**
* Write a style to the container.
*
* @param string $name
* @param string $source
* @param array $dependencies
* @return AssetContainer
*/
public function writeStyle($name, $source, $dependencies = [])
{
$source = '';
return $this->write($name, 'style', $source, $dependencies);
}
/**
* Write a content without tag wrapper.
*
* @param string $name
* @param string $source
* @param array $dependencies
* @return AssetContainer
*/
public function writeContent($name, $source, $dependencies = [])
{
return $this->write($name, 'script', $source, $dependencies);
}
/**
* Add a CSS file to the registered assets.
*
* @param string $name
* @param string $source
* @param array $dependencies
* @param array $attributes
* @param null $version
* @return AssetContainer
*/
public function style($name, $source, $dependencies = [], $attributes = [])
{
if (!array_key_exists('media', $attributes)) {
$attributes['media'] = 'all';
}
// Prepend path to theme.
if ($this->isUsePath()) {
$source = $this->evaluatePath($this->getCurrentPath() . $source);
// Reset using path.
$this->usePath(false);
}
$this->register('style', $name, $source, $dependencies, $attributes);
return $this;
}
/**
* Check using theme path.
*
* @return boolean
*/
public function isUsePath()
{
return (boolean)$this->usePath;
}
/**
* Evaluate path to current theme or force use theme.
*
* @param string $source
* @return string
*/
protected function evaluatePath($source)
{
static $theme;
// Make theme to use few features.
if (!$theme) {
$theme = app('theme');
}
// Switch path to another theme.
if (!is_bool($this->usePath) && $theme->exists($this->usePath)) {
$currentTheme = $theme->getThemeName();
$source = str_replace($currentTheme, $this->usePath, $source);
}
return $source;
}
/**
* Force use a theme path.
*
* @param boolean $use
* @return AssetContainer
*/
public function usePath($use = true)
{
$this->usePath = $use;
return $this;
}
/**
* Add a JavaScript file to the registered assets.
*
* @param string $name
* @param string $source
* @param array $dependencies
* @param array $attributes
* @return AssetContainer
*/
public function script($name, $source, $dependencies = [], $attributes = [])
{
// Prepend path to theme.
if ($this->isUsePath()) {
$source = $this->evaluatePath($this->getCurrentPath() . $source);
// Reset using path.
$this->usePath(false);
}
$this->register('script', $name, $source, $dependencies, $attributes);
return $this;
}
/**
* Get the links to all of the registered CSS assets.
*
* @return string
* @throws Exception
*/
public function styles()
{
return $this->group('style');
}
/**
* Get all of the registered assets for a given type / group.
*
* @param string $group
* @return string
* @throws Exception
*/
protected function group($group)
{
if (!isset($this->assets[$group]) || count($this->assets[$group]) == 0) {
return '';
}
$assets = '';
foreach (array_keys($this->arrange($this->assets[$group])) as $name) {
$assets .= $this->asset($group, $name);
}
return $assets;
}
/**
* Sort and retrieve assets based on their dependencies
*
* @param array $assets
* @return array
* @throws Exception
*/
protected function arrange($assets)
{
[$original, $sorted] = [$assets, []];
while (count($assets) > 0) {
foreach ($assets as $asset => $value) {
$this->evaluateAsset($asset, $value, $original, $sorted, $assets);
}
}
return $sorted;
}
/**
* Evaluate an asset and its dependencies.
*
* @param string $asset
* @param string $value
* @param array $original
* @param array $sorted
* @param array $assets
* @return void
* @throws Exception
*/
protected function evaluateAsset($asset, $value, $original, &$sorted, &$assets)
{
// If the asset has no more dependencies, we can add it to the sorted list
// and remove it from the array of assets. Otherwise, we will not verify
// the asset's dependencies and determine if they've been sorted.
if (count($assets[$asset]['dependencies']) == 0) {
$sorted[$asset] = $value;
unset($assets[$asset]);
} else {
foreach ($assets[$asset]['dependencies'] as $key => $dependency) {
if (!$this->dependencyIsValid($asset, $dependency, $original, $assets)) {
unset($assets[$asset]['dependencies'][$key]);
continue;
}
// If the dependency has not yet been added to the sorted list, we can not
// remove it from this asset's array of dependencies. We'll try again on
// the next trip through the loop.
if (!isset($sorted[$dependency])) {
continue;
}
unset($assets[$asset]['dependencies'][$key]);
}
}
}
/**
* Verify that an asset's dependency is valid.
* A dependency is considered valid if it exists, is not a circular reference, and is
* not a reference to the owning asset itself. If the dependency doesn't exist, no
* error or warning will be given. For the other cases, an exception is thrown.
*
* @param string $asset
* @param string $dependency
* @param array $original
* @param array $assets
*
* @return bool
* @throws Exception
*/
protected function dependencyIsValid($asset, $dependency, $original, $assets)
{
if (!isset($original[$dependency])) {
return false;
} elseif ($dependency === $asset) {
throw new Exception('Asset [' . $asset . '] is dependent on itself.');
} elseif (isset($assets[$dependency]) && in_array($asset, $assets[$dependency]['dependencies'])) {
throw new Exception('Assets [' . $asset . '] and [' . $dependency . '] have a circular dependency.');
}
return true;
}
/**
* Get the HTML link to a registered asset.
*
* @param string $group
* @param string $name
* @return string
*/
protected function asset($group, $name)
{
if (!isset($this->assets[$group][$name])) {
return '';
}
$asset = $this->assets[$group][$name];
// If the bundle source is not a complete URL, we will go ahead and prepend
// the bundle's asset path to the source provided with the asset. This will
// ensure that we attach the correct path to the asset.
if (filter_var($asset['source'], FILTER_VALIDATE_URL) === false) {
$asset['source'] = $this->path($asset['source']);
}
// If source is not a path to asset, render without wrap a HTML.
if (strpos($asset['source'], '<') !== false) {
return $asset['source'];
}
// This line fixing config path.
$asset['source'] = $this->configAssetUrl($asset['source']);
return $this->html($group, $asset['source'], $asset['attributes']);
}
/**
* Returns the full-path for an asset.
*
* @param string $source
* @return string
*/
public function path($source)
{
return $source;
}
/**
* Render asset as HTML.
*
* @param string $group
* @param string $source
* @param array $attributes
* @return string
*/
public function html($group, $source, $attributes)
{
switch ($group) {
case 'script':
$attributes['src'] = $source;
return '' . PHP_EOL;
case 'style':
$defaults = ['media' => 'all', 'type' => 'text/css', 'rel' => 'stylesheet'];
$attributes = $attributes + $defaults;
$attributes['href'] = $source;
return 'attributes($attributes) . '>' . PHP_EOL;
}
return null;
}
/**
* Build an HTML attribute string from an array.
*
* @param array $attributes
* @return string
*/
public function attributes($attributes)
{
$html = [];
// For numeric keys we will assume that the key and the value are the same
// as this will convert HTML attributes such as "required" to a correct
// form like required="required" instead of using incorrect numeric.
foreach ((array)$attributes as $key => $value) {
$element = $this->attributeElement($key, $value);
if (!empty($element)) {
$html[] = $element;
}
}
return count($html) > 0 ? ' ' . implode(' ', $html) : '';
}
/**
* Build a single attribute element.
*
* @param string $key
* @param string $value
* @return string
*/
protected function attributeElement($key, $value)
{
if (is_numeric($key)) {
$key = $value;
}
if (!empty($value)) {
return $key . '="' . e($value) . '"';
}
return null;
}
/**
* @param string $group
* @return array
* @throws Exception
*/
public function getAssets($group)
{
if (!isset($this->assets[$group])) {
return [];
}
$assets = [];
foreach (array_keys($this->arrange($this->assets[$group])) as $name) {
$assets[] = $this->assetUrl($group, $name);
}
return $assets;
}
/**
* @param string $group
* @param string $name
* @return string
*/
protected function assetUrl($group, $name)
{
if (!isset($this->assets[$group][$name])) {
return '';
}
$asset = $this->assets[$group][$name];
// If the bundle source is not a complete URL, we will go ahead and prepend
// the bundle's asset path to the source provided with the asset. This will
// ensure that we attach the correct path to the asset.
if (filter_var($asset['source'], FILTER_VALIDATE_URL) === false) {
$asset['source'] = $this->path($asset['source']);
}
// If source is not a path to asset, render without wrap a HTML.
if (strpos($asset['source'], '<') !== false) {
return $asset['source'];
}
return $this->configAssetUrl($asset['source']);
}
/**
* Get the links to all of the registered JavaScript assets.
*
* @return string
* @throws Exception
*/
public function scripts()
{
return $this->group('script');
}
}