Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'release/1.11'

* release/1.11: (40 commits)
  Update changelog
  Bump version to v1.11
  Add comment to readme saying that my repo is likely to be more up-to-date than fuel's fork
  Talk about forum thread in readme
  Change defaults for min, combine, show_files
  Document the new functions
  Add functions to set group options on-the-fly
  Correct example syntax for inlining
  Make add_group_base accept a single string dep, and document this
  Document the dependencies
  resolve_deps was enabling groups that should have been disabled
  Add functions to add deps to groups
  Create an array of rendered groups, and use it to prevent us from rendering groups both as a dep and as normal. Also enable disbled groups when they're deps.
  Move dep-checking to its own function, and check for circular references.
  Deps can now be defined and processed.
  Document the changes in the readme
  Deprecate the  option to render(), and add it to the group config instead
  Document new inline behaviour in the readme
  Css uris are rewritten, even if the file is inline and isn't minified
  Make 'inline' a group attribute, rather than something passed to Casset::render()
  ...
  • Loading branch information...
commit 4340eddd907fe09a4acc7ef4cd42ee69016cc3b7 2 parents 4314975 + 550d73e
@canton7 authored
View
2  bootstrap.php
@@ -4,7 +4,7 @@
* Casset: Convenient asset library for FuelPHP.
*
* @package Casset
- * @version v1.10
+ * @version v1.11
* @author Antony Male
* @license MIT License
* @copyright 2011 Antony Male
View
14 changelog.md
@@ -3,6 +3,18 @@ Changelog
This file lists the important changes between versions. For a list of minor changes, check the log.
+v1.11
+-----
+ - Asset URLs can be obtained, see "Getting asset paths / urls".
+ - Casset::add_group() has been re-implemented.
+ - Callbacks now exist: You can use these to process js/css files after they're loaded, but before they're cached. Allows use of SASS, Coffeescript, etc.
+ - Exception now thrown if someone attempts to define a group that already exists.
+ - Group options are now specified using array syntax, instead of separate function arguments.
+ - Option to inline groups moved from Caset::render() to when the group's defined.
+ - Option to add attributes to a group's tag moved from Casset::render() to when the group's defined.
+ - Add dependencies between groups. Rendering a group will automatically render its dependencies.
+ - New functions to allow changing of group options on-the-fly.
+
v1.10
-----
Hotfix release.
@@ -10,7 +22,7 @@ Fixed #3, reported by krtek4, where render_css had an erroneous second loop, mea
v1.9
---
-Hotfix release.
+Hotfix release.
Fixes #1, reported by jaysonic, where add_path was (for some reason) private.
View
476 classes/casset.php
@@ -4,7 +4,7 @@
* Casset: Convenient asset library for FuelPHP.
*
* @package Casset
- * @version v1.10
+ * @version v1.11
* @author Antony Male
* @license MIT License
* @copyright 2011 Antony Male
@@ -72,7 +72,23 @@ class Casset {
/**
* @var bool Whether to combine
*/
- protected static $combine_default = false;
+ protected static $combine_default = true;
+
+ /**
+ * @var bool Whether to render files inline by default.
+ */
+ protected static $inline_default = false;
+
+ /**
+ *
+ * @var array The default attributes when creating the asset's tag.
+ */
+ protected static $attr_default = array();
+
+ /**
+ * @var int How deep to go when resolving deps
+ */
+ protected static $deps_max_depth = 5;
/**
* @var bool Whether to show comments above the <script>/<link> tags showing
@@ -87,6 +103,20 @@ class Casset {
protected static $show_files_inline = false;
/**
+ * @var function If given, the function to call when we've read a file, before
+ * minifying.
+ * Note that it's only called if $combine for the file is true
+ * Prototype: callback(content, filename, type, group_name);
+ */
+ protected static $post_load_callback = null;
+
+ /**
+ * @var array Keeps a record of which groups have been rendered.
+ * We then check this when deciding whether to render a dep.
+ */
+ protected static $rendered_groups = array('js' => array(), 'css' => array());
+
+ /**
* @var bool Wether we've been initialized.
*/
public static $initialized = false;
@@ -124,6 +154,8 @@ public static function _init()
static::$min_default = \Config::get('casset.min', static::$min_default);
static::$combine_default = \Config::get('casset.combine', static::$combine_default);
+ static::$deps_max_depth = \Config::get('casset.deps_max_depth', static::$deps_max_depth);
+
$group_sets = \Config::get('casset.groups', array());
@@ -131,25 +163,28 @@ public static function _init()
{
foreach ($groups as $group_name => $group)
{
- $enabled = array_key_exists('enabled', $group) ? $group['enabled'] : true;
- $combine = array_key_exists('combine', $group) ? $group['combine'] : null;
- $min = array_key_exists('min', $group) ? $group['min'] : null;
- static::add_group($group_type, $group_name, $enabled, $combine, $min);
- foreach ($group['files'] as $files)
- {
- if (!is_array($files))
- $files = array($files, false);
- static::add_asset($group_type, $files[0], $files[1], $group_name);
- }
+ $options = array(
+ 'enabled' => array_key_exists('enabled', $group) ? $group['enabled'] : true,
+ 'combine' => array_key_exists('combine', $group) ? $group['combine'] : static::$combine_default,
+ 'min' => array_key_exists('min', $group) ? $group['min'] : static::$min_default,
+ 'inline' => array_key_exists('inline', $group) ? $group['inline'] : static::$inline_default,
+ 'attr' => array_key_exists('attr', $group) ? $group['attr'] : static::$attr_default,
+ 'deps' => array_key_exists('deps', $group) ? $group['deps'] : array(),
+ );
+ static::add_group($group_type, $group_name, $group['files'], $options);
}
}
static::$show_files = \Config::get('casset.show_files', static::$show_files);
static::$show_files_inline = \Config::get('casset.show_files_inline', static::$show_files_inline);
+ static::$post_load_callback = \Config::get('casset.post_load_callback', static::$post_load_callback);
+
static::$initialized = true;
}
+
+
/**
* Parses oen of the 'paths' config keys into the format used internally.
* Config file format:
@@ -193,7 +228,7 @@ public static function add_path($path_key, $path_attr)
public static function set_path($path_key = 'core')
{
if (!array_key_exists($path_key, static::$asset_paths))
- throw new \Fuel_Exception("Asset path key $path_key doesn't exist");
+ throw new Casset_Exception("Asset path key $path_key doesn't exist");
static::$default_path_key = $path_key;
}
@@ -202,57 +237,70 @@ public static function set_path($path_key = 'core')
*
* @param string $group_type 'js' or 'css'
* @param string $group_name The name of the group
- * @param bool $enabled Whether the group is enabled. Enabled groups will be
- * rendered with render_js / render_css
+ * @param array $options. An array of options. array(
+ * 'enabled' => true/false,
+ * 'combine' => true/false,
+ * 'min' => true/false,
+ * 'inline' => true/false,
+ * 'deps' => array(),
+ * );
*/
- private static function add_group($group_type, $group_name, $enabled = true, $combine = null, $min = null)
+ private static function add_group_base($group_type, $group_name, $options = array())
{
+ // Insert defaults
+ $options = array_merge(array(
+ 'enabled' => true,
+ 'combine' => static::$combine_default,
+ 'min' => static::$min_default,
+ 'inline' => static::$inline_default,
+ 'attr' => static::$attr_default,
+ 'deps' => array(),
+ ), $options);
+ if (!is_array($options['deps']))
+ $options['deps'] = array($options['deps']);
+ $options['files'] = array();
// If it already exists, don't overwrite it
if (array_key_exists($group_name, static::$groups[$group_type]))
- return;
- static::$groups[$group_type][$group_name] = array(
- 'files' => array(),
- 'enabled' => $enabled,
- 'combine' => ($combine === null) ? static::$combine_default : $combine,
- 'min' => ($min === null) ? static::$min_default : $min,
- );
+ throw new Casset_Exception("Group $group_name already exists: can't create it.");
+ static::$groups[$group_type][$group_name] = $options;
}
/**
- * Figures out where a file should be, based on its namespace and type.
+ * Adds a group for assets, and adds assets to that group.
*
- * @param string $file The name of the asset to search for
- * @param string $asset_type 'css', 'js' or 'img'
- * @return string The path to the asset, relative to $asset_url
+ * @param string $group_type 'js' or 'css'
+ * @param string $group_name The name of the group
+ * @param array $options. An array of options. array(
+ * 'enabled' => true/false,
+ * 'combine' => true/false,
+ * 'min' => true/false,
+ * 'inline' => true/false,
+ * 'attr' => array(),
+ * 'deps' => array(),
+ * );
+ * To maintain backwards compatibility, you can also pass $enabled here.
+ * @param bool $combine_dep DEPRECATED. Whether to combine files in this group. Default (null) means use config setting
+ * @param boo $min_dep DEPRECATED/ Whether to minify files in this group. Default (null) means use config setting
*/
- private static function find_files($file, $asset_type)
+ public static function add_group($group_type, $group_name, $files, $options = array(), $combine_dep = null, $min_dep = null)
{
- $parts = explode('::', $file, 2);
- if (!array_key_exists($parts[0], static::$asset_paths))
- throw new \Fuel_Exception("Could not find namespace {$parts[0]}");
-
- $path = static::$asset_paths[$parts[0]]['path'];
- $file = $parts[1];
-
- $folder = static::$asset_paths[$parts[0]]['dirs'][$asset_type];
- $file = ltrim($file, '/');
-
- $remote = (strpos($path, '//') !== false);
-
- if ($remote)
+ // Bit of backwards compatibity.
+ // Order used to be add_group(group_type, group_name, files, enabled, combine, min)
+ if (!is_array($options))
{
- // Glob doesn't work on remote locations, so just assume they
- // specified a file, not a glob pattern.
- // Don't look for the file now either. That'll be done by
- // file_get_contents later on, if need be.
- return array($path.$folder.$file);
+ $options = array(
+ 'enabled' => $options,
+ 'combine' => $combine_dep,
+ 'min' => $min_dep,
+ );
}
- else
- {
- $glob_files = glob($path.$folder.$file);
- if (!$glob_files || !count($glob_files))
- throw new \Fuel_Exception("Found no files matching $path$folder$file");
- return $glob_files;
+ // We're basically faking the old add_group. However, the approach has changed since those days
+ // Therefore we create the group it it doesn't already exist, then add the files to it
+ static::add_group_base($group_type, $group_name, $options);
+ foreach ($files as $file) {
+ if (!is_array($file))
+ $file = array($file, false);
+ static::add_asset($group_type, $file[0], $file[1], $group_name);
}
}
@@ -339,6 +387,58 @@ private static function asset_enabled($type, $groups, $enabled)
}
/**
+ * Set group options on-the-fly.
+ *
+ * @param string $type 'js' / 'css
+ * @param mixed $group_names Group name to change, or array of groups to change,
+ * or '' for global group, or '*' for all groups.
+ * @param string $option_key The name of the option to change
+ * @param mixed $option_value What to set the option to
+ */
+ public static function set_group_option($type, $group_names, $option_key, $option_value)
+ {
+ if ($group_names == '')
+ $group_names = array('global');
+ else if ($group_names == '*')
+ $group_names = array_keys(static::$groups[$type]);
+ else if (!is_array($group_names))
+ $group_names = array($group_names);
+
+ // Allow them to specify a single string dep
+ if ($option_key == 'deps' && !is_array($option_value))
+ $option_value = array($option_value);
+
+ foreach ($group_names as $group_name)
+ static::$groups[$type][$group_name][$option_key] = $option_value;
+ }
+
+ /**
+ * Set group options on-the-fly, js version
+ *
+ * @param mixed $group_names Group name to change, or array of groups to change,
+ * or '' for global group, or '*' for all groups.
+ * @param string $option_key The name of the option to change
+ * @param mixed $option_value What to set the option to
+ */
+ public static function set_js_option($group_names, $option_key, $option_value)
+ {
+ static::set_group_option('js', $group_names, $option_key, $option_value);
+ }
+
+ /**
+ * Set group options on-the-fly, css version
+ *
+ * @param mixed $group_names Group name to change, or array of groups to change,
+ * or '' for global group, or '*' for all groups.
+ * @param string $option_key The name of the option to change
+ * @param mixed $option_value What to set the option to
+ */
+ public static function set_css_option($group_names, $option_key, $option_value)
+ {
+ static::set_group_option('css', $group_names, $option_key, $option_value);
+ }
+
+ /**
* Add a javascript asset.
*
* @param string $script The script to add.
@@ -390,7 +490,7 @@ private static function add_asset($type, $script, $script_min, $group)
if (!array_key_exists($group, static::$groups[$type]))
{
// Assume they want the group enabled
- static::add_group($type, $group, true);
+ static::add_group_base($type, $group);
}
array_push(static::$groups[$type][$group]['files'], $files);
}
@@ -428,19 +528,127 @@ private static function add_asset_inline($type, $content)
array_push(static::$inline_assets[$type], $content);
}
+
+ /**
+ * Return the path for the given JS asset. Ties into find_files, so supports
+ * everything that, say, Casset::js() does.
+ * Throws an exception if the file isn't found.
+ * @param string $script the name of the asset to find
+ * @param bool $add_url whether to add the 'url' config key to the filename
+ * @param bool $force_array by default, when one file is found a string is
+ * returned. Setting this to true causes a single-element array to be returned.
+ */
+ public static function get_filepath_js($filename, $add_url = false, $force_array = false)
+ {
+ return static::get_filepath($filename, 'js', $add_url, $force_array);
+ }
+
+ /**
+ * Return the path for the given CSS asset. Ties into find_files, so supports
+ * everything that, say, Casset::js() does.
+ * Throws an exception if the file isn't found.
+ * @param string $script the name of the asset to find
+ * @param bool $add_url whether to add the 'url' config key to the filename
+ * @param bool $force_array by default, when one file is found a string is
+ * returned. Setting this to true causes a single-element array to be returned.
+ */
+ public static function get_filepath_css($filename, $add_url = false, $force_array = false)
+ {
+ return static::get_filepath($filename, 'css', $add_url, $force_array);
+ }
+
+ /**
+ * Return the path for the given img asset. Ties into find_files, so supports
+ * everything that, say, Casset::js() does.
+ * Throws an exception if the file isn't found.
+ * @param string $script the name of the asset to find
+ * @param bool $add_url whether to add the 'url' config key to the filename
+ * @param bool $force_array by default, when one file is found a string is
+ * returned. Setting this to true causes a single-element array to be returned.
+ */
+ public static function get_filepath_img($filename, $add_url = false, $force_array = false)
+ {
+ return static::get_filepath($filename, 'img', $add_url, $force_array);
+ }
+
+ /**
+ * Return the path for the given asset. Ties into find_files, so supports
+ * everything that, say, Casset::js() does.
+ * Throws an exception if the file isn't found.
+ * @param string $script the name of the asset to find
+ * @param string $type js, css or img
+ * @param bool $add_url whether to add the 'url' config key to the filename
+ * @param bool $force_array by default, when one file is found a string is
+ * returned. Setting this to true causes a single-element array to be returned.
+ */
+ public static function get_filepath($filename, $type, $add_url = false, $force_array = false)
+ {
+ if (strpos($filename, '::') === false)
+ $filename = static::$default_path_key.'::'.$filename;
+ $files = static::find_files($filename, $type);
+ if ($add_url)
+ {
+ foreach ($files as &$file)
+ {
+ if (strpos($file, '//') !== false)
+ continue;
+ $file = static::$asset_url.$file;
+ }
+ }
+ if (count($files) == 1 && !$force_array)
+ return $files[0];
+ return $files;
+ }
+
+ /**
+ * Can be used to add deps to a group.
+ *
+ * @param string $type 'js' / 'css
+ * @param string $group The group name to add deps to
+ * @param array $deps An array of group names to add as deps.
+ */
+ public static function add_deps($type, $group, $deps)
+ {
+ if (!is_array($deps))
+ $deps = array($deps);
+ if (!array_key_exists($group, static::$groups[$type]))
+ throw new \Fuel_Exception("Group $group ($type) doesn't exist, so can't add deps to it.");
+ array_push(static::$groups[$type][$group]['deps'], $deps);
+ }
+
+ /**
+ * Sugar for add_deps(), for js groups
+ * @param string $group The group name to add deps to
+ * @param array $deps An array of group names to add as deps.
+ */
+ public static function add_js_deps($group, $deps)
+ {
+ static::add_deps('js', $group, $deps);
+ }
+
+ /**
+ * Sugar for add_deps(), for css groups
+ * @param string $group The group name to add deps to
+ * @param array $deps An array of group names to add as deps.
+ */
+ public static function add_css_deps($group, $deps)
+ {
+ static::add_deps('css', $group, $deps);
+ }
+
/**
* Shortcut to render_js() and render_css().
*
* @param string $group Which group to render. If omitted renders all groups
- * @param bool $inline If true, the result is printed inline. If false, is
+ * @param bool $inline_dep DEPRECATED. If true, the result is printed inline. If false, is
* written to a file and linked to. In fact, $inline = true also causes
* a cache file to be written for speed purposes
* @return string The javascript tags to be written to the page
*/
- public static function render($group = false, $inline = false, $attr = array())
+ public static function render($group = false, $inline_dep = null, $attr = array())
{
- $r = static::render_css($group, $inline, $attr);
- $r.= static::render_js($group, $inline, $attr);
+ $r = static::render_css($group, $inline_dep, $attr);
+ $r.= static::render_js($group, $inline_dep, $attr);
return $r;
}
@@ -448,12 +656,12 @@ public static function render($group = false, $inline = false, $attr = array())
* Renders the specific javascript group, or all groups if no group specified.
*
* @param string $group Which group to render. If omitted renders all groups
- * @param bool $inline If true, the result is printed inline. If false, is
+ * @param bool $inline_dep DEPRECATED. If true, the result is printed inline. If false, is
* written to a file and linked to. In fact, $inline = true also causes
* a cache file to be written for speed purposes
* @return string The javascript tags to be written to the page
*/
- public static function render_js($group = false, $inline = false, $attr = array())
+ public static function render_js($group = false, $inline = null, $attr = array())
{
// Don't force the user to remember that false is used for ommitted non-bool arguments
if (!is_string($group))
@@ -467,6 +675,14 @@ public static function render_js($group = false, $inline = false, $attr = array(
foreach ($file_groups as $group_name => $file_group)
{
+ // We used to take $inline as 2nd argument. However, we now use a group option.
+ // It's easiest if we let $inline override this group option, though.
+ if ($inline === null)
+ $inline = static::$groups['js'][$group_name]['inline'];
+ // $attr is also deprecated. If specified, entirely overrides the group option.
+ if (!count($attr))
+ $attr = static::$groups['js'][$group_name]['attr'];
+
if (static::$groups['js'][$group_name]['combine'])
{
$filename = static::combine('js', $file_group, static::$groups['js'][$group_name]['min'], $inline);
@@ -508,12 +724,12 @@ public static function render_js($group = false, $inline = false, $attr = array(
* Renders the specific css group, or all groups if no group specified.
*
* @param string $group Which group to render. If omitted renders all groups
- * @param bool $inline If true, the result is printed inline. If false, is
+ * @param bool $inline DEPRECATED. If true, the result is printed inline. If false, is
* written to a file and linked to. In fact, $inline = true also causes
* a cache file to be written for speed purposes
* @return string The css tags to be written to the page
*/
- public static function render_css($group = false, $inline = false, $attr = array())
+ public static function render_css($group = false, $inline = null, $attr = array())
{
// Don't force the user to remember that false is used for ommitted non-bool arguments
if (!is_string($group))
@@ -527,6 +743,14 @@ public static function render_css($group = false, $inline = false, $attr = array
foreach ($file_groups as $group_name => $file_group)
{
+ // We used to take $inline as 2nd argument. However, we now use a group option.
+ // It's easiest if we let $inline override this group option, though.
+ if ($inline === null)
+ $inline = static::$groups['css'][$group_name]['inline'];
+ // $attr is also deprecated. If specified, entirely overrides the group option.
+ if (!count($attr))
+ $attr = static::$groups['css'][$group_name]['attr'];
+
if (static::$groups['css'][$group_name]['combine'])
{
@@ -568,6 +792,84 @@ public static function render_css($group = false, $inline = false, $attr = array
}
/**
+ * Figures out where a file should be, based on its namespace and type.
+ *
+ * @param string $file The name of the asset to search for
+ * @param string $asset_type 'css', 'js' or 'img'
+ * @return string The path to the asset, relative to $asset_url
+ */
+ private static function find_files($file, $asset_type)
+ {
+ $parts = explode('::', $file, 2);
+ if (!array_key_exists($parts[0], static::$asset_paths))
+ throw new Casset_Exception("Could not find namespace {$parts[0]}");
+
+ $path = static::$asset_paths[$parts[0]]['path'];
+ $file = $parts[1];
+
+ $folder = static::$asset_paths[$parts[0]]['dirs'][$asset_type];
+ $file = ltrim($file, '/');
+
+ $remote = (strpos($path, '//') !== false);
+
+ if ($remote)
+ {
+ // Glob doesn't work on remote locations, so just assume they
+ // specified a file, not a glob pattern.
+ // Don't look for the file now either. That'll be done by
+ // file_get_contents later on, if need be.
+ return array($path.$folder.$file);
+ }
+ else
+ {
+ $glob_files = glob($path.$folder.$file);
+ if (!$glob_files || !count($glob_files))
+ throw new Casset_Exception("Found no files matching $path$folder$file");
+ return $glob_files;
+ }
+ }
+
+ /**
+ * Given a list of group names, adds to that list, in the appropriate places,
+ * and groups which are listed as dependencies of those group.
+ * Duplicate group names are not a problem, as a group is disabled when it's
+ * rendered.
+ *
+ * @param string $type 'js' /or/ 'css'
+ * @param array $group_names Array of group names to check
+ * @param int $depth Used by this function to check for potentially infinite recursion
+ * @return array List of group names with deps resolved
+ */
+
+ private static function resolve_deps($type, $group_names, $depth=0)
+ {
+ if ($depth > static::$deps_max_depth)
+ {
+ throw new Casset_Exception("Reached depth $depth trying to resolve dependencies. ".
+ "You've probably got some circular ones involving ".implode(',', $group_names).". ".
+ "If not, adjust the config key deps_max_depth.");
+ }
+ // Insert the dep just before what it's a dep for
+ foreach ($group_names as $i => $group_name)
+ {
+ // If the group's already been rendered, bottle
+ if (in_array($group_name, static::$rendered_groups[$type]))
+ continue;
+ // Don't pay attention to bottom-level groups which are disabled
+ if (!static::$groups[$type][$group_name]['enabled'] && $depth == 0)
+ continue;
+ // Otherwise, enable the group. Fairly obvious, as the whole point of
+ // deps is to render disabled groups
+ static::asset_enabled($type, $group_name, true);
+ if (count(static::$groups[$type][$group_name]['deps']))
+ {
+ array_splice($group_names, $i, 0, static::resolve_deps($type, static::$groups[$type][$group_name]['deps'], $depth+1));
+ }
+ }
+ return $group_names;
+ }
+
+ /**
* Determines the list of files to be rendered, along with whether they
* have been minified already.
*
@@ -580,6 +882,9 @@ private static function files_to_render($type, $group)
// If no group specified, print all groups.
if ($group == false)
$group_names = array_keys(static::$groups[$type]);
+ // If a group was specified, but it doesn't exist
+ else if (!array_key_exists($group, static::$groups[$type]))
+ return array();
else
$group_names = array($group);
@@ -587,10 +892,10 @@ private static function files_to_render($type, $group)
$minified = false;
+ $group_names = static::resolve_deps($type, $group_names);
+
foreach ($group_names as $group_name)
{
- if (!array_key_exists($group_name, static::$groups[$type]))
- continue;
if (static::$groups[$type][$group_name]['enabled'] == false)
continue;
// If there are no files in the group, there's no point in printing it.
@@ -601,6 +906,8 @@ private static function files_to_render($type, $group)
// Mark the group as disabled to avoid the same group being printed twice
static::asset_enabled($type, $group_name, false);
+ // Add it to the list of rendered groups
+ array_push(static::$rendered_groups[$type], $group_name);
foreach (static::$groups[$type][$group_name]['files'] as $file_set)
{
@@ -625,6 +932,25 @@ private static function files_to_render($type, $group)
}
/**
+ * Used to load a file from disk.
+ * Also calls the post_load callback.
+ *
+ * @param type $filename
+ * @return type
+ */
+ private static function load_file($filename, $type, $file_group)
+ {
+ $content = file_get_contents($filename);
+ if (static::$post_load_callback != null)
+ {
+ // For some reason, PHP doesn't like you calling member closure directly
+ $func = static::$post_load_callback;
+ $content = $func($content, $filename, $type, $file_group);
+ }
+ return $content;
+ }
+
+ /**
* Takes a list of files, and combines them into a single minified file.
* Doesn't bother if none of the files have been modified since the cache
* file was written.
@@ -666,12 +992,18 @@ private static function combine($type, $file_group, $minify, $inline)
if (static::$show_files_inline)
$content .= PHP_EOL.'/* '.$file['file'].' */'.PHP_EOL.PHP_EOL;
if ($file['minified'] || !$minify)
- $content .= file_get_contents($file['file']).PHP_EOL;
+ {
+ $content_temp = static::load_file($file['file'], $type, $file_group).PHP_EOL;
+ if ($type == 'css')
+ $content .= Casset_Cssurirewriter::rewrite($content_temp, dirname($file['file']));
+ else
+ $content .= $content_temp;
+ }
else
{
- $file_content = file_get_contents($file['file']);
+ $file_content = static::load_file($file['file'], $type, $file_group);
if ($file_content === false)
- throw new \Fuel_Exception("Couldn't not open file {$file['file']}");
+ throw new Casset_Exception("Couldn't not open file {$file['file']}");
if ($type == 'js')
{
$content .= Casset_JSMin::minify($file_content).PHP_EOL;
@@ -722,6 +1054,15 @@ public static function render_css_inline()
}
/**
+ * Sets the post_load file callback. It's pretty basic, and you're expected
+ * to handle e.g. filtering for the right file yourself.
+ * @param function the function to set
+ */
+ public static function set_post_load_callback($callback) {
+ static::$post_load_callback = $callback;
+ }
+
+ /**
* Locates the given image(s), and returns the resulting <img> tag.
*
* @param mixed $images Image(s) to print. Can be string or array of strings
@@ -803,4 +1144,7 @@ private static function clear_cache_base($filter = '*', $before = 'now')
}
+class Casset_Exception extends \Fuel_Exception {
+}
+
/* End of file casset.php */
View
2  classes/casset/csscompressor.php
@@ -17,7 +17,7 @@
* This library is used as part of Casset.
*
* @package Casset
- * @version v1.10
+ * @version v1.11
* @author Antony Male
* @license MIT License
* @link http://github.com/canton7/fuelphp-casset
View
2  classes/casset/cssurirewriter.php
@@ -10,7 +10,7 @@
* This library is used as part of Casset.
*
* @package Casset
- * @version v1.10
+ * @version v1.11
* @author Antony Male
* @license MIT License
* @link http://github.com/canton7/fuelphp-casset
View
2  classes/casset/jsmin.php
@@ -52,7 +52,7 @@
/**
* This library is used as part of Casset.
* @package Casset
- * @version v1.10
+ * @version v1.11
* @author Antony Male
* @license MIT License
* @link http://github.com/canton7/fuelphp-casset
View
36 config/casset.php
@@ -4,7 +4,7 @@
* Casset: Convenient asset library for FuelPHP.
*
* @package Casset
- * @version v1.10
+ * @version v1.11
* @author Antony Male
* @license MIT License
* @copyright 2011 Antony Male
@@ -105,7 +105,7 @@
* When minifying, whether to show the files names in each combined
* file in a comment before the tag to the file.
*/
- 'show_files' => true,
+ 'show_files' => false,
/**
* When minifying, whether to put comments in each minified file showing
@@ -114,6 +114,32 @@
'show_files_inline' => false,
/**
+ * How deep to go when resolving deps, before assuming that there're some
+ * circular ones.
+ */
+ 'deps_max_depth' => 5,
+
+ /**
+ * Here you can define a callback that is called after a while is loaded from
+ * disk, and before it is stored in a cache file.
+ * This set of circumstances only occurs when 'combine' is true -- the callback
+ * will *not* be called if 'combine' is false.
+ *
+ * This parameter expects a closure (or other function reference), with the
+ * following prototype:
+ * function($content, $filename, $type, $group_name)
+ * and should return the content after being processed.
+ * $content = the file content which casset has loaded from file
+ * $filename = the name of the file casset has loaded
+ * $type = 'js' or 'css'
+ * $group_name = the name of the group to which the file belongs
+ *
+ * You are allowed to define functions in this config file, or you can use
+ * Casset::set_post_load_callback(function($content, ...) { ... }); instead
+ */
+ 'post_load_callback' => null,
+
+ /**
* Groups of scripts.
* You can predefine groups of js and css scripts which can be enabled/disabled
* and rendered.
@@ -140,6 +166,7 @@
* 'file2.css',
* ),
* 'enabled' => true,
+ * 'deps' => array('some_other_group'),
* ),
* 'group_name_2' => array(.....),
* ),
@@ -164,9 +191,12 @@
* - 'enabled': whether the group will be rendered when render_css() or
* render_js() is called.
* - 'min: an optional key, allowing you to override the global 'min' config
- * key on a per-group basis. If null or not specified, the 'min' config#
+ * key on a per-group basis. If null or not specified, the 'min' config
* key will be used.
* Using this,
+ * - 'deps': an optional key, allowing you to specify other groups
+ * which are automatically rendered when this group is. See the readme
+ * for more details.
*/
'groups' => array(
),
View
310 readme.md
@@ -7,17 +7,31 @@ There are are some other changes too, please read on!
Thanks to Stephen Clay (and Douglas Crockford) for writing the minification libraries, stolen from http://code.google.com/p/minify/.
+If you have any comments/queries, either send me a message on github, post to the fuelphp [forum thread](http://fuelphp.com/forums/topics/view/2187), catch me on #fuelphp, or open an issue.
+
Installation
------------
-### Manual
-1. Clone / [download](https://github.com/canton7/fuelphp-casset/zipball/master)
+### Using oil (linux or daring windows users only)
+1. cd to your fuel project's root
+2. Run `php oil package install casset`
+3. Optionally edit fuel/packages/casset/config/casset.php (the defaults are sensible)
+4. Create public/assets/cache
+5. Add 'casset' to the 'always_load/packages' array in app/config/config.php (or call `Fuel::add_package('casset')` whenever you want to use it).
+6. Enjoy :)
+
+### Manual (may be more up-to-date)
+1. Clone (`git clone git://github.com/canton7/fuelphp-casset`) / [download](https://github.com/canton7/fuelphp-casset/zipball/master)
2. Stick in fuel/packages/
3. Optionally edit fuel/packages/casset/config/casset.php (the defaults are sensible)
4. Create public/assets/cache
5. Add 'casset' to the 'always_load/packages' array in app/config/config.php (or call `Fuel::add_package('casset')` whenever you want to use it).
6. Enjoy :)
+If you don't want to change the config file in `fuel/packages/casset/config/casset.php`, you can create your own config file in `fuel/app/config/casset.php`.
+You can either copy the entirely of the original config file, or just override the keys as you like.
+The magic of Fuel's `Config` class takes care of the rest.
+
Introduction
------------
@@ -107,9 +121,9 @@ To define a group in the config file, use the 'groups' key, eg:
array('file1.js', 'file1.min.js'),
'file2.js'
),
- 'enabled' => true,
'combine' => false,
'min' => false,
+ 'inline' => true
),
'group_name_2' => array(.....),
),
@@ -119,7 +133,9 @@ To define a group in the config file, use the 'groups' key, eg:
array('file1.css', 'file1.min.css'),
'file2.css',
),
- 'enabled' => true,
+ 'enabled' => false,
+ 'attr' => array('media' => 'print'),
+ 'deps' => array('some_group'),
),
'group_name_3' => array(.....),
),
@@ -133,7 +149,10 @@ If you're using minification, but have a pre-minified copy of your file (jquery
array element.
**enabled**: Optional, specifies whether a group is enabled. A group will only be rendered when it is enabled. Default true.
**combine**: This optional key allows you to override the 'combine' config key on a per-group bases.
-**min**: This optional key allows you to override the 'min' config key on a per-group basis.
+**min**: This optional key allows you to override the 'min' config key on a per-group basis.
+**inline**: Optional, allows you to render the group 'inline' -- that is, show the CSS directly in the file, rather than including a separate .css file. See the section on inling below.
+**attr**: Optional, allows you to specify extra attributes to be added to the script/css/link tag generated. See the section on attributes below.
+**deps**: (Optional) Specifies other groups to be rendered whenever this group is rendered, see the section below.
Groups can be enabled using `Casset::enable_js('group_name')`, and disabled using `Casset::disable_js('group_name')`. CSS equivalents also exist.
The shortcuts `Casset::enable('group_name')` and `Casset::disable('group_name')` also exist, which will enable/disable both the js and css groups of the given name, if they are defined.
@@ -153,13 +172,57 @@ Casset::css('myfile.css', false, 'group_name');
If the group name doesn't exist, the group is created, and enabled.
+You can also add groups on-the-fly using `Casset::add_group($group_type, $group_name, $files, $options)`, where `$options`is an array with *any* of the following keys:
+
+```php
+$options = array(
+ 'enabled' => true/false,
+ 'min' => true/false,
+ 'combine' => true/false,
+ 'inline' => true/false,
+ 'attr' => array(),
+ 'deps' => array(),
+);
+```
+
+The arguments are the same as for the config key -- if `'enabled'`, `'combine'` or `'min'` are omitted, the value specified in the config file are used. Eg:
+
+```php
+Casset::add_group('test_group', array(
+ array('file1.js', 'file1.min.js'),
+ 'file2.js',
+));
+```
+
+This method is provided merely for convenience when adding lots of files to a group at once.
+You don't have to create a group before adding files to it -- the group will be created it it doesn't exist.
+
+You can change any of these options on-the-fly using `Casset::set_group_option($type, $group, $key, $value)`, or the CSS- and JS- specific versions, `Caset::set_js_option($group, $key, $value)` and `Casset::set_css_option($group, $key, $value)`.
+`$group` has some special values: an empty string is a shortcut to the 'global' group (to which files are added if a group is not specified), and '*' is a shortcut to all groups.
+Multiple group names can also be specified, using an array.
+
+Examples:
+
+```php
+// Add a dep to the my_plugin group
+Casset::set_js_option('my_plugin', 'deps', 'jquery');
+
+// Make all files added to the current page using Casset::add_css() display inline:
+Casset::set_css_option('', 'inline', true);
+
+// Turn off minification for all groups, regardless of per-group settings, for the current page:
+CassetLLset_js_option('*', 'min', false);
+```
+
When you call `Casset::render()` (or the js- and css-specific varients), the order that groups are rendered is determined by the order in which they were created, with groups present in the config file appearing first.
Similarly (for JS files only), the order in which files appear is determined by the order in which they were added.
-This allows you a degree of control over what order your files are included in your page, which may be necessary when satisfying dependancies.
+This allows you a degree of control over what order your files are included in your page, which may be necessary when satisfying dependencies.
If this isn't working for you, or you want something a bit more explicit, try this: If file A depends on B, add B to its own group and explicitely render it first.
NOTE: Calling ``Casset::js('file.js')`` will add that file to the "global" group. Use / abuse as you need!
+NOTE: The arguments for `Casset::add_group` used to be different. Backwards compatibilty is maintained (for now), but you are encouranged to more to the new syntax.
+
Paths and namespacing
---------------------
@@ -190,6 +253,12 @@ For the above example, you can specify the following in your config file:
),
```
+You can also add paths on-the-flow using `Casset::add_path($key, $path)`, eg.
+
+```php
+Casset::add_path('admin', 'assets/admin/');
+```
+
Which path to use is then decided by prefixing the asset filename with the key of the path to use. Note that if you omit the path key, the current default path key (initially 'core') is used.
```php
@@ -229,7 +298,7 @@ array (
'some_key' => array(
'path' => 'more_assets/',
'js_dir' => 'javascript/',
- 'css_dir' => 'styles/'
+ 'css_dir' => 'styles/',
'img_dir' => 'images/',
),
),
@@ -238,10 +307,13 @@ array (
This can be particularly useful when you're using some third-party code, and don't have control over where the assets are located.
+Note also that you can add an asset which uses a path which isn't yet defined.
+Casset only requires that the path is defined by the time the file is rendered.
+
Globbing
--------
-As well as filenames, you can specify [http://php.net/glob](glob patterns). This will act exactly the same as if each file which the glob matches had been added individually.
+As well as filenames, you can specify [glob patterns](http://php.net/glob). This will act exactly the same as if each file which the glob matches had been added individually.
For example:
```php
@@ -253,24 +325,94 @@ Casset::css('admin::admin_*.css');
// Executes glob('adders/admin/css/admin_*.css') and adds all matches
Casset::js('*.js', '*.js');
-// Adds all js files in assets/js, ensuring that none of them are minified.
+// Adds all js files in assets/js, ensuring that none of them are pre-minified.
```
An exception is thrown when no files can be matched.
+Dependencies
+------------
+
+Casset allows you to specify dependancies between groups, which are automatically resolved.
+This means that you can, say, define a group for your jQuery plugin, and have jQuery automatically included every time that plugin is included.
+
+Note that dependancies can only be entire groups -- groups can not depend on individual files.
+This has to do with how files are put into cache files, email me if you're interested.
+
+A JS group can only depend on other JS groups, while a CSS group can only depend on other CSS groups.
+
+Casset is pretty intelligent, and will only include a file once, before the file that requires it.
+After a file has been required as a dependency, it will be disabled.
+Casset will also bail after following the dependency chain through a certain number of steps, to avoid cycling dependancies.
+This value is given by the config key 'deps_max_depth'.
+
+The easiest way of specifying dependancies is through the config file:
+
+```php
+'groups' => array(
+ 'js' => array(
+ 'jquery' => array(
+ 'files' => array(
+ array('jquery.js', 'jquery.min.js'),
+ ),
+ ),
+
+ 'my_plugin' => array(
+ 'files' => array(
+ 'jquery.my_plugin.js',
+ ),
+ 'deps' => array(
+ 'jquery',
+ ),
+ ),
+ ),
+),
+```
+
+Dependencies can be either a string (for a single dependency), or an array (for multiple ones).
+
+You can also define dependencies when you call `Casset::add_group()`, by using the `'deps'` key in `$options`.
+
+ Eg:
+
+ ```php
+Caset::add_group('js', 'my_plugin', array('jquery.my_plugin.js'), array(
+ 'deps' => 'jquery',
+));
+ ```
+
+In addition, the functions `Casset::add_js_deps()` and `Casset::add_css_deps()` exist, which can be used like:
+
+```php
+Casset::add_js_deps('group_name', array('this', 'groups', 'deps'));
+```
+
+As usual, there's another base function, `Casset::add_deps()`, which takes 'js' or 'css' as its first argument, but is otherwise identical.
+
+If you have a JS group A, which depends on both the JS group B and CSS group B, a useful trick is to create a CSS group A with no files, that depends on the CSS group B.
+Therefore whenever group A is rendered, both the JS group B and CSS group B will be rendered.
+
Inlining
--------
-If you want Casset to display a group inline, instead of linking to a cache file, you can pass `true` as the second argument to `Casset::render_js()` or `Casset::render_css()`.
-For example...
+If you want Casset to display a group inline, instead of linking to a cache file, you can mark the group as 'inline' when you create it.
```php
-// Render 'group_name' js inline.
-echo Casset::render_js('group_name', true);
-// Render all css groups inline.
-echo Casset::render_css(false, true);
+// In your config (see add_group also)
+'groups' => array(
+ 'js' => array(
+ 'my_inline_group' => array(
+ 'files' => array('file.css'),
+ 'inline' => true,
+ ),
+ ),
+),
```
+NOTE: You could previously pass an argument to `Casset::render()` to tell it to render the group inline.
+This behaviour has been deprecated, although it still works.
+You are encouraged to move away from this technique if you are using it.
+
Occasionally it can be useful to declare a bit of javascript in your view, but have it included in your template. Casset allows for this, although it doesn't support groups and minification
(as I don't see a need for those features in this context -- give me a shout if you find an application for them, and I'll enhance).
@@ -301,22 +443,29 @@ Similarly, `Casset::css_inline()` and `Casset::render_css_inline()` exist.
Extra attributes
----------------
-`Casset::render_js()` and `Casset::render_css()` support an optional third argument which allows the user to define extra attributes to be applied to the script/link tag.
-This can be combined with the fact that one a group has been rendered, it is disabled, allowing the following to be done:
+If you want to apply extra attributes to the script/link tag, you can add them to the group config, using the key 'attr'.
+For example:
```php
-Casset::css('main.css');
-Casset::css('print.css', false, 'print');
-
-// Render the 'print' group
-echo Casset::render_css('print', false, array('media' => 'print');
-// <link rel="stylesheet" type="text/css" href="http://...print.css" media="print />
+// In your config
+'groups' => array(
+ 'css' => array(
+ 'my_print => array(
+ 'files' => array('file.css'),
+ 'attr' => array('media' => 'print'),
+ ),
+ ),
+),
-// Render everything else, except the 'print' group
+// Render the 'my_print' group, along with the others
echo Casset::render_css();
-// <link rel="stylesheet" type="text/css" href="http://...main.css" />
+// <link rel="stylesheet" type="text/css" href="http://...somefile.css" media="print" />
```
+NOTE: You used to be able to pass an `$attr` argument to `Casset::render()`.
+This behaviour has been deprecated, although it still works.
+Please move to the new system.
+
Minification and combining
--------------------------
@@ -363,8 +512,8 @@ Casset supports handing files on remote machines, as well as the local one.
This is done by creating a new namespace, and specifying a url instead of a relative path.
All files using that namespace will then be fetched from the given url.
-However, there are a couple of caveats:
- - It is possible for Casset to fetch, combine and minify remote assets. However, it can obviously only write the cache file locally.
+However, there are a couple of caveats:
+ - It is possible for Casset to fetch, combine and minify remote assets. However, it can obviously only write the cache file locally.
- Casset doesn't bother to check the modification times on remote files when deciding whether the cache is out of date (as this would cause lots of http requests from your server, and entirely defeat
the point of caching in the first place). Therefore if the remote file changes, Casset's cache will not be updated, and you'll have to remove it manually, or with the cache-clearing functions.
@@ -414,6 +563,51 @@ echo Casset::render();
// <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.9.14/jquery-ui.min.js"></script>
```
+Getting asset paths / urls
+--------------------------
+
+Thanks to [Peter](http://fuelphp.com/forums/posts/view_reply/3097) for this one. You can ask Casset for the path / url to a specific file.
+Files are specified in exactly the same way as with eg `Casset::js()`, with the same rules to do with namespacing, `Casset::set_path()`, etc.
+
+The functions in question are `Casset::get_filepath_js()`, `Casset::get_filepath_css()`, and `Casset::get_filepath_img()`.
+
+They're all used in the same way:
+
+```php
+echo Casset::get_filepath_js('file.js');
+// assets/js/file.js
+```
+
+Note that fuel executes in the `/public` directory, so the paths returned are relative to the current working dir.
+If you'd prefer urls to be returned, pass `true` as the second parameter.
+Note that a url will not be added if you're referencing a remote file.
+
+```php
+echo Casset::get_filepath_js('file.js', true);
+// eg http://localhost/site/public/assets/js/file.js
+```
+
+Complexities start arising when you specify globs.
+By default, an array will be returned if more than one file is found, otherwise a string is returned.
+To override this behaviour, and return an array even if only one file is found, pass `true` as the third parameter.
+
+```php
+print_r(Casset::get_filepath_js('file.js', false, true));
+// Array( [0] => 'assets/js/file.js' )
+
+print_r(Casset::get_filepath_js('file*.js'));
+// Array( [0] => 'assets/js/file1.js', [1] => 'assets/js/file2.js' )
+```
+
+There also exists `Casset::get_filepath()`, which takes the form
+
+```php
+Casset::get_filepath($name, $type, $add_url = false, $force_array = false);
+```
+
+`$name`, `$add_url` and `$force_array` are the same as for `Casset::get_filepath_js()`, while the `$type` argument is one of 'js', 'css', or 'img'.
+In the future there are plans to let you specify your own types, hence why this is exposed :)
+
Clearing the cache
------------------
Since cache files are not automatically removed (Casset has no way of knowing whether a cache file might be needed again), a few methods have been provided to remove cache files.
@@ -431,6 +625,68 @@ Casset::clear_cache('yesterday');
// Removes all cache files last modified yesterday
```
+Callbacks
+---------
+
+Quick thanks to [ShonM](https://github.com/shonm) for pushing so hard to get this feature implemented :)
+
+There is currently a single callback, `post_load`, and it is likely that it will stay this way.
+
+Callbacks allow you the flexibility to do you own processing on the files that Casset loads.
+This means that you can use SASS, CoffeeScript, etc, then configure Casset to call the appropriate compiler when it loads the asset.
+
+Note that the `post_load` is *only* called when the 'combine' config key is set to true.
+If 'combine' is false, Casset doesn't generate a cache file and instead links to the asset directly.
+No cache file = no processing of the file by Casset = no callback.
+If you really need this changed, send me a message and I'll start hacking :)
+
+Processing files (beyond minification) is not really what Casset is about, and this reflects in the callback design.
+There is a single callback, which is called for all files, regardless of group, type, extension, etc.
+The callback is passed the name of the file, the type (js or css) and the group to which it belongs,as well as the file content of course.
+It is then up to you to decide how, if at all, you want to process this content, based on the other parameters passed.
+
+The callback is set either in the config file (the `post_load_callback` key), or using `Casset::set_post_load_callback()`.
+Both expect an anonymous function (closure), although I daresay you could bind it straight to some other library's method.
+
+The callback itself has the following prototype, although you can miss out the latter arguments if you want: PHP won't complain.
+
+```php
+function($content, $filename, $type, $group_name) { ... }
+```
+
+Where:
+`$content`: The content of the file which Casset has read, and is passing to you.
+`$filename`: The name of the file which Casset has read.
+`$type`: 'js' or 'css', depending on whether the file is js or css.
+`$group_name`: The group which is currently being rendered, and to which the file belongs.
+
+Obviously, the callback is only called when a cache file is generated.
+When testing, therefore, it is recommended that you stick a `Casset::clear_cache()` above your testing code.
+
+Time for a few examples:
+
+```php
+// In the config file:
+'post_load_callback' => function($content, $filename, $type) {
+ // We don't want to process JS files
+ if ($type == 'js')
+ return $content;
+ return SomeLibrary::some_method($content);
+},
+
+// In a controller somewhere
+Casset::set_post_load_callback(function($content, $filename) {
+ $ext = pathinfo($filename, PATHINFO_EXTENSION);
+ if ($ext != 'sass')
+ return $content;
+ return SomeSassLibrary::some_method($content);
+});
+```
+
+Note that Casset is pretty lazy, so the callback won't be called under you call `Casset::render()` (or one of its variants).
+Therefore feel free to define your callback after telling Casset to include the files you want the callback to process.
+
+
Comparison to Assetic
---------------------
Please sign in to comment.
Something went wrong with that request. Please try again.