2007-07-19 12:40:28 +02:00
< ? php
2008-11-07 03:06:28 +01:00
2007-07-19 12:40:28 +02:00
/**
2015-04-29 10:17:08 +02:00
* Requirements tracker for JavaScript and CSS .
2012-12-12 12:00:52 +01:00
*
2012-04-12 08:02:46 +02:00
* @ package framework
2008-02-25 03:10:37 +01:00
* @ subpackage view
2007-07-19 12:40:28 +02:00
*/
2014-08-16 02:51:17 +02:00
class Requirements implements Flushable {
/**
2015-04-29 10:17:08 +02:00
* Triggered early in the request when a flush is requested
2014-08-16 02:51:17 +02:00
*/
public static function flush () {
self :: delete_all_combined_files ();
}
2012-12-12 12:00:52 +01:00
2009-03-04 04:44:11 +01:00
/**
* Enable combining of css / javascript files .
2015-04-29 10:17:08 +02:00
*
* @ param bool $enable
2009-03-04 04:44:11 +01:00
*/
public static function set_combined_files_enabled ( $enable ) {
2010-05-25 06:13:45 +02:00
self :: backend () -> set_combined_files_enabled ( $enable );
2009-03-04 04:44:11 +01:00
}
2009-04-29 01:52:15 +02:00
/**
2010-05-25 06:13:45 +02:00
* Checks whether combining of css / javascript files is enabled .
2015-04-29 10:17:08 +02:00
*
* @ return bool
2009-04-29 01:52:15 +02:00
*/
2010-05-25 06:13:45 +02:00
public static function get_combined_files_enabled () {
2012-12-08 12:20:20 +01:00
return self :: backend () -> get_combined_files_enabled ();
2010-05-25 06:13:45 +02:00
}
2010-05-25 06:15:43 +02:00
/**
2015-04-29 10:17:08 +02:00
* Set the relative folder e . g . 'assets' for where to store combined files
*
2010-05-25 06:15:43 +02:00
* @ param string $folder Path to folder
*/
public static function set_combined_files_folder ( $folder ) {
self :: backend () -> setCombinedFilesFolder ( $folder );
}
2009-04-29 01:52:15 +02:00
/**
2015-04-29 10:17:08 +02:00
* 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 .
2012-12-12 12:00:52 +01:00
*
2009-04-29 01:52:15 +02:00
* @ param bool
*/
public static function set_suffix_requirements ( $var ) {
2010-05-25 06:13:45 +02:00
self :: backend () -> set_suffix_requirements ( $var );
2009-04-29 01:52:15 +02:00
}
2012-12-12 12:00:52 +01:00
2009-04-29 01:52:15 +02:00
/**
2015-04-29 10:17:08 +02:00
* Check whether we want to suffix requirements
2012-12-12 12:00:52 +01:00
*
2009-04-29 01:52:15 +02:00
* @ return bool
*/
public static function get_suffix_requirements () {
2010-05-25 06:13:45 +02:00
return self :: backend () -> get_suffix_requirements ();
2009-04-29 01:52:15 +02:00
}
2012-12-12 12:00:52 +01:00
2008-11-07 03:06:28 +01:00
/**
2015-04-29 10:17:08 +02:00
* Instance of the requirements for storage . You can create your own backend to change the
* default JS and CSS inclusion behaviour .
2008-11-07 03:06:28 +01:00
*
2015-04-29 10:17:08 +02:00
* @ var Requirements_Backend
2008-11-07 03:06:28 +01:00
*/
private static $backend = null ;
2012-12-12 12:00:52 +01:00
2008-11-10 02:13:42 +01:00
public static function backend () {
2008-11-07 03:06:28 +01:00
if ( ! self :: $backend ) {
self :: $backend = new Requirements_Backend ();
}
return self :: $backend ;
}
2012-12-12 12:00:52 +01:00
2008-11-07 03:06:28 +01:00
/**
* Setter method for changing the Requirements backend
*
2015-04-29 10:17:08 +02:00
* @ param Requirements_Backend $backend
2008-11-07 03:06:28 +01:00
*/
public static function set_backend ( Requirements_Backend $backend ) {
self :: $backend = $backend ;
}
2012-12-12 12:00:52 +01:00
2008-11-07 03:06:28 +01:00
/**
2015-04-29 10:17:08 +02:00
* Register the given JavaScript file as required .
*
* @ param string $file Relative to docroot
2008-11-07 03:06:28 +01:00
*/
2012-09-19 12:07:39 +02:00
public static function javascript ( $file ) {
2008-11-07 03:06:28 +01:00
self :: backend () -> javascript ( $file );
}
2012-12-12 12:00:52 +01:00
2008-11-07 03:06:28 +01:00
/**
2015-04-29 10:17:08 +02:00
* Register the given JavaScript code into the list of requirements
2012-12-12 12:00:52 +01:00
*
2015-04-29 10:17:08 +02:00
* @ param string $script The script content as a string ( without enclosing < script > tag )
* @ param string | int $uniquenessID A unique ID that ensures a piece of code is only added once
2008-11-07 03:06:28 +01:00
*/
2012-09-19 12:07:39 +02:00
public static function customScript ( $script , $uniquenessID = null ) {
2008-11-07 03:06:28 +01:00
self :: backend () -> customScript ( $script , $uniquenessID );
}
/**
2015-04-29 10:17:08 +02:00
* Return all registered custom scripts
2012-12-12 12:00:52 +01:00
*
2015-04-29 10:17:08 +02:00
* @ return array
*/
public static function get_custom_scripts () {
return self :: backend () -> get_custom_scripts ();
}
/**
* Register the given CSS styles into the list of requirements
2012-12-12 12:00:52 +01:00
*
2015-04-29 10:17:08 +02:00
* @ param string $script CSS selectors as a string ( without enclosing < style > tag )
* @ param string | int $uniquenessID A unique ID that ensures a piece of code is only added once
2008-11-07 03:06:28 +01:00
*/
2012-09-19 12:07:39 +02:00
public static function customCSS ( $script , $uniquenessID = null ) {
2009-02-02 00:49:53 +01:00
self :: backend () -> customCSS ( $script , $uniquenessID );
2008-11-07 03:06:28 +01:00
}
2012-12-12 12:00:52 +01:00
2008-11-07 03:06:28 +01:00
/**
2015-04-29 10:17:08 +02:00
* Add the following custom HTML code to the < head > section of the page
2012-12-12 12:00:52 +01:00
*
2015-04-29 10:17:08 +02:00
* @ param string $html Custom HTML code
* @ param string | int $uniquenessID A unique ID that ensures a piece of code is only added once
2008-11-07 03:06:28 +01:00
*/
2012-09-19 12:07:39 +02:00
public static function insertHeadTags ( $html , $uniquenessID = null ) {
2008-11-07 03:06:28 +01:00
self :: backend () -> insertHeadTags ( $html , $uniquenessID );
}
2012-12-12 12:00:52 +01:00
2008-11-07 03:06:28 +01:00
/**
2015-04-29 10:17:08 +02:00
* Include the content of the given JavaScript file in the list of requirements . Dollar - sign
* variables will be interpolated with values from $vars similar to a . ss template .
2012-12-12 12:00:52 +01:00
*
2015-04-29 10:17:08 +02:00
* @ param string $file The template file to load , relative to docroot
* @ param string [] | int [] $vars The array of variables to interpolate .
* @ param string | int $uniquenessID A unique ID that ensures a piece of code is only added once
2008-11-07 03:06:28 +01:00
*/
2012-09-19 12:07:39 +02:00
public static function javascriptTemplate ( $file , $vars , $uniquenessID = null ) {
2008-11-07 03:06:28 +01:00
self :: backend () -> javascriptTemplate ( $file , $vars , $uniquenessID );
}
2012-12-12 12:00:52 +01:00
2008-11-07 03:06:28 +01:00
/**
2015-04-29 10:17:08 +02:00
* Register the given stylesheet into the list of requirements .
2012-12-12 12:00:52 +01:00
*
2015-04-29 10:17:08 +02:00
* @ param string $file The CSS file to load , relative to site root
* @ param string $media Comma - separated list of media types to use in the link tag
* ( e . g . 'screen,projector' )
2008-11-07 03:06:28 +01:00
*/
2012-09-19 12:07:39 +02:00
public static function css ( $file , $media = null ) {
2008-11-07 03:06:28 +01:00
self :: backend () -> css ( $file , $media );
}
2012-12-12 12:00:52 +01:00
2008-11-07 03:06:28 +01:00
/**
2011-03-22 15:02:11 +01:00
* Registers the given themeable stylesheet as required .
*
2015-04-29 10:17:08 +02:00
* A CSS file in the current theme path name 'themename/css/$name.css' is first searched for ,
* and it that doesn ' t exist and the module parameter is set then a CSS file with that name in
* the module is used .
2011-03-22 15:02:11 +01:00
*
2015-04-29 10:17:08 +02:00
* @ param string $name The name of the file - eg '/css/File.css' would have the name 'File'
* @ param string $module The module to fall back to if the css file does not exist in the
* current theme .
* @ param string $media Comma - separated list of media types to use in the link tag
* ( e . g . 'screen,projector' )
2011-03-22 15:02:11 +01:00
*/
public static function themedCSS ( $name , $module = null , $media = null ) {
return self :: backend () -> themedCSS ( $name , $module , $media );
2008-11-07 03:06:28 +01:00
}
2011-03-22 15:02:11 +01:00
2008-11-07 03:06:28 +01:00
/**
2015-04-29 10:17:08 +02:00
* Clear either a single or all requirements
*
* Caution : Clearing single rules added via customCSS and customScript only works if you
* originally specified a $uniquenessID .
2012-12-12 12:00:52 +01:00
*
2015-04-29 10:17:08 +02:00
* @ param string | int $fileOrID
2008-11-07 03:06:28 +01:00
*/
2012-09-19 12:07:39 +02:00
public static function clear ( $fileOrID = null ) {
2008-11-07 03:06:28 +01:00
self :: backend () -> clear ( $fileOrID );
}
/**
2015-04-29 10:17:08 +02:00
* Restore requirements cleared by call to Requirements :: clear
*/
public static function restore () {
self :: backend () -> restore ();
}
/**
* Block inclusion of a specific file
*
* The difference between this and { @ link clear } is that the calling order does not matter ;
* { @ link clear } must be called after the initial registration , whereas { @ link block } can be
* used in advance . This is useful , for example , to block scripts included by a superclass
* without having to override entire functions and duplicate a lot of code .
*
* Note that blocking should be used sparingly because it ' s hard to trace where an file is
* being blocked from .
2008-11-07 03:06:28 +01:00
*
2015-04-29 10:17:08 +02:00
* @ param string | int $fileOrID
2008-11-07 03:06:28 +01:00
*/
2012-09-19 12:07:39 +02:00
public static function block ( $fileOrID ) {
2008-11-07 03:06:28 +01:00
self :: backend () -> block ( $fileOrID );
}
2015-04-29 10:17:08 +02:00
2008-11-07 03:06:28 +01:00
/**
2015-04-29 10:17:08 +02:00
* Remove an item from the block list
2012-12-12 12:00:52 +01:00
*
2015-04-29 10:17:08 +02:00
* @ param string | int $fileOrID
2008-11-07 03:06:28 +01:00
*/
2012-09-19 12:07:39 +02:00
public static function unblock ( $fileOrID ) {
2008-11-07 03:06:28 +01:00
self :: backend () -> unblock ( $fileOrID );
}
/**
2015-04-29 10:17:08 +02:00
* Removes all items from the block list
2008-11-07 03:06:28 +01:00
*/
2012-09-19 12:07:39 +02:00
public static function unblock_all () {
2008-11-07 03:06:28 +01:00
self :: backend () -> unblock_all ();
}
2012-12-12 12:00:52 +01:00
2008-11-07 03:06:28 +01:00
/**
* Update the given HTML content with the appropriate include tags for the registered
2015-04-29 10:17:08 +02:00
* requirements . Needs to receive a valid HTML / XHTML template in the $content parameter ,
* including a head and body tag .
2012-12-12 12:00:52 +01:00
*
2015-04-29 10:17:08 +02:00
* @ param string $templateFile No longer used , only retained for compatibility
* @ param string $content HTML content that has already been parsed from the $templateFile
* through { @ link SSViewer }
* @ return string HTML content augmented with the requirements tags
2008-11-07 03:06:28 +01:00
*/
2012-09-19 12:07:39 +02:00
public static function includeInHTML ( $templateFile , $content ) {
2008-11-07 03:06:28 +01:00
return self :: backend () -> includeInHTML ( $templateFile , $content );
}
2012-12-12 12:00:52 +01:00
2015-04-29 10:17:08 +02:00
/**
* Attach requirements inclusion to X - Include - JS and X - Include - CSS headers on the given
* HTTP Response
*
* @ param SS_HTTPResponse $response
*/
2012-09-19 12:07:39 +02:00
public static function include_in_response ( SS_HTTPResponse $response ) {
2008-11-12 05:31:33 +01:00
return self :: backend () -> include_in_response ( $response );
}
2012-12-12 12:00:52 +01:00
2008-11-07 03:06:28 +01:00
/**
2015-04-29 10:17:08 +02:00
* Add i18n files from the given javascript directory . SilverStripe expects that the given
* directory will contain a number of JavaScript files named by language : en_US . js , de_DE . js ,
* etc .
2012-12-12 12:00:52 +01:00
*
2015-04-29 10:17:08 +02:00
* @ param string $langDir The JavaScript lang directory , relative to the site root , e . g . ,
* 'framework/javascript/lang'
* @ param bool $return Return all relative file paths rather than including them in
* requirements
* @ param bool $langOnly Only include language files , not the base libraries
*
* @ return array
2008-11-07 03:06:28 +01:00
*/
2012-01-06 12:00:04 +01:00
public static function add_i18n_javascript ( $langDir , $return = false , $langOnly = false ) {
return self :: backend () -> add_i18n_javascript ( $langDir , $return , $langOnly );
2008-11-07 03:06:28 +01:00
}
2012-12-12 12:00:52 +01:00
2008-11-07 03:06:28 +01:00
/**
2015-04-29 10:17:08 +02:00
* Concatenate several css or javascript files into a single dynamically generated file . This
* increases performance by fewer HTTP requests .
*
* The combined file is regenerated based on every file modification time . Optionally a
* rebuild can be triggered by appending ? flush = 1 to the URL . If all files to be combined are
* JavaScript , we use the external JSMin library to minify the JavaScript .
*
* All combined files will have a comment on the start of each concatenated file denoting their
* original position . For easier debugging , we only minify JavaScript if not in development
* mode ({ @ link Director :: isDev ()}) .
*
* CAUTION : You ' re responsible for ensuring that the load order for combined files is
* retained - otherwise combining JavaScript files can lead to functional errors in the
* JavaScript logic , and combining CSS can lead to incorrect inheritance . You can also
* only include each file once across all includes and comibinations in a single page load .
*
* CAUTION : Combining CSS Files discards any " media " information .
*
* Example for combined JavaScript :
* < code >
* Requirements :: combine_files (
* 'foobar.js' ,
* array (
* 'mysite/javascript/foo.js' ,
* 'mysite/javascript/bar.js' ,
* )
* );
* </ code >
*
* Example for combined CSS :
* < code >
* Requirements :: combine_files (
* 'foobar.css' ,
* array (
* 'mysite/javascript/foo.css' ,
* 'mysite/javascript/bar.css' ,
* )
* );
* </ code >
*
* @ param string $combinedFileName Filename of the combined file relative to docroot
* @ param array $files Array of filenames relative to docroot
2008-11-07 03:06:28 +01:00
*
2015-04-29 10:17:08 +02:00
* @ return bool | void
2008-11-07 03:06:28 +01:00
*/
2015-04-29 10:17:08 +02:00
public static function combine_files ( $combinedFileName , $files ) {
self :: backend () -> combine_files ( $combinedFileName , $files );
2008-11-07 03:06:28 +01:00
}
2012-12-12 12:00:52 +01:00
2008-11-07 03:06:28 +01:00
/**
2015-04-29 10:17:08 +02:00
* Return all combined files ; keys are the combined file names , values are lists of
* files being combined .
2012-12-12 12:00:52 +01:00
*
2008-11-07 03:06:28 +01:00
* @ return array
*/
2012-09-19 12:07:39 +02:00
public static function get_combine_files () {
2008-11-07 03:06:28 +01:00
return self :: backend () -> get_combine_files ();
}
2012-12-12 12:00:52 +01:00
2008-11-07 03:06:28 +01:00
/**
2015-04-29 10:17:08 +02:00
* Delete all dynamically generated combined files from the filesystem
2012-12-12 12:00:52 +01:00
*
2008-11-07 03:06:28 +01:00
* @ param string $combinedFileName If left blank , all combined files are deleted .
*/
2012-09-19 12:07:39 +02:00
public static function delete_combined_files ( $combinedFileName = null ) {
2008-11-07 03:06:28 +01:00
return self :: backend () -> delete_combined_files ( $combinedFileName );
}
2012-12-12 12:00:52 +01:00
2014-08-16 02:51:17 +02:00
/**
* Deletes all generated combined files in the configured combined files directory ,
2015-04-29 10:17:08 +02:00
* but doesn ' t delete the directory itself
2014-08-16 02:51:17 +02:00
*/
public static function delete_all_combined_files () {
return self :: backend () -> delete_all_combined_files ();
}
2008-11-07 03:06:28 +01:00
/**
* Re - sets the combined files definition . See { @ link Requirements_Backend :: clear_combined_files ()}
*/
2012-09-19 12:07:39 +02:00
public static function clear_combined_files () {
2008-11-07 03:06:28 +01:00
self :: backend () -> clear_combined_files ();
}
2012-12-12 12:00:52 +01:00
2008-11-07 03:06:28 +01:00
/**
2015-04-29 10:17:08 +02:00
* Do the heavy lifting involved in combining ( and , in the case of JavaScript minifying ) the
* combined files .
2008-11-07 03:06:28 +01:00
*/
2012-09-19 12:07:39 +02:00
public static function process_combined_files () {
2008-11-07 03:06:28 +01:00
return self :: backend () -> process_combined_files ();
}
2008-07-18 01:32:31 +02:00
2008-07-18 05:47:17 +02:00
/**
2015-04-29 10:17:08 +02:00
* Set whether you want to write the JS to the body of the page rather than at the end of the
* head tag .
2008-11-07 03:06:28 +01:00
*
2015-04-29 10:17:08 +02:00
* @ param bool
2008-12-04 23:38:32 +01:00
*/
2012-09-19 12:07:39 +02:00
public static function set_write_js_to_body ( $var ) {
2008-12-04 23:38:32 +01:00
self :: backend () -> set_write_js_to_body ( $var );
}
2012-12-12 12:00:52 +01:00
2013-11-25 20:49:35 +01:00
/**
2015-04-29 10:17:08 +02:00
* Set whether to force the JavaScript to end of the body . Useful if you use inline script tags
* that don ' t rely on scripts included via { @ link Requirements :: javascript ()) .
2013-11-25 20:49:35 +01:00
*
2015-04-29 10:17:08 +02:00
* @ param boolean $var If true , force the JavaScript to be included at the bottom of the page
2013-11-25 20:49:35 +01:00
*/
public static function set_force_js_to_bottom ( $var ) {
2013-12-07 15:38:51 +01:00
self :: backend () -> set_force_js_to_bottom ( $var );
2013-11-25 20:49:35 +01:00
}
2015-04-29 10:17:08 +02:00
/**
* Output debugging information
*/
2012-09-19 12:07:39 +02:00
public static function debug () {
2008-11-07 03:06:28 +01:00
return self :: backend () -> debug ();
}
}
2010-04-23 02:11:41 +02:00
/**
2012-04-12 08:02:46 +02:00
* @ package framework
2010-04-23 02:11:41 +02:00
* @ subpackage view
*/
2008-11-07 03:06:28 +01:00
class Requirements_Backend {
2010-05-25 06:13:45 +02:00
/**
2015-04-29 10:17:08 +02:00
* 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 .
*
2010-05-25 06:13:45 +02:00
* @ var bool
*/
protected $suffix_requirements = true ;
/**
2015-04-29 10:17:08 +02:00
* Whether to combine CSS and JavaScript files
2010-05-25 06:13:45 +02:00
*
2015-04-29 10:17:08 +02:00
* @ var bool
2010-05-25 06:13:45 +02:00
*/
protected $combined_files_enabled = true ;
/**
2015-04-29 10:17:08 +02:00
* Paths to all required JavaScript files relative to docroot
2010-05-25 06:13:45 +02:00
*
2008-07-18 05:47:17 +02:00
* @ var array $javascript
*/
2008-11-07 03:06:28 +01:00
protected $javascript = array ();
2008-07-18 01:32:31 +02:00
2008-07-18 05:47:17 +02:00
/**
2015-04-29 10:17:08 +02:00
* Paths to all required CSS files relative to the docroot .
2010-05-25 06:13:45 +02:00
*
2008-09-22 18:02:33 +02:00
* @ var array $css
2008-07-18 05:47:17 +02:00
*/
2008-11-07 03:06:28 +01:00
protected $css = array ();
2008-07-18 01:32:31 +02:00
2008-07-18 05:47:17 +02:00
/**
2015-04-29 10:17:08 +02:00
* All custom javascript code that is inserted into the page ' s HTML
2008-07-18 05:47:17 +02:00
*
* @ var array $customScript
*/
2008-11-07 03:06:28 +01:00
protected $customScript = array ();
2008-07-18 01:32:31 +02:00
2008-07-18 05:47:17 +02:00
/**
2015-04-29 10:17:08 +02:00
* All custom CSS rules which are inserted directly at the bottom of the HTML < head > tag
2008-07-18 05:47:17 +02:00
*
* @ var array $customCSS
*/
2008-11-07 03:06:28 +01:00
protected $customCSS = array ();
2008-07-18 01:32:31 +02:00
2008-07-18 05:47:17 +02:00
/**
2015-04-29 10:17:08 +02:00
* All custom HTML markup which is added before the closing < head > tag , e . g . additional
* metatags .
2008-07-18 05:47:17 +02:00
*/
2008-11-07 03:06:28 +01:00
protected $customHeadTags = array ();
2008-07-18 01:32:31 +02:00
2008-07-18 05:47:17 +02:00
/**
2015-04-29 10:17:08 +02:00
* Remembers the file paths or uniquenessIDs of all Requirements cleared through
* { @ link clear ()}, so that they can be restored later .
2008-07-18 05:47:17 +02:00
*
* @ var array $disabled
*/
2008-11-07 03:06:28 +01:00
protected $disabled = array ();
2010-05-25 06:13:45 +02:00
2008-07-18 05:47:17 +02:00
/**
2015-04-29 10:17:08 +02:00
* 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 .
*
2008-07-18 05:47:17 +02:00
* Use { @ link unblock ()} or { @ link unblock_all ()} to revert changes .
2010-05-25 06:13:45 +02:00
*
2008-07-18 05:47:17 +02:00
* @ var array $blocked
*/
2008-11-07 03:06:28 +01:00
protected $blocked = array ();
2010-05-25 06:13:45 +02:00
2008-07-18 01:32:31 +02:00
/**
2015-04-29 10:17:08 +02:00
* A list of combined files registered via { @ link combine_files ()} . Keys are the output file
* names , values are lists of input files .
2010-05-25 06:13:45 +02:00
*
2008-07-18 05:47:17 +02:00
* @ var array $combine_files
2008-07-18 01:32:31 +02:00
*/
2008-11-07 03:06:28 +01:00
public $combine_files = array ();
2010-05-25 06:13:45 +02:00
2008-07-18 01:32:31 +02:00
/**
2015-04-29 10:17:08 +02:00
* Use the JSMin library to minify any javascript file passed to { @ link combine_files ()} .
2008-07-18 01:32:31 +02:00
*
2015-04-29 10:17:08 +02:00
* @ var bool
2008-07-18 01:32:31 +02:00
*/
2008-11-07 03:06:28 +01:00
public $combine_js_with_jsmin = true ;
2010-05-25 06:13:45 +02:00
2013-06-04 14:47:44 +02:00
/**
2015-04-29 10:17:08 +02:00
* Whether or not file headers should be written when combining files
2013-06-04 14:47:44 +02:00
*
* @ var boolean
*/
public $write_header_comment = true ;
2015-04-29 10:17:08 +02:00
2010-05-25 06:17:05 +02:00
/**
2015-04-29 10:17:08 +02:00
* 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
2010-05-25 06:17:05 +02:00
*/
2010-10-15 04:37:41 +02:00
protected $combinedFilesFolder = null ;
2010-05-25 06:15:43 +02:00
2008-08-11 07:29:41 +02:00
/**
2015-04-29 10:17:08 +02:00
* Put all JavaScript includes at the bottom of the template before the closing < body > tag ,
* rather than the default behaviour of placing them at the end of the < head > tag . This means
* script downloads won ' t block other HTTP requests , which can be a performance improvement .
*
* @ var bool
2008-08-11 07:29:41 +02:00
*/
2008-11-12 05:31:33 +01:00
public $write_js_to_body = true ;
2015-04-29 10:17:08 +02:00
2013-11-25 20:49:35 +01:00
/**
2015-04-29 10:17:08 +02:00
* Force the JavaScript to the bottom of the page , even if there ' s a script tag in the body already
*
2013-11-25 20:49:35 +01:00
* @ var boolean
*/
2013-12-07 15:38:51 +01:00
protected $force_js_to_bottom = false ;
2014-08-16 02:51:17 +02:00
2015-04-29 10:17:08 +02:00
/**
* Enable or disable the combination of CSS and JavaScript files
*
* @ param $enable
*/
2012-09-19 12:07:39 +02:00
public function set_combined_files_enabled ( $enable ) {
2010-05-25 06:15:43 +02:00
$this -> combined_files_enabled = ( bool ) $enable ;
2010-05-25 06:13:45 +02:00
}
2012-12-12 12:00:52 +01:00
2015-04-29 10:17:08 +02:00
/**
* Check whether file combination is enabled .
*
* @ return bool
*/
2012-09-19 12:07:39 +02:00
public function get_combined_files_enabled () {
2010-05-25 06:15:43 +02:00
return $this -> combined_files_enabled ;
}
2010-10-15 04:37:41 +02:00
/**
2015-04-29 10:17:08 +02:00
* Set the folder to save combined files in . 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 .
*
* @ param string $folder
2010-10-15 04:37:41 +02:00
*/
2012-09-19 12:07:39 +02:00
public function setCombinedFilesFolder ( $folder ) {
2010-05-25 06:15:43 +02:00
$this -> combinedFilesFolder = $folder ;
2010-05-25 06:13:45 +02:00
}
2012-12-12 12:00:52 +01:00
2010-10-15 04:37:41 +02:00
/**
2015-04-29 10:17:08 +02:00
* @ return string Folder relative to the webroot
2010-10-15 04:37:41 +02:00
*/
2012-09-19 12:07:39 +02:00
public function getCombinedFilesFolder () {
2010-10-15 04:37:41 +02:00
return ( $this -> combinedFilesFolder ) ? $this -> combinedFilesFolder : ASSETS_DIR . '/_combinedfiles' ;
}
2012-12-12 12:00:52 +01:00
2010-05-25 06:13:45 +02:00
/**
2015-04-29 10:17:08 +02:00
* 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 .
2012-12-12 12:00:52 +01:00
*
2010-05-25 06:13:45 +02:00
* @ param bool
*/
2012-09-19 12:07:39 +02:00
public function set_suffix_requirements ( $var ) {
2010-05-25 06:13:45 +02:00
$this -> suffix_requirements = $var ;
}
2012-12-12 12:00:52 +01:00
2010-05-25 06:13:45 +02:00
/**
2015-04-29 10:17:08 +02:00
* Check whether we want to suffix requirements
2012-12-12 12:00:52 +01:00
*
2010-05-25 06:13:45 +02:00
* @ return bool
*/
2012-09-19 12:07:39 +02:00
public function get_suffix_requirements () {
2010-05-25 06:13:45 +02:00
return $this -> suffix_requirements ;
}
2012-12-12 12:00:52 +01:00
2008-12-04 23:38:32 +01:00
/**
2015-04-29 10:17:08 +02:00
* Set whether you want to write the JS to the body of the page rather than at the end of the
* head tag .
2008-12-04 23:38:32 +01:00
*
2015-04-29 10:17:08 +02:00
* @ param bool
2008-12-04 23:38:32 +01:00
*/
public function set_write_js_to_body ( $var ) {
$this -> write_js_to_body = $var ;
}
2015-04-29 10:17:08 +02:00
2013-12-07 15:38:51 +01:00
/**
2015-04-29 10:17:08 +02:00
* Forces the JavaScript requirements to the end of the body , right before the closing tag
2013-12-07 15:38:51 +01:00
*
2015-04-29 10:17:08 +02:00
* @ param bool
2013-12-07 15:38:51 +01:00
*/
public function set_force_js_to_bottom ( $var ) {
$this -> force_js_to_bottom = $var ;
}
2015-04-29 10:17:08 +02:00
2007-07-19 12:40:28 +02:00
/**
2015-04-29 10:17:08 +02:00
* Register the given JavaScript file as required .
*
* @ param string $file Relative to docroot
2007-07-19 12:40:28 +02:00
*/
2008-11-07 03:06:28 +01:00
public function javascript ( $file ) {
$this -> javascript [ $file ] = true ;
}
2012-12-12 12:00:52 +01:00
2008-11-07 03:06:28 +01:00
/**
2015-04-29 10:17:08 +02:00
* Returns an array of all required JavaScript
2008-11-07 03:06:28 +01:00
*
* @ return array
*/
public function get_javascript () {
2015-04-29 10:17:08 +02:00
return array_keys ( array_diff_key ( $this -> javascript , $this -> blocked ));
2007-07-19 12:40:28 +02:00
}
2012-12-12 12:00:52 +01:00
2007-07-19 12:40:28 +02:00
/**
2015-04-29 10:17:08 +02:00
* Register the given JavaScript code into the list of requirements
*
* @ param string $script The script content as a string ( without enclosing < script > tag )
* @ param string | int $uniquenessID A unique ID that ensures a piece of code is only added once
2007-07-19 12:40:28 +02:00
*/
2008-11-07 03:06:28 +01:00
public function customScript ( $script , $uniquenessID = null ) {
2009-02-16 22:10:03 +01:00
if ( $uniquenessID ) $this -> customScript [ $uniquenessID ] = $script ;
else $this -> customScript [] = $script ;
2012-12-12 12:00:52 +01:00
2007-07-19 12:40:28 +02:00
$script .= " \n " ;
}
2012-12-12 12:00:52 +01:00
2009-02-02 00:49:53 +01:00
/**
2015-04-29 10:17:08 +02:00
* Return all registered custom scripts
2009-02-02 00:49:53 +01:00
*
2015-04-29 10:17:08 +02:00
* @ return array
*/
public function get_custom_scripts () {
$requirements = " " ;
if ( $this -> customScript ) {
foreach ( $this -> customScript as $script ) {
$requirements .= " $script\n " ;
}
}
return $requirements ;
}
/**
* Register the given CSS styles into the list of requirements
*
* @ param string $script CSS selectors as a string ( without enclosing < style > tag )
* @ param string | int $uniquenessID A unique ID that ensures a piece of code is only added once
2009-02-02 00:49:53 +01:00
*/
2012-09-19 12:07:39 +02:00
public function customCSS ( $script , $uniquenessID = null ) {
2009-02-16 22:10:03 +01:00
if ( $uniquenessID ) $this -> customCSS [ $uniquenessID ] = $script ;
else $this -> customCSS [] = $script ;
2007-07-19 12:40:28 +02:00
}
2012-12-12 12:00:52 +01:00
2007-07-19 12:40:28 +02:00
/**
2015-04-29 10:17:08 +02:00
* Add the following custom HTML code to the < head > section of the page
2008-07-18 05:47:17 +02:00
*
2015-04-29 10:17:08 +02:00
* @ param string $html Custom HTML code
* @ param string | int $uniquenessID A unique ID that ensures a piece of code is only added once
2007-07-19 12:40:28 +02:00
*/
2012-09-19 12:07:39 +02:00
public function insertHeadTags ( $html , $uniquenessID = null ) {
2009-02-16 22:10:03 +01:00
if ( $uniquenessID ) $this -> customHeadTags [ $uniquenessID ] = $html ;
else $this -> customHeadTags [] = $html ;
2007-07-19 12:40:28 +02:00
}
2012-12-12 12:00:52 +01:00
2007-07-19 12:40:28 +02:00
/**
2015-04-29 10:17:08 +02:00
* Include the content of the given JavaScript file in the list of requirements . Dollar - sign
* variables will be interpolated with values from $vars similar to a . ss template .
*
* @ param string $file The template file to load , relative to docroot
* @ param string [] | int [] $vars The array of variables to interpolate .
* @ param string | int $uniquenessID A unique ID that ensures a piece of code is only added once
2007-07-19 12:40:28 +02:00
*/
2012-09-19 12:07:39 +02:00
public function javascriptTemplate ( $file , $vars , $uniquenessID = null ) {
2007-07-19 12:40:28 +02:00
$script = file_get_contents ( Director :: getAbsFile ( $file ));
2009-02-16 22:08:54 +01:00
$search = array ();
$replace = array ();
if ( $vars ) foreach ( $vars as $k => $v ) {
2007-07-19 12:40:28 +02:00
$search [] = '$' . $k ;
$replace [] = str_replace ( " \\ ' " , " ' " , Convert :: raw2js ( $v ));
}
2012-12-12 12:00:52 +01:00
2007-07-19 12:40:28 +02:00
$script = str_replace ( $search , $replace , $script );
2008-11-07 03:06:28 +01:00
$this -> customScript ( $script , $uniquenessID );
2007-07-19 12:40:28 +02:00
}
2012-12-12 12:00:52 +01:00
2007-07-19 12:40:28 +02:00
/**
2015-04-29 10:17:08 +02:00
* Register the given stylesheet into the list of requirements .
2012-12-12 12:00:52 +01:00
*
2015-04-29 10:17:08 +02:00
* @ param string $file The CSS file to load , relative to site root
* @ param string $media Comma - separated list of media types to use in the link tag
* ( e . g . 'screen,projector' )
2007-07-19 12:40:28 +02:00
*/
2012-09-19 12:07:39 +02:00
public function css ( $file , $media = null ) {
2008-11-07 03:06:28 +01:00
$this -> css [ $file ] = array (
2007-07-19 12:40:28 +02:00
" media " => $media
);
}
2012-12-12 12:00:52 +01:00
2015-04-29 10:17:08 +02:00
/**
* Get the list of registered CSS file requirements , excluding blocked files
*
* @ return array
*/
2012-09-19 12:07:39 +02:00
public function get_css () {
2009-02-02 00:49:53 +01:00
return array_diff_key ( $this -> css , $this -> blocked );
2008-11-07 03:06:28 +01:00
}
2012-12-12 12:00:52 +01:00
2007-07-19 12:40:28 +02:00
/**
2015-04-29 10:17:08 +02:00
* Clear either a single or all requirements
2012-12-12 12:00:52 +01:00
*
2015-04-29 10:17:08 +02:00
* Caution : Clearing single rules added via customCSS and customScript only works if you
* originally specified a $uniquenessID .
2012-12-12 12:00:52 +01:00
*
2015-04-29 10:17:08 +02:00
* @ param string | int $fileOrID
2007-07-19 12:40:28 +02:00
*/
2012-09-19 12:07:39 +02:00
public function clear ( $fileOrID = null ) {
2007-07-19 12:40:28 +02:00
if ( $fileOrID ) {
2009-12-16 06:41:38 +01:00
foreach ( array ( 'javascript' , 'css' , 'customScript' , 'customCSS' , 'customHeadTags' ) as $type ) {
2008-11-07 03:06:28 +01:00
if ( isset ( $this -> { $type }[ $fileOrID ])) {
$this -> disabled [ $type ][ $fileOrID ] = $this -> { $type }[ $fileOrID ];
unset ( $this -> { $type }[ $fileOrID ]);
2007-07-19 12:40:28 +02:00
}
}
} else {
2008-11-07 03:06:28 +01:00
$this -> disabled [ 'javascript' ] = $this -> javascript ;
$this -> disabled [ 'css' ] = $this -> css ;
$this -> disabled [ 'customScript' ] = $this -> customScript ;
$this -> disabled [ 'customCSS' ] = $this -> customCSS ;
2008-12-15 02:45:13 +01:00
$this -> disabled [ 'customHeadTags' ] = $this -> customHeadTags ;
2012-12-12 12:00:52 +01:00
2008-11-07 03:06:28 +01:00
$this -> javascript = array ();
$this -> css = array ();
$this -> customScript = array ();
$this -> customCSS = array ();
$this -> customHeadTags = array ();
2007-07-19 12:40:28 +02:00
}
}
2012-12-12 12:00:52 +01:00
2007-07-19 12:40:28 +02:00
/**
* Restore requirements cleared by call to Requirements :: clear
*/
2012-09-19 12:07:39 +02:00
public function restore () {
2008-11-07 03:06:28 +01:00
$this -> javascript = $this -> disabled [ 'javascript' ];
$this -> css = $this -> disabled [ 'css' ];
$this -> customScript = $this -> disabled [ 'customScript' ];
$this -> customCSS = $this -> disabled [ 'customCSS' ];
2008-12-15 02:49:06 +01:00
$this -> customHeadTags = $this -> disabled [ 'customHeadTags' ];
2007-07-19 12:40:28 +02:00
}
2015-04-29 10:17:08 +02:00
/**
* Block inclusion of a specific file
*
* The difference between this and { @ link clear } is that the calling order does not matter ;
* { @ link clear } must be called after the initial registration , whereas { @ link block } can be
* used in advance . This is useful , for example , to block scripts included by a superclass
* without having to override entire functions and duplicate a lot of code .
*
* Note that blocking should be used sparingly because it ' s hard to trace where an file is
* being blocked from .
*
* @ param string | int $fileOrID
*/
public function block ( $fileOrID ) {
$this -> blocked [ $fileOrID ] = $fileOrID ;
}
/**
* Remove an item from the block list
*
* @ param string | int $fileOrID
*/
public function unblock ( $fileOrID ) {
if ( isset ( $this -> blocked [ $fileOrID ])) unset ( $this -> blocked [ $fileOrID ]);
}
/**
* Removes all items from the block list
*/
public function unblock_all () {
$this -> blocked = array ();
}
2012-12-12 12:00:52 +01:00
2007-07-19 12:40:28 +02:00
/**
* Update the given HTML content with the appropriate include tags for the registered
2008-07-18 01:32:31 +02:00
* requirements . Needs to receive a valid HTML / XHTML template in the $content parameter ,
2015-04-29 10:17:08 +02:00
* including a head and body tag .
2012-12-12 12:00:52 +01:00
*
2015-04-29 10:17:08 +02:00
* @ param string $templateFile No longer used , only retained for compatibility
* @ param string $content HTML content that has already been parsed from the $templateFile
* through { @ link SSViewer }
* @ return string HTML content augmented with the requirements tags
2007-07-19 12:40:28 +02:00
*/
2012-09-19 12:07:39 +02:00
public function includeInHTML ( $templateFile , $content ) {
2012-10-03 16:16:19 +02:00
if (
2012-12-12 12:00:52 +01:00
( strpos ( $content , '</head>' ) !== false || strpos ( $content , '</head ' ) !== false )
2012-10-03 16:16:19 +02:00
&& ( $this -> css || $this -> javascript || $this -> customCSS || $this -> customScript || $this -> customHeadTags )
) {
2008-08-11 04:25:44 +02:00
$requirements = '' ;
$jsRequirements = '' ;
2012-12-12 12:00:52 +01:00
// Combine files - updates $this->javascript and $this->css
$this -> process_combined_files ();
2010-05-25 06:15:43 +02:00
foreach ( array_diff_key ( $this -> javascript , $this -> blocked ) as $file => $dummy ) {
2013-03-11 09:58:14 +01:00
$path = Convert :: raw2xml ( $this -> path_for_file ( $file ));
2008-08-11 09:22:50 +02:00
if ( $path ) {
$jsRequirements .= " <script type= \" text/javascript \" src= \" $path\ " ></ script > \n " ;
2008-08-11 04:25:44 +02:00
}
2007-07-19 12:40:28 +02:00
}
2012-12-12 12:00:52 +01:00
2015-04-29 10:17:08 +02:00
// Add all inline JavaScript *after* including external files they might rely on
2008-11-07 03:06:28 +01:00
if ( $this -> customScript ) {
2012-12-12 12:00:52 +01:00
foreach ( array_diff_key ( $this -> customScript , $this -> blocked ) as $script ) {
2008-08-11 04:25:44 +02:00
$jsRequirements .= " <script type= \" text/javascript \" > \n //<![CDATA[ \n " ;
$jsRequirements .= " $script\n " ;
$jsRequirements .= " \n //]]> \n </script> \n " ;
}
2007-07-19 12:40:28 +02:00
}
2012-12-12 12:00:52 +01:00
foreach ( array_diff_key ( $this -> css , $this -> blocked ) as $file => $params ) {
2013-03-11 09:58:14 +01:00
$path = Convert :: raw2xml ( $this -> path_for_file ( $file ));
2008-08-11 09:22:50 +02:00
if ( $path ) {
2012-12-12 12:00:52 +01:00
$media = ( isset ( $params [ 'media' ]) && ! empty ( $params [ 'media' ]))
2012-09-26 23:34:00 +02:00
? " media= \" { $params [ 'media' ] } \" " : " " ;
2008-08-11 09:22:50 +02:00
$requirements .= " <link rel= \" stylesheet \" type= \" text/css \" { $media } href= \" $path\ " /> \n " ;
2008-08-11 04:25:44 +02:00
}
2007-07-19 12:40:28 +02:00
}
2012-12-12 12:00:52 +01:00
foreach ( array_diff_key ( $this -> customCSS , $this -> blocked ) as $css ) {
2008-08-11 04:25:44 +02:00
$requirements .= " <style type= \" text/css \" > \n $css\n </style> \n " ;
}
2012-12-12 12:00:52 +01:00
foreach ( array_diff_key ( $this -> customHeadTags , $this -> blocked ) as $customHeadTag ) {
$requirements .= " $customHeadTag\n " ;
2008-08-11 04:25:44 +02:00
}
2012-12-12 12:00:52 +01:00
2013-12-07 15:38:51 +01:00
if ( $this -> force_js_to_bottom ) {
// Remove all newlines from code to preserve layout
$jsRequirements = preg_replace ( '/>\n*/' , '>' , $jsRequirements );
2015-04-29 10:17:08 +02:00
// Forcefully put the scripts at the bottom of the body instead of before the first
// script tag.
2013-12-07 15:38:51 +01:00
$content = preg_replace ( " /(< \ /body[^>]*>)/i " , $jsRequirements . " \\ 1 " , $content );
// Put CSS at the bottom of the head
$content = preg_replace ( " /(< \ /head>)/i " , $requirements . " \\ 1 " , $content );
} elseif ( $this -> write_js_to_body ) {
2008-08-11 07:29:41 +02:00
// Remove all newlines from code to preserve layout
$jsRequirements = preg_replace ( '/>\n*/' , '>' , $jsRequirements );
2015-04-29 10:17:08 +02:00
2014-08-30 20:32:28 +02:00
// If your template already has script tags in the body, then we try to put our script
2009-05-06 08:36:16 +02:00
// tags just before those. Otherwise, we put it at the bottom.
2008-08-11 07:29:41 +02:00
$p2 = stripos ( $content , '<body' );
2012-07-01 08:29:54 +02:00
$p1 = stripos ( $content , '<script' , $p2 );
2014-08-30 20:32:28 +02:00
$commentTags = array ();
$canWriteToBody = ( $p1 !== false )
&&
2015-04-29 10:17:08 +02:00
// Check that the script tag is not inside a html comment tag
2014-08-30 20:32:28 +02:00
! (
2014-10-31 15:41:58 +01:00
preg_match ( '/.*(?|(<!--)|(-->))/U' , $content , $commentTags , 0 , $p1 )
2014-08-30 20:32:28 +02:00
&&
2014-10-31 15:41:58 +01:00
$commentTags [ 1 ] == '-->'
2014-08-30 20:32:28 +02:00
);
if ( $canWriteToBody ) {
2009-05-06 08:36:16 +02:00
$content = substr ( $content , 0 , $p1 ) . $jsRequirements . substr ( $content , $p1 );
2008-08-11 07:29:41 +02:00
} else {
2009-08-11 10:54:57 +02:00
$content = preg_replace ( " /(< \ /body[^>]*>)/i " , $jsRequirements . " \\ 1 " , $content );
2008-08-11 07:29:41 +02:00
}
2012-12-12 12:00:52 +01:00
// Put CSS at the bottom of the head
2010-10-15 04:49:52 +02:00
$content = preg_replace ( " /(< \ /head>)/i " , $requirements . " \\ 1 " , $content );
2008-08-11 04:25:44 +02:00
} else {
2010-10-15 04:49:52 +02:00
$content = preg_replace ( " /(< \ /head>)/i " , $requirements . " \\ 1 " , $content );
$content = preg_replace ( " /(< \ /head>)/i " , $jsRequirements . " \\ 1 " , $content );
2008-08-11 04:25:44 +02:00
}
2012-12-12 12:00:52 +01:00
}
2008-08-11 07:29:41 +02:00
return $content ;
2007-07-19 12:40:28 +02:00
}
2008-11-12 05:31:33 +01:00
/**
2015-04-29 10:17:08 +02:00
* Attach requirements inclusion to X - Include - JS and X - Include - CSS headers on the given
* HTTP Response
*
* @ param SS_HTTPResponse $response
2008-11-12 05:31:33 +01:00
*/
2012-09-19 12:07:39 +02:00
public function include_in_response ( SS_HTTPResponse $response ) {
2012-12-12 12:00:52 +01:00
$this -> process_combined_files ();
2008-11-13 02:31:31 +01:00
$jsRequirements = array ();
2008-11-13 02:39:30 +01:00
$cssRequirements = array ();
2008-11-12 05:31:33 +01:00
2012-12-12 12:00:52 +01:00
foreach ( array_diff_key ( $this -> javascript , $this -> blocked ) as $file => $dummy ) {
2008-11-12 05:31:33 +01:00
$path = $this -> path_for_file ( $file );
2012-01-06 14:09:43 +01:00
if ( $path ) {
$jsRequirements [] = str_replace ( ',' , '%2C' , $path );
}
2008-11-12 05:31:33 +01:00
}
2012-12-12 12:00:52 +01:00
2008-11-12 05:31:33 +01:00
$response -> addHeader ( 'X-Include-JS' , implode ( ',' , $jsRequirements ));
2012-12-12 12:00:52 +01:00
foreach ( array_diff_key ( $this -> css , $this -> blocked ) as $file => $params ) {
2008-11-12 05:31:33 +01:00
$path = $this -> path_for_file ( $file );
2012-01-06 14:09:43 +01:00
if ( $path ) {
$path = str_replace ( ',' , '%2C' , $path );
$cssRequirements [] = isset ( $params [ 'media' ]) ? " $path :##: $params[media] " : $path ;
}
2008-11-12 05:31:33 +01:00
}
$response -> addHeader ( 'X-Include-CSS' , implode ( ',' , $cssRequirements ));
}
2012-12-12 12:00:52 +01:00
2009-03-10 23:08:52 +01:00
/**
2015-04-29 10:17:08 +02:00
* Add i18n files from the given javascript directory . SilverStripe expects that the given
* directory will contain a number of JavaScript files named by language : en_US . js , de_DE . js ,
* etc .
2012-12-12 12:00:52 +01:00
*
2015-04-29 10:17:08 +02:00
* @ param string $langDir The JavaScript lang directory , relative to the site root , e . g . ,
* 'framework/javascript/lang'
* @ param bool $return Return all relative file paths rather than including them in
* requirements
* @ param bool $langOnly Only include language files , not the base libraries
*
* @ return array
2009-03-10 23:08:52 +01:00
*/
2012-01-06 12:00:04 +01:00
public function add_i18n_javascript ( $langDir , $return = false , $langOnly = false ) {
$files = array ();
2013-10-23 17:53:08 +02:00
$base = Director :: baseFolder () . '/' ;
2013-03-21 19:48:54 +01:00
if ( i18n :: config () -> js_i18n ) {
2009-03-10 23:08:52 +01:00
// Include i18n.js even if no languages are found. The fact that
// add_i18n_javascript() was called indicates that the methods in
// here are needed.
2012-03-24 04:38:57 +01:00
if ( ! $langOnly ) $files [] = FRAMEWORK_DIR . '/javascript/i18n.js' ;
2009-03-10 23:08:52 +01:00
if ( substr ( $langDir , - 1 ) != '/' ) $langDir .= '/' ;
2012-12-12 12:00:52 +01:00
2013-10-23 17:53:08 +02:00
$candidates = array (
'en.js' ,
'en_US.js' ,
i18n :: get_lang_from_locale ( i18n :: default_locale ()) . '.js' ,
i18n :: default_locale () . '.js' ,
i18n :: get_lang_from_locale ( i18n :: get_locale ()) . '.js' ,
i18n :: get_locale () . '.js' ,
);
foreach ( $candidates as $candidate ) {
if ( file_exists ( $base . DIRECTORY_SEPARATOR . $langDir . $candidate )) {
$files [] = $langDir . $candidate ;
}
2013-01-09 22:31:19 +01:00
}
2009-03-10 23:08:52 +01:00
} else {
2013-10-23 17:53:08 +02:00
// Stub i18n implementation for when i18n is disabled.
2012-03-24 04:38:57 +01:00
if ( ! $langOnly ) $files [] = FRAMEWORK_DIR . '/javascript/i18nx.js' ;
2012-01-06 12:00:04 +01:00
}
if ( $return ) {
return $files ;
} else {
foreach ( $files as $file ) $this -> javascript ( $file );
2008-10-03 02:46:07 +02:00
}
2012-12-12 12:00:52 +01:00
}
2008-08-11 09:22:50 +02:00
/**
2015-04-29 10:17:08 +02:00
* Finds the path for specified file
2008-08-11 09:22:50 +02:00
*
* @ param string $fileOrUrl
2015-04-29 10:17:08 +02:00
* @ return string | bool
2008-08-11 09:22:50 +02:00
*/
2010-05-25 06:13:45 +02:00
protected function path_for_file ( $fileOrUrl ) {
2012-08-23 01:13:29 +02:00
if ( preg_match ( '{^//|http[s]?}' , $fileOrUrl )) {
2008-08-11 09:22:50 +02:00
return $fileOrUrl ;
} elseif ( Director :: fileExists ( $fileOrUrl )) {
2012-12-18 00:58:09 +01:00
$filePath = preg_replace ( '/\?.*/' , '' , Director :: baseFolder () . '/' . $fileOrUrl );
2011-03-14 06:26:32 +01:00
$prefix = Director :: baseURL ();
2009-04-29 01:52:15 +02:00
$mtimesuffix = " " ;
2009-04-29 02:07:39 +02:00
$suffix = '' ;
2012-12-18 00:58:09 +01:00
if ( $this -> suffix_requirements ) {
$mtimesuffix = " ?m= " . filemtime ( $filePath );
$suffix = '&' ;
}
2009-04-29 02:07:39 +02:00
if ( strpos ( $fileOrUrl , '?' ) !== false ) {
2012-12-18 00:58:09 +01:00
if ( strlen ( $suffix ) == 0 ) {
$suffix = '?' ;
}
$suffix .= substr ( $fileOrUrl , strpos ( $fileOrUrl , '?' ) + 1 );
2009-04-29 02:07:39 +02:00
$fileOrUrl = substr ( $fileOrUrl , 0 , strpos ( $fileOrUrl , '?' ));
2013-04-30 00:33:21 +02:00
} else {
$suffix = '' ;
2009-04-29 02:07:39 +02:00
}
return " { $prefix } { $fileOrUrl } { $mtimesuffix } { $suffix } " ;
2008-08-11 09:22:50 +02:00
} else {
return false ;
}
}
2012-12-12 12:00:52 +01:00
2008-07-18 01:32:31 +02:00
/**
2015-04-29 10:17:08 +02:00
* Concatenate several css or javascript files into a single dynamically generated file . This
* increases performance by fewer HTTP requests .
2012-12-12 12:00:52 +01:00
*
2015-04-29 10:17:08 +02:00
* The combined file is regenerated based on every file modification time . Optionally a
* rebuild can be triggered by appending ? flush = 1 to the URL . If all files to be combined are
* JavaScript , we use the external JSMin library to minify the JavaScript . This can be
* controlled using { @ link $combine_js_with_jsmin } .
2012-12-12 12:00:52 +01:00
*
2015-04-29 10:17:08 +02:00
* All combined files will have a comment on the start of each concatenated file denoting their
* original position . For easier debugging , we only minify JavaScript if not in development
* mode ({ @ link Director :: isDev ()}) .
2012-12-12 12:00:52 +01:00
*
2015-04-29 10:17:08 +02:00
* CAUTION : You ' re responsible for ensuring that the load order for combined files is
* retained - otherwise combining JavaScript files can lead to functional errors in the
* JavaScript logic , and combining CSS can lead to incorrect inheritance . You can also
* only include each file once across all includes and combinations in a single page load .
2012-12-12 12:00:52 +01:00
*
2008-09-22 18:02:33 +02:00
* CAUTION : Combining CSS Files discards any " media " information .
2008-07-18 01:32:31 +02:00
*
* Example for combined JavaScript :
* < code >
* Requirements :: combine_files (
* 'foobar.js' ,
* array (
2015-04-29 10:17:08 +02:00
* 'mysite/javascript/foo.js' ,
* 'mysite/javascript/bar.js' ,
* )
2008-07-18 01:32:31 +02:00
* );
* </ code >
*
* Example for combined CSS :
* < code >
* Requirements :: combine_files (
* 'foobar.css' ,
2015-04-29 10:17:08 +02:00
* array (
* 'mysite/javascript/foo.css' ,
* 'mysite/javascript/bar.css' ,
* )
2008-07-18 01:32:31 +02:00
* );
* </ code >
*
2015-04-29 10:17:08 +02:00
* @ param string $combinedFileName Filename of the combined file relative to docroot
* @ param array $files Array of filenames relative to docroot
*
* @ return bool | void
2008-07-18 01:32:31 +02:00
*/
2013-02-12 22:54:53 +01:00
public function combine_files ( $combinedFileName , $files , $media = null ) {
2008-07-18 05:47:17 +02:00
// duplicate check
2008-11-07 03:06:28 +01:00
foreach ( $this -> combine_files as $_combinedFileName => $_files ) {
2008-07-18 05:47:17 +02:00
$duplicates = array_intersect ( $_files , $files );
2010-10-15 05:01:03 +02:00
if ( $duplicates && $combinedFileName != $_combinedFileName ) {
2012-09-26 23:34:00 +02:00
user_error ( " Requirements_Backend::combine_files(): Already included files " . implode ( ',' , $duplicates )
. " in combined file ' { $_combinedFileName } ' " , E_USER_NOTICE );
2008-07-18 05:47:17 +02:00
return false ;
}
}
2010-10-15 05:09:15 +02:00
foreach ( $files as $index => $file ) {
if ( is_array ( $file )) {
// Either associative array path=>path type=>type or numeric 0=>path 1=>type
// Otherwise, assume path is the first item
2012-09-26 23:34:00 +02:00
if ( isset ( $file [ 'type' ]) && in_array ( $file [ 'type' ], array ( 'css' , 'javascript' , 'js' ))) {
2010-10-15 05:09:15 +02:00
switch ( $file [ 'type' ]) {
case 'css' :
2013-02-12 22:54:53 +01:00
$this -> css ( $file [ 'path' ], $media );
2010-10-15 05:09:15 +02:00
break ;
default :
$this -> javascript ( $file [ 'path' ]);
break ;
}
$files [ $index ] = $file [ 'path' ];
2012-09-26 23:34:00 +02:00
} elseif ( isset ( $file [ 1 ]) && in_array ( $file [ 1 ], array ( 'css' , 'javascript' , 'js' ))) {
2010-10-15 05:09:15 +02:00
switch ( $file [ 1 ]) {
case 'css' :
2013-02-12 22:54:53 +01:00
$this -> css ( $file [ 0 ], $media );
2010-10-15 05:09:15 +02:00
break ;
default :
$this -> javascript ( $file [ 0 ]);
break ;
}
$files [ $index ] = $file [ 0 ];
} else {
$file = array_shift ( $file );
}
}
if ( ! is_array ( $file )) {
if ( substr ( $file , - 2 ) == 'js' ) {
$this -> javascript ( $file );
} elseif ( substr ( $file , - 3 ) == 'css' ) {
2013-02-12 22:54:53 +01:00
$this -> css ( $file , $media );
2010-10-15 05:09:15 +02:00
} else {
2012-09-26 23:34:00 +02:00
user_error ( " Requirements_Backend::combine_files(): Couldn't guess file type for file ' $file ', "
. " please specify by passing using an array instead. " , E_USER_NOTICE );
2010-10-15 05:09:15 +02:00
}
}
}
2008-11-07 03:06:28 +01:00
$this -> combine_files [ $combinedFileName ] = $files ;
2008-07-18 05:47:17 +02:00
}
2012-12-12 12:00:52 +01:00
2015-04-29 10:17:08 +02:00
/**
* Return all combined files ; keys are the combined file names , values are lists of
* files being combined .
*
2008-07-18 05:47:17 +02:00
* @ return array
*/
2012-09-19 12:07:39 +02:00
public function get_combine_files () {
2008-11-07 03:06:28 +01:00
return $this -> combine_files ;
2008-07-18 05:47:17 +02:00
}
2012-12-12 12:00:52 +01:00
2008-07-18 05:47:17 +02:00
/**
2015-04-29 10:17:08 +02:00
* Delete all dynamically generated combined files from the filesystem
2012-12-12 12:00:52 +01:00
*
2008-07-18 05:47:17 +02:00
* @ param string $combinedFileName If left blank , all combined files are deleted .
*/
2012-09-19 12:07:39 +02:00
public function delete_combined_files ( $combinedFileName = null ) {
2008-11-07 03:06:28 +01:00
$combinedFiles = ( $combinedFileName ) ? array ( $combinedFileName => null ) : $this -> combine_files ;
2012-12-12 12:00:52 +01:00
$combinedFolder = ( $this -> getCombinedFilesFolder ()) ?
2012-09-26 23:34:00 +02:00
( Director :: baseFolder () . '/' . $this -> combinedFilesFolder ) : Director :: baseFolder ();
2008-07-18 05:47:17 +02:00
foreach ( $combinedFiles as $combinedFile => $sourceItems ) {
2010-05-25 06:15:43 +02:00
$filePath = $combinedFolder . '/' . $combinedFile ;
2008-07-18 05:47:17 +02:00
if ( file_exists ( $filePath )) {
unlink ( $filePath );
}
}
2008-07-18 01:32:31 +02:00
}
2012-12-12 12:00:52 +01:00
2014-08-16 02:51:17 +02:00
/**
* Deletes all generated combined files in the configured combined files directory ,
* but doesn ' t delete the directory itself .
*/
public function delete_all_combined_files () {
$combinedFolder = $this -> getCombinedFilesFolder ();
if ( ! $combinedFolder ) return false ;
$path = Director :: baseFolder () . '/' . $combinedFolder ;
if ( file_exists ( $path )) {
Filesystem :: removeFolder ( $path , true );
}
}
2015-04-29 10:17:08 +02:00
/**
* Clear all registered CSS and JavaScript file combinations
*/
2012-09-19 12:07:39 +02:00
public function clear_combined_files () {
2008-11-07 03:06:28 +01:00
$this -> combine_files = array ();
2008-08-12 01:18:56 +02:00
}
2008-11-07 03:06:28 +01:00
2008-07-18 01:32:31 +02:00
/**
2015-04-29 10:17:08 +02:00
* Do the heavy lifting involved in combining ( and , in the case of JavaScript minifying ) the
* combined files .
2008-11-07 03:06:28 +01:00
*/
2012-09-19 12:07:39 +02:00
public function process_combined_files () {
2015-04-29 10:17:08 +02:00
// The class_exists call prevents us loading SapphireTest.php (slow) just to know that
2009-04-27 02:19:16 +02:00
// SapphireTest isn't running :-)
2010-05-25 06:16:33 +02:00
if ( class_exists ( 'SapphireTest' , false )) $runningTest = SapphireTest :: is_running_test ();
2009-04-27 02:19:16 +02:00
else $runningTest = false ;
2012-12-12 12:00:52 +01:00
2012-06-29 08:35:55 +02:00
if (( Director :: isDev () && ! $runningTest && ! isset ( $_REQUEST [ 'combine' ])) || ! $this -> combined_files_enabled ) {
2008-11-12 22:15:58 +01:00
return ;
}
2012-09-20 04:00:11 +02:00
2008-07-18 01:32:31 +02:00
// Make a map of files that could be potentially combined
$combinerCheck = array ();
2008-11-07 03:06:28 +01:00
foreach ( $this -> combine_files as $combinedFile => $sourceItems ) {
2008-07-18 01:32:31 +02:00
foreach ( $sourceItems as $sourceItem ) {
2012-12-12 12:00:52 +01:00
if ( isset ( $combinerCheck [ $sourceItem ]) && $combinerCheck [ $sourceItem ] != $combinedFile ){
2012-09-26 23:34:00 +02:00
user_error ( " Requirements_Backend::process_combined_files - file ' $sourceItem ' appears in two " .
" combined files: " . " ' { $combinerCheck [ $sourceItem ] } ' and ' $combinedFile ' " , E_USER_WARNING );
2008-07-18 01:32:31 +02:00
}
$combinerCheck [ $sourceItem ] = $combinedFile ;
2012-12-12 12:00:52 +01:00
2008-07-18 01:32:31 +02:00
}
}
2010-05-25 06:15:43 +02:00
// Work out the relative URL for the combined files from the base folder
2010-10-15 04:37:41 +02:00
$combinedFilesFolder = ( $this -> getCombinedFilesFolder ()) ? ( $this -> getCombinedFilesFolder () . '/' ) : '' ;
2010-05-25 06:15:43 +02:00
2015-04-29 10:17:08 +02:00
// Figure out which ones apply to this request
2008-07-18 01:32:31 +02:00
$combinedFiles = array ();
$newJSRequirements = array ();
$newCSSRequirements = array ();
2008-11-07 03:06:28 +01:00
foreach ( $this -> javascript as $file => $dummy ) {
2008-07-18 01:32:31 +02:00
if ( isset ( $combinerCheck [ $file ])) {
2010-05-25 06:15:43 +02:00
$newJSRequirements [ $combinedFilesFolder . $combinerCheck [ $file ]] = true ;
2008-07-18 01:32:31 +02:00
$combinedFiles [ $combinerCheck [ $file ]] = true ;
} else {
$newJSRequirements [ $file ] = true ;
}
}
2012-12-12 12:00:52 +01:00
2008-11-07 03:06:28 +01:00
foreach ( $this -> css as $file => $params ) {
2008-07-18 01:32:31 +02:00
if ( isset ( $combinerCheck [ $file ])) {
2013-02-12 22:54:53 +01:00
// Inherit the parameters from the last file in the combine set.
$newCSSRequirements [ $combinedFilesFolder . $combinerCheck [ $file ]] = $params ;
2008-07-18 01:32:31 +02:00
$combinedFiles [ $combinerCheck [ $file ]] = true ;
} else {
$newCSSRequirements [ $file ] = $params ;
}
}
2009-02-02 00:49:53 +01:00
2008-07-18 01:32:31 +02:00
// Process the combined files
2008-07-18 05:47:17 +02:00
$base = Director :: baseFolder () . '/' ;
2010-05-25 06:15:43 +02:00
foreach ( array_diff_key ( $combinedFiles , $this -> blocked ) as $combinedFile => $dummy ) {
2008-11-07 03:06:28 +01:00
$fileList = $this -> combine_files [ $combinedFile ];
2010-10-15 04:37:41 +02:00
$combinedFilePath = $base . $combinedFilesFolder . '/' . $combinedFile ;
2008-07-18 01:32:31 +02:00
2010-10-04 06:28:48 +02:00
// Make the folder if necessary
if ( ! file_exists ( dirname ( $combinedFilePath ))) {
Filesystem :: makeFolder ( dirname ( $combinedFilePath ));
}
2012-09-20 04:00:11 +02:00
2015-04-29 10:17:08 +02:00
// If the file isn't writeable, don't even bother trying to make the combined file and return. The
// files will be included individually instead. This is a complex test because is_writable fails
// if the file doesn't exist yet.
2012-09-20 04:00:11 +02:00
if (( file_exists ( $combinedFilePath ) && ! is_writable ( $combinedFilePath ))
|| ( ! file_exists ( $combinedFilePath ) && ! is_writable ( dirname ( $combinedFilePath )))
) {
2012-09-26 23:34:00 +02:00
user_error ( " Requirements_Backend::process_combined_files(): Couldn't create ' $combinedFilePath ' " ,
E_USER_WARNING );
2012-09-20 04:00:11 +02:00
return false ;
2010-10-04 06:28:48 +02:00
}
2012-09-20 04:00:11 +02:00
// Determine if we need to build the combined include
2014-08-16 02:51:17 +02:00
if ( file_exists ( $combinedFilePath )) {
2008-07-18 05:47:17 +02:00
// file exists, check modification date of every contained file
$srcLastMod = 0 ;
foreach ( $fileList as $file ) {
2012-12-08 12:20:20 +01:00
if ( file_exists ( $base . $file )) {
$srcLastMod = max ( filemtime ( $base . $file ), $srcLastMod );
}
2008-07-18 01:32:31 +02:00
}
2010-05-25 06:18:48 +02:00
$refresh = $srcLastMod > filemtime ( $combinedFilePath );
2008-07-18 05:47:17 +02:00
} else {
2015-04-29 10:17:08 +02:00
// File doesn't exist, or refresh was explicitly required
2008-07-18 05:47:17 +02:00
$refresh = true ;
}
2008-07-18 01:32:31 +02:00
2008-07-18 05:47:17 +02:00
if ( ! $refresh ) continue ;
2014-11-26 03:27:54 +01:00
$failedToMinify = false ;
2008-07-18 05:47:17 +02:00
$combinedData = " " ;
2010-05-25 06:16:33 +02:00
foreach ( array_diff ( $fileList , $this -> blocked ) as $file ) {
2008-07-18 05:47:17 +02:00
$fileContent = file_get_contents ( $base . $file );
2014-08-25 01:49:38 +02:00
try {
$fileContent = $this -> minifyFile ( $file , $fileContent );
} catch ( Exception $e ){
2014-11-26 03:27:54 +01:00
$failedToMinify = true ;
2014-08-25 01:49:38 +02:00
}
2012-12-12 12:00:52 +01:00
2013-06-04 14:47:44 +02:00
if ( $this -> write_header_comment ) {
2015-04-29 10:17:08 +02:00
// Write a header comment for each file for easier identification and debugging. The semicolon between each file is required for jQuery to be combined properly and protects against unterminated statements.
2013-06-04 14:47:44 +02:00
$combinedData .= " /****** FILE: $file *****/ \n " ;
2008-07-18 01:32:31 +02:00
}
2013-06-04 14:47:44 +02:00
$combinedData .= $fileContent . " \n " ;
2008-07-18 01:32:31 +02:00
}
2010-05-25 06:15:43 +02:00
2008-11-18 02:48:37 +01:00
$successfulWrite = false ;
2010-05-25 06:15:43 +02:00
$fh = fopen ( $combinedFilePath , 'wb' );
2008-11-18 02:48:37 +01:00
if ( $fh ) {
if ( fwrite ( $fh , $combinedData ) == strlen ( $combinedData )) $successfulWrite = true ;
fclose ( $fh );
unset ( $fh );
}
2014-11-26 03:27:54 +01:00
if ( $failedToMinify ){
2015-04-29 10:17:08 +02:00
// Failed to minify, use unminified files instead. This warning is raised at the end to allow code execution
2014-11-26 03:27:54 +01:00
// to complete in case this warning is caught inside a try-catch block.
user_error ( 'Failed to minify ' . $file . ', exception: ' . $e -> getMessage (), E_USER_WARNING );
}
2009-02-02 00:49:53 +01:00
2008-11-18 02:48:37 +01:00
// Unsuccessful write - just include the regular JS files, rather than the combined one
if ( ! $successfulWrite ) {
2012-09-26 23:34:00 +02:00
user_error ( " Requirements_Backend::process_combined_files(): Couldn't create ' $combinedFilePath ' " ,
E_USER_WARNING );
2010-10-04 06:28:48 +02:00
continue ;
2008-11-18 02:48:37 +01:00
}
2008-07-18 05:47:17 +02:00
}
2008-11-18 02:48:37 +01:00
2015-04-29 10:17:08 +02:00
// Note: Alters the original information, which means you can't call this method repeatedly - it will behave
// differently on the subsequent calls
2008-11-18 02:48:37 +01:00
$this -> javascript = $newJSRequirements ;
$this -> css = $newCSSRequirements ;
2012-12-08 12:20:20 +01:00
}
2015-04-29 10:17:08 +02:00
/**
* Minify the given $content according to the file type indicated in $filename
*
* @ param string $filename
* @ param string $content
* @ return string
*/
2013-06-04 14:47:44 +02:00
protected function minifyFile ( $filename , $content ) {
// if we have a javascript file and jsmin is enabled, minify the content
$isJS = stripos ( $filename , '.js' );
if ( $isJS && $this -> combine_js_with_jsmin ) {
require_once ( 'thirdparty/jsmin/jsmin.php' );
increase_time_limit_to ();
$content = JSMin :: minify ( $content );
}
$content .= ( $isJS ? ';' : '' ) . " \n " ;
return $content ;
}
2008-11-07 03:06:28 +01:00
/**
2015-04-29 10:17:08 +02:00
* Registers the given themeable stylesheet as required .
*
* A CSS file in the current theme path name 'themename/css/$name.css' is first searched for ,
* and it that doesn ' t exist and the module parameter is set then a CSS file with that name in
* the module is used .
*
* @ param string $name The name of the file - eg '/css/File.css' would have the name 'File'
* @ param string $module The module to fall back to if the css file does not exist in the
* current theme .
* @ param string $media Comma - separated list of media types to use in the link tag
* ( e . g . 'screen,projector' )
2008-11-07 03:06:28 +01:00
*/
2011-03-22 15:02:11 +01:00
public function themedCSS ( $name , $module = null , $media = null ) {
2013-10-25 23:42:01 +02:00
$theme = SSViewer :: get_theme_folder ();
$project = project ();
$absbase = BASE_PATH . DIRECTORY_SEPARATOR ;
$abstheme = $absbase . $theme ;
$absproject = $absbase . $project ;
2012-06-22 03:34:03 +02:00
$css = " /css/ $name .css " ;
2013-10-25 23:42:01 +02:00
if ( file_exists ( $absproject . $css )) {
$this -> css ( $project . $css , $media );
} elseif ( $module && file_exists ( $abstheme . '_' . $module . $css )) {
$this -> css ( $theme . '_' . $module . $css , $media );
} elseif ( file_exists ( $abstheme . $css )) {
$this -> css ( $theme . $css , $media );
} elseif ( $module ) {
$this -> css ( $module . $css , $media );
2011-03-22 15:02:11 +01:00
}
2008-11-07 03:06:28 +01:00
}
2012-12-12 12:00:52 +01:00
2015-04-29 10:17:08 +02:00
/**
* Output debugging information .
*/
2012-09-19 12:07:39 +02:00
public function debug () {
2008-11-07 03:06:28 +01:00
Debug :: show ( $this -> javascript );
Debug :: show ( $this -> css );
Debug :: show ( $this -> customCSS );
Debug :: show ( $this -> customScript );
Debug :: show ( $this -> customHeadTags );
Debug :: show ( $this -> combine_files );
2007-07-19 12:40:28 +02:00
}
2012-12-12 12:00:52 +01:00
2012-03-24 04:38:57 +01:00
}