Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

357 lines (312 sloc) 8.398 kb
<?php namespace Laravel;
class Asset {
/**
* All of the instantiated asset containers.
*
* @var array
*/
public static $containers = array();
/**
* Get an asset container instance.
*
* <code>
* // Get the default asset container
* $container = Asset::container();
*
* // Get a named asset container
* $container = Asset::container('footer');
* </code>
*
* @param string $container
* @return Asset_Container
*/
public static function container($container = 'default')
{
if ( ! isset(static::$containers[$container]))
{
static::$containers[$container] = new Asset_Container($container);
}
return static::$containers[$container];
}
/**
* Magic Method for calling methods on the default container.
*
* <code>
* // Call the "styles" method on the default container
* echo Asset::styles();
*
* // Call the "add" method on the default container
* Asset::add('jquery', 'js/jquery.js');
* </code>
*/
public static function __callStatic($method, $parameters)
{
return call_user_func_array(array(static::container(), $method), $parameters);
}
}
class Asset_Container {
/**
* The asset container name.
*
* @var string
*/
public $name;
/**
* The bundle that the assets belong to.
*
* @var string
*/
public $bundle = DEFAULT_BUNDLE;
/**
* All of the registered assets.
*
* @var array
*/
public $assets = array();
/**
* Create a new asset container instance.
*
* @param string $name
* @return void
*/
public function __construct($name)
{
$this->name = $name;
}
/**
* 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.
*
* <code>
* // 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, array('defer'));
* </code>
*
* @param string $name
* @param string $source
* @param array $dependencies
* @param array $attributes
* @return void
*/
public function add($name, $source, $dependencies = array(), $attributes = array())
{
$type = (pathinfo($source, PATHINFO_EXTENSION) == 'css') ? 'style' : 'script';
return $this->$type($name, $source, $dependencies, $attributes);
}
/**
* Add a CSS file to the registered assets.
*
* @param string $name
* @param string $source
* @param array $dependencies
* @param array $attributes
* @return Asset_Container
*/
public function style($name, $source, $dependencies = array(), $attributes = array())
{
if ( ! array_key_exists('media', $attributes))
{
$attributes['media'] = 'all';
}
$this->register('style', $name, $source, $dependencies, $attributes);
return $this;
}
/**
* Add a JavaScript file to the registered assets.
*
* @param string $name
* @param string $source
* @param array $dependencies
* @param array $attributes
* @return Asset_Container
*/
public function script($name, $source, $dependencies = array(), $attributes = array())
{
$this->register('script', $name, $source, $dependencies, $attributes);
return $this;
}
/**
* Returns the full-path for an asset.
*
* @param string $source
* @return string
*/
public function path($source)
{
return Bundle::assets($this->bundle).$source;
}
/**
* Set the bundle that the container's assets belong to.
*
* @param string $bundle
* @return Asset_Container
*/
public function bundle($bundle)
{
$this->bundle = $bundle;
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');
}
/**
* Get the links to all of the registered CSS assets.
*
* @return string
*/
public function styles()
{
return $this->group('style');
}
/**
* Get the links to all of the registered JavaScript assets.
*
* @return string
*/
public function scripts()
{
return $this->group('script');
}
/**
* Get all of the registered assets for a given type / group.
*
* @param string $group
* @return string
*/
protected function group($group)
{
if ( ! isset($this->assets[$group]) or count($this->assets[$group]) == 0) return '';
$assets = '';
foreach ($this->arrange($this->assets[$group]) as $name => $data)
{
$assets .= $this->asset($group, $name);
}
return $assets;
}
/**
* 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']);
}
return HTML::$group($asset['source'], $asset['attributes']);
}
/**
* Sort and retrieve assets based on their dependencies
*
* @param array $assets
* @return array
*/
protected function arrange($assets)
{
list($original, $sorted) = array($assets, array());
while (count($assets) > 0)
{
foreach ($assets as $asset => $value)
{
$this->evaluate_asset($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
*/
protected function evaluate_asset($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->dependency_is_valid($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
*/
protected function dependency_is_valid($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]) and in_array($asset, $assets[$dependency]['dependencies']))
{
throw new \Exception("Assets [$asset] and [$dependency] have a circular dependency.");
}
return true;
}
}
Jump to Line
Something went wrong with that request. Please try again.