array(provided filepaths)
*/
protected $providedJavascript = array();
/**
* Paths to all required CSS files relative to the docroot.
*
* @var array
*/
protected $css = array();
/**
* All custom javascript code that is inserted into the page's HTML
*
* @var array
*/
protected $customScript = array();
/**
* All custom CSS rules which are inserted directly at the bottom of the HTML
tag
*
* @var array
*/
protected $customCSS = array();
/**
* All custom HTML markup which is added before the closing tag, e.g. additional
* metatags.
*
* @var array
*/
protected $customHeadTags = array();
/**
* Remembers the file paths or uniquenessIDs of all Requirements cleared through
* {@link clear()}, so that they can be restored later.
*
* @var array
*/
protected $disabled = array();
/**
* The file paths (relative to docroot) or uniquenessIDs of any included requirements which
* should be blocked when executing {@link inlcudeInHTML()}. This is useful, for example,
* to block scripts included by a superclass without having to override entire functions and
* duplicate a lot of code.
*
* Use {@link unblock()} or {@link unblock_all()} to revert changes.
*
* @var array
*/
protected $blocked = array();
/**
* A list of combined files registered via {@link combine_files()}. Keys are the output file
* names, values are lists of input files.
*
* @var array
*/
protected $combinedFiles = array();
/**
* Use the injected minification service to minify any javascript file passed to {@link combine_files()}.
*
* @var bool
*/
protected $minifyCombinedFiles = false;
/**
* Whether or not file headers should be written when combining files
*
* @var boolean
*/
protected $writeHeaderComment = true;
/**
* Where to save combined files. By default they're placed in assets/_combinedfiles, however
* this may be an issue depending on your setup, especially for CSS files which often contain
* relative paths.
*
* @var string
*/
protected $combinedFilesFolder = null;
/**
* Put all JavaScript includes at the bottom of the template before the closing tag,
* rather than the default behaviour of placing them at the end of the tag. This means
* script downloads won't block other HTTP requests, which can be a performance improvement.
*
* @var bool
*/
public $writeJavascriptToBody = true;
/**
* Force the JavaScript to the bottom of the page, even if there's a script tag in the body already
*
* @var boolean
*/
protected $forceJSToBottom = false;
/**
* Configures the default prefix for combined files.
*
* This defaults to `_combinedfiles`, and is the folder within the configured asset backend that
* combined files will be stored in. If using a backend shared with other systems, it is usually
* necessary to distinguish combined files from other assets.
*
* @config
* @var string
*/
private static $default_combined_files_folder = '_combinedfiles';
/**
* Flag to include the hash in the querystring instead of the filename for combined files.
*
* By default the `` of the source files is appended to the end of the combined file
* (prior to the file extension). If combined files are versioned in source control or running
* in a distributed environment (such as one where the newest version of a file may not always be
* immediately available) then it may sometimes be necessary to disable this. When this is set to true,
* the hash will instead be appended via a querystring parameter to enable cache busting, but not in
* the filename itself. I.e. `assets/_combinedfiles/name.js?m=`
*
* @config
* @var bool
*/
private static $combine_hash_querystring = false;
/**
* @var GeneratedAssetHandler
*/
protected $assetHandler = null;
/**
* @var Requirements_Minifier
*/
protected $minifier = null;
/**
* Gets the backend storage for generated files
*
* @return GeneratedAssetHandler
*/
public function getAssetHandler()
{
return $this->assetHandler;
}
/**
* Set a new asset handler for this backend
*
* @param GeneratedAssetHandler $handler
*/
public function setAssetHandler(GeneratedAssetHandler $handler)
{
$this->assetHandler = $handler;
}
/**
* Gets the minification service for this backend
*
* @deprecated 4.0..5.0
* @return Requirements_Minifier
*/
public function getMinifier()
{
return $this->minifier;
}
/**
* Set a new minification service for this backend
*
* @param Requirements_Minifier $minifier
*/
public function setMinifier(Requirements_Minifier $minifier = null)
{
$this->minifier = $minifier;
}
/**
* Enable or disable the combination of CSS and JavaScript files
*
* @param bool $enable
*/
public function setCombinedFilesEnabled($enable)
{
$this->combinedFilesEnabled = (bool)$enable;
}
/**
* Check if header comments are written
*
* @return bool
*/
public function getWriteHeaderComment()
{
return $this->writeHeaderComment;
}
/**
* Flag whether header comments should be written for each combined file
*
* @param bool $write
* @return $this
*/
public function setWriteHeaderComment($write)
{
$this->writeHeaderComment = $write;
return $this;
}
/**
* Set the folder to save combined files in. By default they're placed in _combinedfiles,
* however this may be an issue depending on your setup, especially for CSS files which often
* contain relative paths.
*
* This must not include any 'assets' prefix
*
* @param string $folder
*/
public function setCombinedFilesFolder($folder)
{
$this->combinedFilesFolder = $folder;
}
/**
* Retrieve the combined files folder prefix
*
* @return string
*/
public function getCombinedFilesFolder()
{
if ($this->combinedFilesFolder) {
return $this->combinedFilesFolder;
}
return Config::inst()->get(__CLASS__, 'default_combined_files_folder');
}
/**
* Set whether to add caching query params to the requests for file-based requirements.
* Eg: themes/myTheme/js/main.js?m=123456789. The parameter is a timestamp generated by
* filemtime. This has the benefit of allowing the browser to cache the URL infinitely,
* while automatically busting this cache every time the file is changed.
*
* @param bool
*/
public function setSuffixRequirements($var)
{
$this->suffixRequirements = $var;
}
/**
* Check whether we want to suffix requirements
*
* @return bool
*/
public function getSuffixRequirements()
{
return $this->suffixRequirements;
}
/**
* Set whether you want to write the JS to the body of the page rather than at the end of the
* head tag.
*
* @param bool
* @return $this
*/
public function setWriteJavascriptToBody($var)
{
$this->writeJavascriptToBody = $var;
return $this;
}
/**
* Check whether you want to write the JS to the body of the page rather than at the end of the
* head tag.
*
* @return bool
*/
public function getWriteJavascriptToBody()
{
return $this->writeJavascriptToBody;
}
/**
* Forces the JavaScript requirements to the end of the body, right before the closing tag
*
* @param bool
* @return $this
*/
public function setForceJSToBottom($var)
{
$this->forceJSToBottom = $var;
return $this;
}
/**
* Check if the JavaScript requirements are written to the end of the body, right before the closing tag
*
* @return bool
*/
public function getForceJSToBottom()
{
return $this->forceJSToBottom;
}
/**
* Check if minify files should be combined
*
* @return bool
*/
public function getMinifyCombinedFiles()
{
return $this->minifyCombinedFiles;
}
/**
* Set if combined files should be minified
*
* @param bool $minify
* @return $this
*/
public function setMinifyCombinedFiles($minify)
{
$this->minifyCombinedFiles = $minify;
return $this;
}
/**
* Register the given JavaScript file as required.
*
* @param string $file Relative to docroot
* @param array $options List of options. Available options include:
* - 'provides' : List of scripts files included in this file
* - 'async' : Boolean value to set async attribute to script tag
* - 'defer' : Boolean value to set defer attribute to script tag
* - 'type' : Override script type= value.
*/
public function javascript($file, $options = array())
{
// Get type
$type = null;
if (isset($this->javascript[$file]['type'])) {
$type = $this->javascript[$file]['type'];
}
if (isset($options['type'])) {
$type = $options['type'];
}
// make sure that async/defer is set if it is set once even if file is included multiple times
$async = (
isset($options['async']) && isset($options['async']) == true
|| (
isset($this->javascript[$file])
&& isset($this->javascript[$file]['async'])
&& $this->javascript[$file]['async'] == true
)
);
$defer = (
isset($options['defer']) && isset($options['defer']) == true
|| (
isset($this->javascript[$file])
&& isset($this->javascript[$file]['defer'])
&& $this->javascript[$file]['defer'] == true
)
);
$this->javascript[$file] = array(
'async' => $async,
'defer' => $defer,
'type' => $type,
);
// Record scripts included in this file
if (isset($options['provides'])) {
$this->providedJavascript[$file] = array_values($options['provides']);
}
}
/**
* Remove a javascript requirement
*
* @param string $file
*/
protected function unsetJavascript($file)
{
unset($this->javascript[$file]);
}
/**
* Gets all scripts that are already provided by prior scripts.
* This follows these rules:
* - Files will not be considered provided if they are separately
* included prior to the providing file.
* - Providing files can be blocked, and don't provide anything
* - Provided files can't be blocked (you need to block the provider)
* - If a combined file includes files that are provided by prior
* scripts, then these should be excluded from the combined file.
* - If a combined file includes files that are provided by later
* scripts, then these files should be included in the combined
* file, but we can't block the later script either (possible double
* up of file).
*
* @return array Array of provided files (map of $path => $path)
*/
public function getProvidedScripts()
{
$providedScripts = array();
$includedScripts = array();
foreach ($this->javascript as $script => $options) {
// Ignore scripts that are explicitly blocked
if (isset($this->blocked[$script])) {
continue;
}
// At this point, the file is included.
// This might also be combined at this point, potentially.
$includedScripts[$script] = true;
// Record any files this provides, EXCEPT those already included by now
if (isset($this->providedJavascript[$script])) {
foreach ($this->providedJavascript[$script] as $provided) {
if (!isset($includedScripts[$provided])) {
$providedScripts[$provided] = $provided;
}
}
}
}
return $providedScripts;
}
/**
* Returns an array of required JavaScript, excluding blocked
* and duplicates of provided files.
*
* @return array
*/
public function getJavascript()
{
return array_diff_key(
$this->javascript,
$this->getBlocked(),
$this->getProvidedScripts()
);
}
/**
* Gets all javascript, including blocked files. Unwraps the array into a non-associative list
*
* @return array Indexed array of javascript files
*/
protected function getAllJavascript()
{
return $this->javascript;
}
/**
* Register the given JavaScript code into the list of requirements
*
* @param string $script The script content as a string (without enclosing