asset.php 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. <?php namespace Laravel;
  2. class Asset {
  3. /**
  4. * All of the instantiated asset containers.
  5. *
  6. * @var array
  7. */
  8. protected static $containers = array();
  9. /**
  10. * Get an asset container instance.
  11. *
  12. * @param string $container
  13. * @return Asset_Container
  14. */
  15. public static function container($container = 'default')
  16. {
  17. if ( ! isset(static::$containers[$container]))
  18. {
  19. static::$containers[$container] = new Asset_Container($container);
  20. }
  21. return static::$containers[$container];
  22. }
  23. /**
  24. * Magic Method for calling methods on the default Asset container.
  25. */
  26. public static function __callStatic($method, $parameters)
  27. {
  28. return call_user_func_array(array(static::container(), $method), $parameters);
  29. }
  30. }
  31. class Asset_Container {
  32. /**
  33. * The asset container name.
  34. *
  35. * @var string
  36. */
  37. public $name;
  38. /**
  39. * All of the registered assets.
  40. *
  41. * @var array
  42. */
  43. public $assets = array();
  44. /**
  45. * Create a new asset container instance.
  46. *
  47. * @param string $name
  48. * @param HTML $html
  49. * @return void
  50. */
  51. public function __construct($name)
  52. {
  53. $this->name = $name;
  54. }
  55. /**
  56. * Add an asset to the container.
  57. *
  58. * The extension of the asset source will be used to determine the type of
  59. * asset being registered (CSS or JavaScript). If you are using a non-standard
  60. * extension, you may use the style or script methods to register assets.
  61. *
  62. * @param string $name
  63. * @param string $source
  64. * @param array $dependencies
  65. * @param array $attributes
  66. * @return void
  67. */
  68. public function add($name, $source, $dependencies = array(), $attributes = array())
  69. {
  70. $type = (pathinfo($source, PATHINFO_EXTENSION) == 'css') ? 'style' : 'script';
  71. return call_user_func(array($this, $type), $name, $source, $dependencies, $attributes);
  72. }
  73. /**
  74. * Add a CSS file to the registered assets.
  75. *
  76. * @param string $name
  77. * @param string $source
  78. * @param array $dependencies
  79. * @param array $attributes
  80. * @return Asset_Container
  81. */
  82. public function style($name, $source, $dependencies = array(), $attributes = array())
  83. {
  84. if ( ! array_key_exists('media', $attributes))
  85. {
  86. $attributes['media'] = 'all';
  87. }
  88. $this->register('style', $name, $source, $dependencies, $attributes);
  89. return $this;
  90. }
  91. /**
  92. * Add a JavaScript file to the registered assets.
  93. *
  94. * @param string $name
  95. * @param string $source
  96. * @param array $dependencies
  97. * @param array $attributes
  98. * @return Asset_Container
  99. */
  100. public function script($name, $source, $dependencies = array(), $attributes = array())
  101. {
  102. $this->register('script', $name, $source, $dependencies, $attributes);
  103. return $this;
  104. }
  105. /**
  106. * Add an asset to the array of registered assets.
  107. *
  108. * @param string $type
  109. * @param string $name
  110. * @param string $source
  111. * @param array $dependencies
  112. * @param array $attributes
  113. * @return void
  114. */
  115. protected function register($type, $name, $source, $dependencies, $attributes)
  116. {
  117. $dependencies = (array) $dependencies;
  118. $this->assets[$type][$name] = compact('source', 'dependencies', 'attributes');
  119. }
  120. /**
  121. * Get the links to all of the registered CSS assets.
  122. *
  123. * @return string
  124. */
  125. public function styles()
  126. {
  127. return $this->group('style');
  128. }
  129. /**
  130. * Get the links to all of the registered JavaScript assets.
  131. *
  132. * @return string
  133. */
  134. public function scripts()
  135. {
  136. return $this->group('script');
  137. }
  138. /**
  139. * Get all of the registered assets for a given type / group.
  140. *
  141. * @param string $group
  142. * @return string
  143. */
  144. protected function group($group)
  145. {
  146. if ( ! isset($this->assets[$group]) or count($this->assets[$group]) == 0) return '';
  147. $assets = '';
  148. foreach ($this->arrange($this->assets[$group]) as $name => $data)
  149. {
  150. $assets .= $this->asset($group, $name);
  151. }
  152. return $assets;
  153. }
  154. /**
  155. * Get the HTML link to a registered asset.
  156. *
  157. * @param string $group
  158. * @param string $name
  159. * @return string
  160. */
  161. protected function asset($group, $name)
  162. {
  163. if ( ! isset($this->assets[$group][$name])) return '';
  164. $asset = $this->assets[$group][$name];
  165. return HTML::$group($asset['source'], $asset['attributes']);
  166. }
  167. /**
  168. * Sort and retrieve assets based on their dependencies
  169. *
  170. * @param array $assets
  171. * @return array
  172. */
  173. protected function arrange($assets)
  174. {
  175. list($original, $sorted) = array($assets, array());
  176. while (count($assets) > 0)
  177. {
  178. foreach ($assets as $asset => $value)
  179. {
  180. $this->evaluate_asset($asset, $value, $original, $sorted, $assets);
  181. }
  182. }
  183. return $sorted;
  184. }
  185. /**
  186. * Evaluate an asset and its dependencies.
  187. *
  188. * @param string $asset
  189. * @param string $value
  190. * @param array $original
  191. * @param array $sorted
  192. * @param array $assets
  193. * @return void
  194. */
  195. protected function evaluate_asset($asset, $value, $original, &$sorted, &$assets)
  196. {
  197. // If the asset has no more dependencies, we can add it to the sorted list
  198. // and remove it from the array of assets. Otherwise, we will not verify
  199. // the asset's dependencies and determine if they have already been sorted.
  200. if (count($assets[$asset]['dependencies']) == 0)
  201. {
  202. $sorted[$asset] = $value;
  203. unset($assets[$asset]);
  204. }
  205. else
  206. {
  207. foreach ($assets[$asset]['dependencies'] as $key => $dependency)
  208. {
  209. if ( ! $this->dependency_is_valid($asset, $dependency, $original, $assets))
  210. {
  211. unset($assets[$asset]['dependencies'][$key]);
  212. continue;
  213. }
  214. // If the dependency has not yet been added to the sorted list, we can not
  215. // remove it from this asset's array of dependencies. We'll try again on
  216. // the next trip through the loop.
  217. if ( ! isset($sorted[$dependency])) continue;
  218. unset($assets[$asset]['dependencies'][$key]);
  219. }
  220. }
  221. }
  222. /**
  223. * Verify that an asset's dependency is valid.
  224. *
  225. * A dependency is considered valid if it exists, is not a circular reference, and is
  226. * not a reference to the owning asset itself.
  227. *
  228. * @param string $asset
  229. * @param string $dependency
  230. * @param array $original
  231. * @param array $assets
  232. * @return bool
  233. */
  234. protected function dependency_is_valid($asset, $dependency, $original, $assets)
  235. {
  236. if ( ! isset($original[$dependency])) return false;
  237. if ($dependency === $asset)
  238. {
  239. throw new \Exception("Asset [$asset] is dependent on itself.");
  240. }
  241. elseif (isset($assets[$dependency]) and in_array($asset, $assets[$dependency]['dependencies']))
  242. {
  243. throw new \Exception("Assets [$asset] and [$dependency] have a circular dependency.");
  244. }
  245. }
  246. }