2010-02-07 08:45:21 +00:00
< ? php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Library functions to facilitate the use of JavaScript in Moodle .
*
2012-01-05 13:28:24 +13:00
* Note : you can find history of this file in lib / ajax / ajaxlib . php
*
* @ copyright 2009 Tim Hunt , 2010 Petr Skoda
* @ license http :// www . gnu . org / copyleft / gpl . html GNU GPL v3 or later
2012-02-14 18:24:09 +13:00
* @ package core
2012-01-11 10:40:55 +13:00
* @ category output
2010-02-07 08:45:21 +00:00
*/
2010-07-25 13:35:05 +00:00
defined ( 'MOODLE_INTERNAL' ) || die ();
2010-02-07 08:45:21 +00:00
/**
* This class tracks all the things that are needed by the current page .
*
* Normally , the only instance of this class you will need to work with is the
* one accessible via $PAGE -> requires .
*
2010-05-22 19:34:17 +00:00
* Typical usage would be
2010-02-07 08:45:21 +00:00
* < pre >
2011-02-13 17:44:01 +01:00
* $PAGE -> requires -> js_init_call ( 'M.mod_forum.init_view' );
2010-02-07 08:45:21 +00:00
* </ pre >
*
* It also supports obsoleted coding style withouth YUI3 modules .
* < pre >
2010-05-22 19:34:17 +00:00
* $PAGE -> requires -> css ( '/mod/mymod/userstyles.php?id=' . $id ); // not overridable via themes!
2010-02-07 08:45:21 +00:00
* $PAGE -> requires -> js ( '/mod/mymod/script.js' );
* $PAGE -> requires -> js ( '/mod/mymod/small_but_urgent.js' , true );
* $PAGE -> requires -> js_function_call ( 'init_mymod' , array ( $data ), true );
* </ pre >
*
* There are some natural restrictions on some methods . For example , { @ link css ()}
* can only be called before the < head > tag is output . See the comments on the
* individual methods for details .
*
* @ copyright 2009 Tim Hunt , 2010 Petr Skoda
2012-01-05 13:28:24 +13:00
* @ license http :// www . gnu . org / copyleft / gpl . html GNU GPL v3 or later
2010-02-07 08:45:21 +00:00
* @ since Moodle 2.0
2012-02-14 18:24:09 +13:00
* @ package core
2012-01-11 10:40:55 +13:00
* @ category output
2010-02-07 08:45:21 +00:00
*/
class page_requirements_manager {
2012-01-05 13:28:24 +13:00
/**
2012-01-11 10:40:55 +13:00
* @ var array List of string available from JS
2012-01-05 13:28:24 +13:00
*/
2010-02-07 08:45:21 +00:00
protected $stringsforjs = array ();
2012-01-05 13:28:24 +13:00
2012-06-21 09:58:45 +02:00
/**
* @ var array List of get_string $a parameters - used for validation only .
*/
protected $stringsforjs_as = array ();
2012-01-05 13:28:24 +13:00
/**
2012-01-11 10:40:55 +13:00
* @ var array List of JS variables to be initialised
2012-01-05 13:28:24 +13:00
*/
2010-02-07 08:45:21 +00:00
protected $jsinitvariables = array ( 'head' => array (), 'footer' => array ());
2012-01-05 13:28:24 +13:00
/**
2012-01-11 10:40:55 +13:00
* @ var array Included JS scripts
2012-01-05 13:28:24 +13:00
*/
2010-02-07 08:45:21 +00:00
protected $jsincludes = array ( 'head' => array (), 'footer' => array ());
2012-01-05 13:28:24 +13:00
/**
2012-01-11 10:40:55 +13:00
* @ var array List of needed function calls
2012-01-05 13:28:24 +13:00
*/
2010-02-07 08:45:21 +00:00
protected $jscalls = array ( 'normal' => array (), 'ondomready' => array ());
2012-01-05 13:28:24 +13:00
2010-02-07 08:45:21 +00:00
/**
2012-01-11 10:40:55 +13:00
* @ var array List of skip links , those are needed for accessibility reasons
2010-02-07 08:45:21 +00:00
*/
protected $skiplinks = array ();
2012-01-05 13:28:24 +13:00
2010-02-07 08:45:21 +00:00
/**
2012-01-11 10:40:55 +13:00
* @ var array Javascript code used for initialisation of page , it should
* be relatively small
2010-02-07 08:45:21 +00:00
*/
protected $jsinitcode = array ();
2012-01-05 13:28:24 +13:00
2010-02-07 08:45:21 +00:00
/**
2012-01-11 10:40:55 +13:00
* @ var array of moodle_url Theme sheets , initialised only from core_renderer
2010-02-07 08:45:21 +00:00
*/
protected $cssthemeurls = array ();
2012-01-11 10:40:55 +13:00
2010-02-07 08:45:21 +00:00
/**
2012-01-11 10:40:55 +13:00
* @ var array of moodle_url List of custom theme sheets , these are strongly discouraged !
2010-02-07 08:45:21 +00:00
* Useful mostly only for CSS submitted by teachers that is not part of the theme .
*/
protected $cssurls = array ();
2012-01-05 13:28:24 +13:00
2010-02-07 08:45:21 +00:00
/**
2012-01-11 10:40:55 +13:00
* @ var array List of requested event handlers
2010-02-07 08:45:21 +00:00
*/
protected $eventhandlers = array ();
2012-01-05 13:28:24 +13:00
2010-02-07 08:45:21 +00:00
/**
2012-01-11 10:40:55 +13:00
* @ var array Extra modules
2010-02-07 08:45:21 +00:00
*/
protected $extramodules = array ();
2012-01-05 13:28:24 +13:00
/**
2012-01-11 10:40:55 +13:00
* @ var bool Flag indicated head stuff already printed
2012-01-05 13:28:24 +13:00
*/
2010-02-07 08:45:21 +00:00
protected $headdone = false ;
2012-01-05 13:28:24 +13:00
/**
2012-01-11 10:40:55 +13:00
* @ var bool Flag indicating top of body already printed
2012-01-05 13:28:24 +13:00
*/
2010-02-07 08:45:21 +00:00
protected $topofbodydone = false ;
2012-01-05 13:28:24 +13:00
/**
2012-01-11 10:40:55 +13:00
* @ var stdClass YUI PHPLoader instance responsible for YUI3 loading from PHP only
2012-01-05 13:28:24 +13:00
*/
2010-02-07 08:45:21 +00:00
protected $yui3loader ;
2012-01-05 13:28:24 +13:00
/**
2012-08-12 11:41:53 +02:00
* @ var stdClass default YUI loader configuration
2012-01-05 13:28:24 +13:00
*/
2012-08-06 10:21:37 +02:00
protected $YUI_config ;
2012-01-05 13:28:24 +13:00
/**
2012-01-11 10:40:55 +13:00
* @ var array Some config vars exposed in JS , please no secret stuff there
2012-01-05 13:28:24 +13:00
*/
2010-02-07 08:45:21 +00:00
protected $M_cfg ;
2012-01-05 13:28:24 +13:00
/**
2012-01-11 10:40:55 +13:00
* @ var array Stores debug backtraces from when JS modules were included in the page
2012-01-05 13:28:24 +13:00
*/
2010-11-12 05:26:47 +00:00
protected $debug_moduleloadstacktraces = array ();
2010-02-07 08:45:21 +00:00
/**
* Page requirements constructor .
*/
public function __construct () {
global $CFG ;
2012-06-12 10:13:17 +02:00
// You may need to set up URL rewrite rule because oversized URLs might not be allowed by web server.
$sep = empty ( $CFG -> yuislasharguments ) ? '?' : '/' ;
2012-05-05 15:59:43 +02:00
2011-08-19 10:21:30 +02:00
$this -> yui3loader = new stdClass ();
2010-02-07 08:45:21 +00:00
2012-08-12 11:41:53 +02:00
// Set up some loader options.
2010-02-07 08:45:21 +00:00
if ( debugging ( '' , DEBUG_DEVELOPER )) {
2012-08-12 11:41:53 +02:00
$this -> yui3loader -> filter = 'RAW' ; // For more detailed logging info use 'DEBUG' here.
2010-02-07 08:45:21 +00:00
} else {
$this -> yui3loader -> filter = null ;
}
2012-01-15 15:24:34 +01:00
if ( ! empty ( $CFG -> useexternalyui ) and strpos ( $CFG -> httpswwwroot , 'https:' ) !== 0 ) {
2010-02-07 08:45:21 +00:00
$this -> yui3loader -> base = 'http://yui.yahooapis.com/' . $CFG -> yui3version . '/build/' ;
$this -> yui3loader -> comboBase = 'http://yui.yahooapis.com/combo?' ;
} else {
2012-08-05 21:30:18 +02:00
$this -> yui3loader -> base = $CFG -> httpswwwroot . '/lib/yuilib/' . $CFG -> yui3version . '/build/' ;
2012-05-05 15:59:43 +02:00
$this -> yui3loader -> comboBase = $CFG -> httpswwwroot . '/theme/yui_combo.php' . $sep ;
2010-02-07 08:45:21 +00:00
}
2012-08-12 11:41:53 +02:00
// Enable combo loader? This significantly helps with caching and performance!
2010-02-07 08:45:21 +00:00
$this -> yui3loader -> combine = ! empty ( $CFG -> yuicomboloading );
2010-08-05 04:05:28 +00:00
if ( empty ( $CFG -> cachejs )) {
$jsrev = - 1 ;
} else if ( empty ( $CFG -> jsrev )) {
$jsrev = 1 ;
} else {
$jsrev = $CFG -> jsrev ;
}
2010-09-06 06:07:47 +00:00
2012-08-12 11:41:53 +02:00
// Set up JS YUI loader helper object.
2012-08-06 10:21:37 +02:00
$this -> YUI_config = new stdClass ();
$this -> YUI_config -> base = $this -> yui3loader -> base ;
$this -> YUI_config -> comboBase = $this -> yui3loader -> comboBase ;
$this -> YUI_config -> combine = $this -> yui3loader -> combine ;
$this -> YUI_config -> filter = ( string ) $this -> yui3loader -> filter ;
$this -> YUI_config -> insertBefore = 'firstthemesheet' ;
$this -> YUI_config -> modules = array ();
$this -> YUI_config -> groups = array (
2012-08-12 11:41:53 +02:00
// Loader for our YUI modules stored in /yui/ subdirectories of our plugins and subsystems.
2010-07-01 02:26:21 +00:00
'moodle' => array (
'name' => 'moodle' ,
2012-05-05 15:59:43 +02:00
'base' => $CFG -> httpswwwroot . '/theme/yui_combo.php' . $sep . 'moodle/' . $jsrev . '/' ,
'comboBase' => $CFG -> httpswwwroot . '/theme/yui_combo.php' . $sep ,
2010-07-01 02:26:21 +00:00
'combine' => $this -> yui3loader -> combine ,
'filter' => '' ,
'ext' => false ,
2012-08-12 11:41:53 +02:00
'root' => 'moodle/' . $jsrev . '/' , // Add the rev to the root path so that we can control caching.
2010-07-01 02:26:21 +00:00
'patterns' => array (
'moodle-' => array (
'group' => 'moodle' ,
'configFn' => '@MOODLECONFIGFN@'
2012-08-02 17:40:50 +02:00
)
2010-06-04 01:49:53 +00:00
)
2010-07-01 02:26:21 +00:00
),
2012-08-12 11:41:53 +02:00
// Gallery modules are not supported much, sorry.
2010-07-01 02:26:21 +00:00
'local' => array (
'name' => 'gallery' ,
2012-08-05 21:30:18 +02:00
'base' => $CFG -> httpswwwroot . '/lib/yui/gallery/' ,
2012-05-05 15:59:43 +02:00
'comboBase' => $CFG -> httpswwwroot . '/theme/yui_combo.php' . $sep ,
2010-07-01 02:26:21 +00:00
'combine' => $this -> yui3loader -> combine ,
2012-08-06 10:21:37 +02:00
'filter' => $this -> YUI_config -> filter ,
2010-07-01 02:26:21 +00:00
'ext' => false ,
'root' => 'gallery/' ,
'patterns' => array (
'gallery-' => array (
'group' => 'gallery' ,
'configFn' => '@GALLERYCONFIGFN@' ,
2012-08-02 17:40:50 +02:00
)
2010-07-01 02:26:21 +00:00
)
2012-08-05 21:30:18 +02:00
),
2012-08-12 11:41:53 +02:00
// Loader configuration for our 2in3, for now ignores $CFG->useexternalyui.
2012-08-05 21:30:18 +02:00
'yui2' => array (
'base' => $CFG -> httpswwwroot . '/lib/yuilib/2in3/' . $CFG -> yui2version . '/build/' ,
'comboBase' => $CFG -> httpswwwroot . '/theme/yui_combo.php' . $sep ,
'combine' => $this -> yui3loader -> combine ,
'ext' => false ,
'root' => '2in3/' . $CFG -> yui2version . '/build/' ,
'patterns' => array (
'yui2-' => array (
'group' => 'yui2' ,
'configFn' => '@2IN3CONFIGFN@'
)
)
2010-07-01 02:26:21 +00:00
)
);
2012-08-12 11:41:53 +02:00
// Every page should include definition of following modules.
2011-08-03 18:20:01 +08:00
$this -> js_module ( $this -> find_module ( 'core_filepicker' ));
2010-02-07 08:45:21 +00:00
$this -> js_module ( $this -> find_module ( 'core_dock' ));
}
/**
* Initialise with the bits of JavaScript that every Moodle page should have .
*
* @ param moodle_page $page
2011-12-10 13:16:18 +01:00
* @ param core_renderer $renderer
2010-02-07 08:45:21 +00:00
*/
protected function init_requirements_data ( moodle_page $page , core_renderer $renderer ) {
global $CFG ;
// JavaScript should always work with $CFG->httpswwwroot rather than $CFG->wwwroot.
// Otherwise, in some situations, users will get warnings about insecure content
2010-02-11 13:27:02 +00:00
// on secure pages from their web browser.
2010-02-07 08:45:21 +00:00
$this -> M_cfg = array (
'wwwroot' => $CFG -> httpswwwroot , // Yes, really. See above.
'sesskey' => sesskey (),
'loadingicon' => $renderer -> pix_url ( 'i/loading_small' , 'moodle' ) -> out ( false ),
'themerev' => theme_get_revision (),
2012-05-05 14:45:26 +02:00
'slasharguments' => ( int )( ! empty ( $CFG -> slasharguments )),
2010-02-07 08:45:21 +00:00
'theme' => $page -> theme -> name ,
2011-03-12 17:42:52 +01:00
'jsrev' => (( empty ( $CFG -> cachejs ) or empty ( $CFG -> jsrev )) ? - 1 : $CFG -> jsrev ),
2012-09-27 10:58:42 +12:00
'svgicons' => $page -> theme -> use_svg_icons ()
2010-02-07 08:45:21 +00:00
);
if ( debugging ( '' , DEBUG_DEVELOPER )) {
$this -> M_cfg [ 'developerdebug' ] = true ;
}
2012-08-12 11:41:53 +02:00
// Accessibility stuff.
2010-05-04 08:29:05 +00:00
$this -> skip_link_to ( 'maincontent' , get_string ( 'tocontent' , 'access' ));
2010-02-07 08:45:21 +00:00
2012-08-12 11:41:53 +02:00
// Add strings used on many pages.
2010-02-07 08:45:21 +00:00
$this -> string_for_js ( 'confirmation' , 'admin' );
$this -> string_for_js ( 'cancel' , 'moodle' );
$this -> string_for_js ( 'yes' , 'moodle' );
2010-05-21 08:13:08 +00:00
2012-08-12 11:41:53 +02:00
// Alter links in top frame to break out of frames.
2010-02-11 13:27:02 +00:00
if ( $page -> pagelayout === 'frametop' ) {
$this -> js_init_call ( 'M.util.init_frametop' );
}
2010-02-07 08:45:21 +00:00
}
/**
* Ensure that the specified JavaScript file is linked to from this page .
*
2012-08-12 11:41:53 +02:00
* NOTE : This function is to be used in RARE CASES ONLY , please store your JS in module . js file
* and use $PAGE -> requires -> js_init_call () instead or use / yui / subdirectories for YUI modules .
2010-02-07 08:45:21 +00:00
*
* By default the link is put at the end of the page , since this gives best page - load performance .
*
* Even if a particular script is requested more than once , it will only be linked
* to once .
*
* @ param string | moodle_url $url The path to the . js file , relative to $CFG -> dirroot / $CFG -> wwwroot .
* For example '/mod/mymod/customscripts.js' ; use moodle_url for external scripts
* @ param bool $inhead initialise in head
*/
2012-01-05 13:28:24 +13:00
public function js ( $url , $inhead = false ) {
2010-02-07 08:45:21 +00:00
$url = $this -> js_fix_url ( $url );
$where = $inhead ? 'head' : 'footer' ;
$this -> jsincludes [ $where ][ $url -> out ()] = $url ;
}
/**
2012-08-12 11:41:53 +02:00
* This method was used to load YUI2 libraries into global scope ,
* use YUI 2 in3 instead . Every YUI2 module is represented as a yui2 -*
* sandboxed module in YUI3 code via Y . YUI2 . property .
2010-02-07 08:45:21 +00:00
*
2012-08-12 11:41:53 +02:00
* { @ see http :// tracker . moodle . org / browse / MDL - 34741 }
2010-02-07 08:45:21 +00:00
*
2012-08-12 11:41:53 +02:00
* @ param string | array $libname
* @ deprecated since 2.4
2010-02-07 08:45:21 +00:00
*/
public function yui2_lib ( $libname ) {
2012-08-12 11:41:53 +02:00
throw new coding_exception ( 'PAGE->yui2_lib() is not available any more, use YUI 2in3 instead, see MDL-34741 for more information.' );
2010-02-07 08:45:21 +00:00
}
/**
* Returns the actual url through which a script is served .
2012-01-05 13:28:24 +13:00
*
2010-02-07 08:45:21 +00:00
* @ param moodle_url | string $url full moodle url , or shortened path to script
* @ return moodle_url
*/
protected function js_fix_url ( $url ) {
global $CFG ;
if ( $url instanceof moodle_url ) {
return $url ;
} else if ( strpos ( $url , '/' ) === 0 ) {
2012-07-05 19:33:06 +02:00
// Fix the admin links if needed.
if ( $CFG -> admin !== 'admin' ) {
if ( strpos ( $url , " /admin/ " ) === 0 ) {
$url = preg_replace ( " |^/admin/| " , " / $CFG->admin / " , $url );
}
}
2010-02-07 08:45:21 +00:00
if ( debugging ()) {
2012-08-12 11:41:53 +02:00
// Check file existence only when in debug mode.
2010-02-07 08:45:21 +00:00
if ( ! file_exists ( $CFG -> dirroot . strtok ( $url , '?' ))) {
2010-08-03 14:32:28 +00:00
throw new coding_exception ( 'Attempt to require a JavaScript file that does not exist.' , $url );
2010-02-07 08:45:21 +00:00
}
}
2012-09-07 13:56:16 +02:00
if ( ! empty ( $CFG -> cachejs ) and ! empty ( $CFG -> jsrev ) and $CFG -> jsrev > 0 and substr ( $url , - 3 ) === '.js' ) {
2012-05-05 18:16:50 +02:00
if ( empty ( $CFG -> slasharguments )) {
2012-05-08 19:13:53 +02:00
return new moodle_url ( $CFG -> httpswwwroot . '/lib/javascript.php' , array ( 'rev' => $CFG -> jsrev , 'jsfile' => $url ));
2012-05-05 18:16:50 +02:00
} else {
$returnurl = new moodle_url ( $CFG -> httpswwwroot . '/lib/javascript.php' );
$returnurl -> set_slashargument ( '/' . $CFG -> jsrev . $url );
return $returnurl ;
}
2010-02-07 12:50:53 +00:00
} else {
return new moodle_url ( $CFG -> httpswwwroot . $url );
}
2010-02-07 08:45:21 +00:00
} else {
throw new coding_exception ( 'Invalid JS url, it has to be shortened url starting with / or moodle_url instance.' , $url );
}
}
/**
2010-05-22 19:34:17 +00:00
* Find out if JS module present and return details .
2012-01-05 13:28:24 +13:00
*
2010-04-01 20:54:27 +00:00
* @ param string $component name of component in frankenstyle , ex : core_group , mod_forum
2010-02-07 08:45:21 +00:00
* @ return array description of module or null if not found
*/
2010-04-01 20:54:27 +00:00
protected function find_module ( $component ) {
2012-03-30 10:11:43 +08:00
global $CFG , $PAGE ;
2010-02-07 08:45:21 +00:00
$module = null ;
2010-04-01 20:54:27 +00:00
if ( strpos ( $component , 'core_' ) === 0 ) {
2012-08-12 11:41:53 +02:00
// Must be some core stuff - list here is not complete, this is just the stuff used from multiple places
// so that we do nto have to repeat the definition of these modules over and over again.
2010-04-01 20:54:27 +00:00
switch ( $component ) {
2010-02-07 08:45:21 +00:00
case 'core_filepicker' :
$module = array ( 'name' => 'core_filepicker' ,
'fullpath' => '/repository/filepicker.js' ,
2012-05-04 16:46:41 +08:00
'requires' => array ( 'base' , 'node' , 'node-event-simulate' , 'json' , 'async-queue' , 'io-base' , 'io-upload-iframe' , 'io-form' , 'yui2-treeview' , 'panel' , 'cookie' , 'datatable' , 'datatable-sort' , 'resize-plugin' , 'dd-plugin' , 'moodle-core_filepicker' ),
'strings' => array ( array ( 'lastmodified' , 'moodle' ), array ( 'name' , 'moodle' ), array ( 'type' , 'repository' ), array ( 'size' , 'repository' ),
array ( 'invalidjson' , 'repository' ), array ( 'error' , 'moodle' ), array ( 'info' , 'moodle' ),
array ( 'nofilesattached' , 'repository' ), array ( 'filepicker' , 'repository' ), array ( 'logout' , 'repository' ),
array ( 'nofilesavailable' , 'repository' ), array ( 'norepositoriesavailable' , 'repository' ),
2011-05-02 10:11:19 +08:00
array ( 'fileexistsdialogheader' , 'repository' ), array ( 'fileexistsdialog_editor' , 'repository' ),
2012-05-15 10:51:00 +08:00
array ( 'fileexistsdialog_filemanager' , 'repository' ), array ( 'renameto' , 'repository' ),
array ( 'referencesexist' , 'repository' )
2010-05-21 08:13:08 +00:00
));
2010-02-07 08:45:21 +00:00
break ;
case 'core_comment' :
$module = array ( 'name' => 'core_comment' ,
'fullpath' => '/comment/comment.js' ,
2011-08-25 11:38:34 +02:00
'requires' => array ( 'base' , 'io-base' , 'node' , 'json' , 'yui2-animation' , 'overlay' ),
2010-04-08 01:53:48 +00:00
'strings' => array ( array ( 'confirmdeletecomments' , 'admin' ), array ( 'yes' , 'moodle' ), array ( 'no' , 'moodle' ))
2010-03-31 09:53:05 +00:00
);
2010-02-07 08:45:21 +00:00
break ;
case 'core_role' :
$module = array ( 'name' => 'core_role' ,
2010-09-21 06:09:14 +00:00
'fullpath' => '/admin/roles/module.js' ,
'requires' => array ( 'node' , 'cookie' ));
2010-02-07 08:45:21 +00:00
break ;
case 'core_completion' :
$module = array ( 'name' => 'core_completion' ,
'fullpath' => '/course/completion.js' );
break ;
case 'core_dock' :
$module = array ( 'name' => 'core_dock' ,
'fullpath' => '/blocks/dock.js' ,
2010-05-31 03:33:34 +00:00
'requires' => array ( 'base' , 'node' , 'event-custom' , 'event-mouseenter' , 'event-resize' ),
2010-05-24 04:03:31 +00:00
'strings' => array ( array ( 'addtodock' , 'block' ), array ( 'undockitem' , 'block' ), array ( 'undockall' , 'block' ), array ( 'thisdirectionvertical' , 'langconfig' )));
2010-02-07 08:45:21 +00:00
break ;
case 'core_message' :
$module = array ( 'name' => 'core_message' ,
2011-05-13 11:13:13 +01:00
'requires' => array ( 'base' , 'node' , 'event' , 'node-event-simulate' ),
2010-02-07 08:45:21 +00:00
'fullpath' => '/message/module.js' );
break ;
2010-02-24 03:57:36 +00:00
case 'core_group' :
$module = array ( 'name' => 'core_group' ,
'fullpath' => '/group/module.js' ,
'requires' => array ( 'node' , 'overlay' , 'event-mouseenter' ));
break ;
2010-11-01 17:44:59 +00:00
case 'core_question_engine' :
$module = array ( 'name' => 'core_question_engine' ,
'fullpath' => '/question/qengine.js' ,
'requires' => array ( 'node' , 'event' ));
break ;
2010-04-01 20:33:15 +00:00
case 'core_rating' :
$module = array ( 'name' => 'core_rating' ,
2010-03-16 05:57:51 +00:00
'fullpath' => '/rating/module.js' ,
2011-08-25 11:38:34 +02:00
'requires' => array ( 'node' , 'event' , 'overlay' , 'io-base' , 'json' ));
2010-03-16 05:57:51 +00:00
break ;
2011-11-08 20:05:19 +00:00
case 'core_dndupload' :
$module = array ( 'name' => 'core_dndupload' ,
'fullpath' => '/lib/form/dndupload.js' ,
2012-06-16 15:42:07 +01:00
'requires' => array ( 'node' , 'event' , 'json' , 'core_filepicker' ),
'strings' => array ( array ( 'uploadformlimit' , 'moodle' ), array ( 'droptoupload' , 'moodle' ), array ( 'maxfilesreached' , 'moodle' ), array ( 'dndenabled_inbox' , 'moodle' ), array ( 'fileexists' , 'moodle' )));
2011-11-08 20:05:19 +00:00
break ;
2010-02-07 08:45:21 +00:00
}
} else {
2010-04-10 23:21:18 +00:00
if ( $dir = get_component_directory ( $component )) {
if ( file_exists ( " $dir /module.js " )) {
if ( strpos ( $dir , $CFG -> dirroot . '/' ) === 0 ) {
$dir = substr ( $dir , strlen ( $CFG -> dirroot ));
$module = array ( 'name' => $component , 'fullpath' => " $dir /module.js " , 'requires' => array ());
}
2010-02-07 08:45:21 +00:00
}
}
}
return $module ;
}
/**
* Append YUI3 module to default YUI3 JS loader .
2012-01-05 13:28:24 +13:00
* The structure of module array is described at { @ link http :// developer . yahoo . com / yui / 3 / yui / }
*
2010-02-07 08:45:21 +00:00
* @ param string | array $module name of module ( details are autodetected ), or full module specification as array
* @ return void
*/
public function js_module ( $module ) {
global $CFG ;
if ( empty ( $module )) {
throw new coding_exception ( 'Missing YUI3 module name or full description.' );
}
if ( is_string ( $module )) {
$module = $this -> find_module ( $module );
}
if ( empty ( $module ) or empty ( $module [ 'name' ]) or empty ( $module [ 'fullpath' ])) {
throw new coding_exception ( 'Missing YUI3 module details.' );
}
2010-11-12 05:26:47 +00:00
// Don't load this module if we already have, no need to!
if ( $this -> js_module_loaded ( $module [ 'name' ])) {
if ( debugging ( '' , DEBUG_DEVELOPER )) {
$this -> debug_moduleloadstacktraces [ $module [ 'name' ]][] = format_backtrace ( debug_backtrace ());
}
return ;
}
2010-02-07 08:45:21 +00:00
$module [ 'fullpath' ] = $this -> js_fix_url ( $module [ 'fullpath' ]) -> out ( false );
2012-08-12 11:41:53 +02:00
// Add all needed strings.
2010-02-07 09:34:19 +00:00
if ( ! empty ( $module [ 'strings' ])) {
foreach ( $module [ 'strings' ] as $string ) {
$identifier = $string [ 0 ];
$component = isset ( $string [ 1 ]) ? $string [ 1 ] : 'moodle' ;
$a = isset ( $string [ 2 ]) ? $string [ 2 ] : null ;
$this -> string_for_js ( $identifier , $component , $a );
}
}
unset ( $module [ 'strings' ]);
2010-11-12 05:26:47 +00:00
// Process module requirements and attempt to load each. This allows
// moodle modules to require each other.
if ( ! empty ( $module [ 'requires' ])){
foreach ( $module [ 'requires' ] as $requirement ) {
$rmodule = $this -> find_module ( $requirement );
if ( is_array ( $rmodule )) {
$this -> js_module ( $rmodule );
}
}
}
2010-12-02 05:46:09 +00:00
2010-02-07 08:45:21 +00:00
if ( $this -> headdone ) {
$this -> extramodules [ $module [ 'name' ]] = $module ;
} else {
2012-08-06 10:21:37 +02:00
$this -> YUI_config -> modules [ $module [ 'name' ]] = $module ;
2010-02-07 08:45:21 +00:00
}
2010-11-12 05:26:47 +00:00
if ( debugging ( '' , DEBUG_DEVELOPER )) {
if ( ! array_key_exists ( $module [ 'name' ], $this -> debug_moduleloadstacktraces )) {
$this -> debug_moduleloadstacktraces [ $module [ 'name' ]] = array ();
}
$this -> debug_moduleloadstacktraces [ $module [ 'name' ]][] = format_backtrace ( debug_backtrace ());
}
}
/**
* Returns true if the module has already been loaded .
*
2011-12-10 13:16:18 +01:00
* @ param string | array $module
2010-11-12 05:26:47 +00:00
* @ return bool True if the module has already been loaded
*/
protected function js_module_loaded ( $module ) {
if ( is_string ( $module )) {
$modulename = $module ;
} else {
$modulename = $module [ 'name' ];
}
2012-08-06 10:21:37 +02:00
return array_key_exists ( $modulename , $this -> YUI_config -> modules ) ||
2010-11-12 05:26:47 +00:00
array_key_exists ( $modulename , $this -> extramodules );
}
/**
* Returns the stacktraces from loading js modules .
* @ return array
*/
public function get_loaded_modules () {
return $this -> debug_moduleloadstacktraces ;
2010-02-07 08:45:21 +00:00
}
/**
* Ensure that the specified CSS file is linked to from this page .
*
* Because stylesheet links must go in the < head > part of the HTML , you must call
* this function before { @ link get_head_code ()} is called . That normally means before
* the call to print_header . If you call it when it is too late , an exception
* will be thrown .
*
* Even if a particular style sheet is requested more than once , it will only
* be linked to once .
*
2010-05-31 12:22:21 +00:00
* Please note use of this feature is strongly discouraged ,
2010-02-07 08:45:21 +00:00
* it is suitable only for places where CSS is submitted directly by teachers .
* ( Students must not be allowed to submit any external CSS because it may
* contain embedded javascript ! ) . Example of correct use is mod / data .
*
* @ param string $stylesheet The path to the . css file , relative to $CFG -> wwwroot .
* For example :
* $PAGE -> requires -> css ( 'mod/data/css.php?d=' . $data -> id );
*/
public function css ( $stylesheet ) {
global $CFG ;
if ( $this -> headdone ) {
throw new coding_exception ( 'Cannot require a CSS file after <head> has been printed.' , $stylesheet );
}
if ( $stylesheet instanceof moodle_url ) {
// ok
} else if ( strpos ( $stylesheet , '/' ) === 0 ) {
$stylesheet = new moodle_url ( $CFG -> httpswwwroot . $stylesheet );
} else {
throw new coding_exception ( 'Invalid stylesheet parameter.' , $stylesheet );
}
2012-08-12 11:41:53 +02:00
$this -> cssurls [ $stylesheet -> out ()] = $stylesheet ;
2010-02-07 08:45:21 +00:00
}
/**
2012-08-12 11:41:53 +02:00
* Add theme stylesheet to page - do not use from plugin code ,
2010-02-07 08:45:21 +00:00
* this should be called only from the core renderer !
2012-01-05 13:28:24 +13:00
*
2010-02-07 08:45:21 +00:00
* @ param moodle_url $stylesheet
* @ return void
*/
public function css_theme ( moodle_url $stylesheet ) {
$this -> cssthemeurls [] = $stylesheet ;
}
/**
* Ensure that a skip link to a given target is printed at the top of the < body >.
*
* You must call this function before { @ link get_top_of_body_code ()}, ( if not , an exception
* will be thrown ) . That normally means you must call this before the call to print_header .
*
* If you ask for a particular skip link to be printed , it is then your responsibility
2010-05-22 19:34:17 +00:00
* to ensure that the appropriate < a name = " ... " > tag is printed in the body of the
2010-02-07 08:45:21 +00:00
* page , so that the skip link goes somewhere .
*
* Even if a particular skip link is requested more than once , only one copy of it will be output .
*
2012-08-12 11:41:53 +02:00
* @ param string $target the name of anchor this link should go to . For example 'maincontent' .
* @ param string $linktext The text to use for the skip link . Normally get_string ( 'skipto' , 'access' , ... );
2010-02-07 08:45:21 +00:00
*/
public function skip_link_to ( $target , $linktext ) {
if ( $this -> topofbodydone ) {
debugging ( 'Page header already printed, can not add skip links any more, code needs to be fixed.' );
return ;
}
$this -> skiplinks [ $target ] = $linktext ;
}
/**
* !!! DEPRECATED !!! please use js_init_call () if possible
* Ensure that the specified JavaScript function is called from an inline script
* somewhere on this page .
*
* By default the call will be put in a script tag at the
* end of the page after initialising Y instance , since this gives best page - load
* performance and allows you to use YUI3 library .
*
* If you request that a particular function is called several times , then
* that is what will happen ( unlike linking to a CSS or JS file , where only
* one link will be output ) .
*
2010-05-22 19:34:17 +00:00
* The main benefit of the method is the automatic encoding of all function parameters .
2010-02-07 08:45:21 +00:00
*
2012-08-12 11:41:53 +02:00
* @ deprecated
*
2010-02-07 08:45:21 +00:00
* @ param string $function the name of the JavaScritp function to call . Can
* be a compound name like 'Y.Event.purgeElement' . Can also be
* used to create and object by using a 'function name' like 'new user_selector' .
* @ param array $arguments and array of arguments to be passed to the function .
* When generating the function call , this will be escaped using json_encode ,
* so passing objects and arrays should work .
2012-01-05 13:28:24 +13:00
* @ param bool $ondomready If tru the function is only called when the dom is
* ready for manipulation .
* @ param int $delay The delay before the function is called .
2010-02-07 08:45:21 +00:00
*/
public function js_function_call ( $function , array $arguments = null , $ondomready = false , $delay = 0 ) {
$where = $ondomready ? 'ondomready' : 'normal' ;
$this -> jscalls [ $where ][] = array ( $function , $arguments , $delay );
}
2010-06-04 01:49:53 +00:00
/**
2010-07-01 02:26:21 +00:00
* Adds a call to make use of a YUI gallery module . DEPRECATED DO NOT USE !!!
2010-06-04 01:49:53 +00:00
*
2010-07-01 02:26:21 +00:00
* @ deprecated DO NOT USE
2010-06-04 01:49:53 +00:00
*
* @ param string | array $modules One or more gallery modules to require
* @ param string $version
* @ param string $function
* @ param array $arguments
* @ param bool $ondomready
*/
public function js_gallery_module ( $modules , $version , $function , array $arguments = null , $ondomready = false ) {
global $CFG ;
2010-07-01 02:26:21 +00:00
debugging ( 'This function will be removed before 2.0 is released please change it from js_gallery_module to yui_module' , DEBUG_DEVELOPER );
$this -> yui_module ( $modules , $function , $arguments , $version , $ondomready );
}
/**
2012-08-12 11:41:53 +02:00
* Creates a JavaScript function call that requires one or more modules to be loaded .
2010-07-01 02:26:21 +00:00
*
* This function can be used to include all of the standard YUI module types within JavaScript :
* - YUI3 modules [ node , event , io ]
* - YUI2 modules [ yui2 -* ]
* - Moodle modules [ moodle -* ]
* - Gallery modules [ gallery -* ]
*
* @ param array | string $modules One or more modules
* @ param string $function The function to call once modules have been loaded
* @ param array $arguments An array of arguments to pass to the function
* @ param string $galleryversion The gallery version to use
* @ param bool $ondomready
*/
public function yui_module ( $modules , $function , array $arguments = null , $galleryversion = '2010.04.08-12-35' , $ondomready = false ) {
2010-09-17 08:04:01 +00:00
global $CFG ;
2010-06-04 01:49:53 +00:00
if ( ! is_array ( $modules )) {
$modules = array ( $modules );
}
2012-06-19 11:26:23 +12:00
if ( empty ( $CFG -> useexternalyui )) {
2010-06-04 01:49:53 +00:00
// We need to set the M.yui.galleryversion to the correct version
2010-07-01 02:26:21 +00:00
$jscode = 'M.yui.galleryversion=' . json_encode ( $galleryversion ) . ';' ;
2010-06-04 01:49:53 +00:00
} else {
// Set Y's config.gallery to the version
2010-07-01 02:26:21 +00:00
$jscode = 'Y.config.gallery=' . json_encode ( $galleryversion ) . ';' ;
2010-06-04 01:49:53 +00:00
}
2012-03-28 11:38:53 +08:00
$jscode .= 'Y.use(' . join ( ',' , array_map ( 'json_encode' , convert_to_array ( $modules ))) . ',function() {' . js_writer :: function_call ( $function , $arguments ) . '});' ;
2010-06-04 01:49:53 +00:00
if ( $ondomready ) {
$jscode = " Y.on('domready', function() { $jscode }); " ;
}
$this -> jsinitcode [] = $jscode ;
}
2010-02-07 08:45:21 +00:00
/**
* Ensure that the specified JavaScript function is called from an inline script
* from page footer .
*
* @ param string $function the name of the JavaScritp function to with init code ,
* usually something like 'M.mod_mymodule.init'
* @ param array $extraarguments and array of arguments to be passed to the function .
* The first argument is always the YUI3 Y instance with all required dependencies
* already loaded .
* @ param bool $ondomready wait for dom ready ( helps with some IE problems when modifying DOM )
* @ param array $module JS module specification array
*/
public function js_init_call ( $function , array $extraarguments = null , $ondomready = false , array $module = null ) {
$jscode = js_writer :: function_call_with_Y ( $function , $extraarguments );
if ( ! $module ) {
2012-08-12 11:41:53 +02:00
// Detect module automatically.
2010-02-07 08:45:21 +00:00
if ( preg_match ( '/M\.([a-z0-9]+_[^\.]+)/' , $function , $matches )) {
$module = $this -> find_module ( $matches [ 1 ]);
}
}
$this -> js_init_code ( $jscode , $ondomready , $module );
}
/**
* Add short static javascript code fragment to page footer .
* This is intended primarily for loading of js modules and initialising page layout .
* Ideally the JS code fragment should be stored in plugin renderer so that themes
* may override it .
2012-08-12 11:41:53 +02:00
*
2010-02-07 08:45:21 +00:00
* @ param string $jscode
* @ param bool $ondomready wait for dom ready ( helps with some IE problems when modifying DOM )
* @ param array $module JS module specification array
*/
public function js_init_code ( $jscode , $ondomready = false , array $module = null ) {
$jscode = trim ( $jscode , " ; \n " ) . ';' ;
if ( $module ) {
$this -> js_module ( $module );
$modulename = $module [ 'name' ];
$jscode = " Y.use(' $modulename ', function(Y) { $jscode }); " ;
}
if ( $ondomready ) {
$jscode = " Y.on('domready', function() { $jscode }); " ;
}
$this -> jsinitcode [] = $jscode ;
}
/**
* Make a language string available to JavaScript .
*
2010-02-07 09:43:07 +00:00
* All the strings will be available in a M . str object in the global namespace .
2010-02-07 08:45:21 +00:00
* So , for example , after a call to $PAGE -> requires -> string_for_js ( 'course' , 'moodle' );
2010-02-07 09:43:07 +00:00
* then the JavaScript variable M . str . moodle . course will be 'Course' , or the
2010-02-07 08:45:21 +00:00
* equivalent in the current language .
*
* The arguments to this function are just like the arguments to get_string
2010-11-02 14:59:32 +00:00
* except that $component is not optional , and there are some aspects to consider
* when the string contains { $a } placeholder .
*
* If the string does not contain any { $a } placeholder , you can simply use
* M . str . component . identifier to obtain it . If you prefer , you can call
* M . util . get_string ( identifier , component ) to get the same result .
*
* If you need to use { $a } placeholders , there are two options . Either the
* placeholder should be substituted in PHP on server side or it should
* be substituted in Javascript at client side .
*
* To substitute the placeholder at server side , just provide the required
* value for the placeholder when you require the string . Because each string
* is only stored once in the JavaScript ( based on $identifier and $module )
* you cannot get the same string with two different values of $a . If you try ,
* an exception will be thrown . Once the placeholder is substituted , you can
* use M . str or M . util . get_string () as shown above :
*
2012-08-12 11:41:53 +02:00
* // Require the string in PHP and replace the placeholder.
2010-11-02 14:59:32 +00:00
* $PAGE -> requires -> string_for_js ( 'fullnamedisplay' , 'moodle' , $USER );
2012-08-12 11:41:53 +02:00
* // Use the result of the substitution in Javascript.
2010-11-02 14:59:32 +00:00
* alert ( M . str . moodle . fullnamedisplay );
*
* To substitute the placeholder at client side , use M . util . get_string ()
2012-02-14 18:24:09 +13:00
* function . It implements the same logic as { @ link get_string ()} :
2010-11-02 14:59:32 +00:00
*
2012-08-12 11:41:53 +02:00
* // Require the string in PHP but keep {$a} as it is.
2010-11-02 14:59:32 +00:00
* $PAGE -> requires -> string_for_js ( 'fullnamedisplay' , 'moodle' );
2012-08-12 11:41:53 +02:00
* // Provide the values on the fly in Javascript.
2010-11-02 14:59:32 +00:00
* user = { firstname : 'Harry' , lastname : 'Potter' }
* alert ( M . util . get_string ( 'fullnamedisplay' , 'moodle' , user );
*
* If you do need the same string expanded with different $a values in PHP
* on server side , then the solution is to put them in your own data structure
* ( e . g . and array ) that you pass to JavaScript with { @ link data_for_js ()} .
2010-02-07 08:45:21 +00:00
*
* @ param string $identifier the desired string .
2011-12-10 13:16:18 +01:00
* @ param string $component the language file to look in .
2010-02-07 08:45:21 +00:00
* @ param mixed $a any extra data to add into the string ( optional ) .
*/
2012-08-12 11:41:53 +02:00
public function string_for_js ( $identifier , $component , $a = null ) {
2010-02-07 08:45:21 +00:00
if ( ! $component ) {
2012-06-21 09:58:45 +02:00
throw new coding_exception ( 'The $component parameter is required for page_requirements_manager::string_for_js().' );
2010-02-07 08:45:21 +00:00
}
2012-06-21 09:58:45 +02:00
if ( isset ( $this -> stringsforjs_as [ $component ][ $identifier ]) and $this -> stringsforjs_as [ $component ][ $identifier ] !== $a ) {
2010-02-07 08:45:21 +00:00
throw new coding_exception ( " Attempt to re-define already required string ' $identifier ' " .
2012-06-21 09:58:45 +02:00
" from lang file ' $component ' with different \$ a parameter? " );
}
if ( ! isset ( $this -> stringsforjs [ $component ][ $identifier ])) {
$this -> stringsforjs [ $component ][ $identifier ] = new lang_string ( $identifier , $component , $a );
$this -> stringsforjs_as [ $component ][ $identifier ] = $a ;
2010-02-07 08:45:21 +00:00
}
}
/**
2012-08-12 11:41:53 +02:00
* Make an array of language strings available for JS .
2010-02-07 08:45:21 +00:00
*
* This function calls the above function { @ link string_for_js ()} for each requested
* string in the $identifiers array that is passed to the argument for a single module
* passed in $module .
*
* < code >
2011-03-10 00:50:18 +01:00
* $PAGE -> requires -> strings_for_js ( array ( 'one' , 'two' , 'three' ), 'mymod' , array ( 'a' , null , 3 ));
2010-02-07 08:45:21 +00:00
*
2012-08-12 11:41:53 +02:00
* // The above is identical to calling:
2010-02-07 08:45:21 +00:00
*
2011-03-10 00:50:18 +01:00
* $PAGE -> requires -> string_for_js ( 'one' , 'mymod' , 'a' );
* $PAGE -> requires -> string_for_js ( 'two' , 'mymod' );
* $PAGE -> requires -> string_for_js ( 'three' , 'mymod' , 3 );
2010-02-07 08:45:21 +00:00
* </ code >
*
* @ param array $identifiers An array of desired strings
* @ param string $component The module to load for
* @ param mixed $a This can either be a single variable that gets passed as extra
* information for every string or it can be an array of mixed data where the
* key for the data matches that of the identifier it is meant for .
*
*/
2012-08-12 11:41:53 +02:00
public function strings_for_js ( $identifiers , $component , $a = null ) {
2010-02-07 08:45:21 +00:00
foreach ( $identifiers as $key => $identifier ) {
if ( is_array ( $a ) && array_key_exists ( $key , $a )) {
$extra = $a [ $key ];
} else {
$extra = $a ;
}
$this -> string_for_js ( $identifier , $component , $extra );
}
}
/**
* !!!!!! DEPRECATED !!!!!! please use js_init_call () for everything now .
*
* Make some data from PHP available to JavaScript code .
*
* For example , if you call
* < pre >
* $PAGE -> requires -> data_for_js ( 'mydata' , array ( 'name' => 'Moodle' ));
* </ pre >
* then in JavsScript mydata . name will be 'Moodle' .
2012-08-12 11:41:53 +02:00
*
* @ deprecated
2010-02-07 08:45:21 +00:00
* @ param string $variable the the name of the JavaScript variable to assign the data to .
* Will probably work if you use a compound name like 'mybuttons.button[1]' , but this
* should be considered an experimental feature .
* @ param mixed $data The data to pass to JavaScript . This will be escaped using json_encode ,
* so passing objects and arrays should work .
* @ param bool $inhead initialise in head
* @ return void
*/
public function data_for_js ( $variable , $data , $inhead = false ) {
$where = $inhead ? 'head' : 'footer' ;
$this -> jsinitvariables [ $where ][] = array ( $variable , $data );
}
/**
* Creates a YUI event handler .
*
2012-08-12 11:41:53 +02:00
* @ param mixed $selector standard YUI selector for elements , may be array or string , element id is in the form " #idvalue "
2010-02-07 08:45:21 +00:00
* @ param string $event A valid DOM event ( click , mousedown , change etc . )
* @ param string $function The name of the function to call
* @ param array $arguments An optional array of argument parameters to pass to the function
2010-05-21 08:13:08 +00:00
*/
public function event_handler ( $selector , $event , $function , array $arguments = null ) {
2010-02-07 08:45:21 +00:00
$this -> eventhandlers [] = array ( 'selector' => $selector , 'event' => $event , 'function' => $function , 'arguments' => $arguments );
}
/**
* Returns code needed for registering of event handlers .
* @ return string JS code
*/
protected function get_event_handler_code () {
$output = '' ;
foreach ( $this -> eventhandlers as $h ) {
$output .= js_writer :: event_handler ( $h [ 'selector' ], $h [ 'event' ], $h [ 'function' ], $h [ 'arguments' ]);
}
return $output ;
}
/**
* Get the inline JavaScript code that need to appear in a particular place .
2011-12-10 13:16:18 +01:00
* @ param bool $ondomready
* @ return string
2010-02-07 08:45:21 +00:00
*/
protected function get_javascript_code ( $ondomready ) {
$where = $ondomready ? 'ondomready' : 'normal' ;
$output = '' ;
if ( $this -> jscalls [ $where ]) {
foreach ( $this -> jscalls [ $where ] as $data ) {
2010-02-14 08:56:57 +00:00
$output .= js_writer :: function_call ( $data [ 0 ], $data [ 1 ], $data [ 2 ]);
2010-02-07 08:45:21 +00:00
}
if ( ! empty ( $ondomready )) {
$output = " Y.on('domready', function() { \n $output\n }); " ;
}
}
return $output ;
}
/**
* Returns js code to be executed when Y is available .
2012-01-05 13:28:24 +13:00
* @ return string
2010-02-07 08:45:21 +00:00
*/
protected function get_javascript_init_code () {
if ( count ( $this -> jsinitcode )) {
return implode ( " \n " , $this -> jsinitcode ) . " \n " ;
}
return '' ;
}
/**
* Returns basic YUI3 JS loading code .
* YUI3 is using autoloading of both CSS and JS code .
*
* Major benefit of this compared to standard js / csss loader is much improved
* caching , better browser cache utilisation , much fewer http requests .
*
* @ return string
*/
protected function get_yui3lib_headcode () {
2011-08-19 10:21:30 +02:00
global $CFG ;
$code = '' ;
if ( $this -> yui3loader -> combine ) {
$code .= '<link rel="stylesheet" type="text/css" href="' . $this -> yui3loader -> comboBase
. $CFG -> yui3version . '/build/cssreset/reset-min.css&'
. $CFG -> yui3version . '/build/cssfonts/fonts-min.css&'
. $CFG -> yui3version . '/build/cssgrids/grids-min.css&'
. $CFG -> yui3version . '/build/cssbase/base-min.css" />' ;
2012-04-29 19:00:51 +02:00
$code .= '<script type="text/javascript" src="' . $this -> yui3loader -> comboBase . $CFG -> yui3version . '/build/yui/yui-min.js"></script>' ;
2011-08-19 10:21:30 +02:00
} else {
$code .= '<link rel="stylesheet" type="text/css" href="' . $this -> yui3loader -> base . 'cssreset/reset-min.css" />' ;
$code .= '<link rel="stylesheet" type="text/css" href="' . $this -> yui3loader -> base . 'cssfonts/fonts-min.css" />' ;
$code .= '<link rel="stylesheet" type="text/css" href="' . $this -> yui3loader -> base . 'cssgrids/grids-min.css" />' ;
$code .= '<link rel="stylesheet" type="text/css" href="' . $this -> yui3loader -> base . 'cssbase/base-min.css" />' ;
2012-04-29 19:00:51 +02:00
$code .= '<script type="text/javascript" src="' . $this -> yui3loader -> base . 'yui/yui-min.js"></script>' ;
2011-08-19 10:21:30 +02:00
}
2011-12-10 13:46:15 +01:00
2012-08-05 21:30:18 +02:00
if ( $this -> yui3loader -> filter === 'RAW' ) {
2011-12-10 13:46:15 +01:00
$code = str_replace ( '-min.css' , '.css' , $code );
$code = str_replace ( '-min.js' , '.js' , $code );
2012-08-05 21:30:18 +02:00
} else if ( $this -> yui3loader -> filter === 'DEBUG' ) {
2011-12-10 13:46:15 +01:00
$code = str_replace ( '-min.css' , '.css' , $code );
$code = str_replace ( '-min.js' , '-debug.js' , $code );
2011-08-19 10:21:30 +02:00
}
2010-02-07 08:45:21 +00:00
return $code ;
}
/**
2012-08-12 11:41:53 +02:00
* Returns html tags needed for inclusion of theme CSS .
2012-01-05 13:28:24 +13:00
*
2010-02-07 08:45:21 +00:00
* @ return string
*/
protected function get_css_code () {
// First of all the theme CSS, then any custom CSS
// Please note custom CSS is strongly discouraged,
// because it can not be overridden by themes!
// It is suitable only for things like mod/data which accepts CSS from teachers.
2010-07-08 02:12:49 +00:00
$attributes = array ( 'rel' => 'stylesheet' , 'type' => 'text/css' );
2010-02-07 08:45:21 +00:00
2010-07-08 02:12:49 +00:00
// This line of code may look funny but it is currently required in order
// to avoid MASSIVE display issues in Internet Explorer.
// As of IE8 + YUI3.1.1 the reference stylesheet (firstthemesheet) gets
// ignored whenever another resource is added until such time as a redraw
// is forced, usually by moving the mouse over the affected element.
2010-07-20 05:19:22 +00:00
$code = html_writer :: tag ( 'script' , '/** Required in order to fix style inclusion problems in IE with YUI **/' , array ( 'id' => 'firstthemesheet' , 'type' => 'text/css' ));
2010-02-07 08:45:21 +00:00
$urls = $this -> cssthemeurls + $this -> cssurls ;
foreach ( $urls as $url ) {
$attributes [ 'href' ] = $url ;
$code .= html_writer :: empty_tag ( 'link' , $attributes ) . " \n " ;
2012-08-12 11:41:53 +02:00
// This id is needed in first sheet only so that theme may override YUI sheets loaded on the fly.
2010-02-07 08:45:21 +00:00
unset ( $attributes [ 'id' ]);
}
return $code ;
}
/**
2012-08-12 11:41:53 +02:00
* Adds extra modules specified after printing of page header .
2012-01-05 13:28:24 +13:00
*
2011-12-10 13:16:18 +01:00
* @ return string
2010-02-07 08:45:21 +00:00
*/
protected function get_extra_modules_code () {
if ( empty ( $this -> extramodules )) {
return '' ;
}
return html_writer :: script ( js_writer :: function_call ( 'M.yui.add_module' , array ( $this -> extramodules )));
}
/**
* Generate any HTML that needs to go inside the < head > tag .
*
* Normally , this method is called automatically by the code that prints the
* < head > tag . You should not normally need to call it in your own code .
*
2011-12-10 13:16:18 +01:00
* @ param moodle_page $page
* @ param core_renderer $renderer
2010-02-07 08:45:21 +00:00
* @ return string the HTML code to to inside the < head > tag .
*/
public function get_head_code ( moodle_page $page , core_renderer $renderer ) {
global $CFG ;
2012-08-12 11:41:53 +02:00
// Note: the $page and $output are not stored here because it would
// create circular references in memory which prevents garbage collection.
2010-02-07 08:45:21 +00:00
$this -> init_requirements_data ( $page , $renderer );
2012-08-12 11:41:53 +02:00
// YUI3 JS and CSS is always loaded first - it is cached in browser.
2010-02-07 08:45:21 +00:00
$output = $this -> get_yui3lib_headcode ();
2012-08-12 11:41:53 +02:00
// Now theme CSS + custom CSS in this specific order.
2010-02-07 08:45:21 +00:00
$output .= $this -> get_css_code ();
2012-08-12 11:41:53 +02:00
// Set up global YUI3 loader object - this should contain all code needed by plugins.
// Note: in JavaScript just use "YUI().use('overlay', function(Y) { .... });",
// this needs to be done before including any other script.
2012-08-05 21:30:18 +02:00
$js = " var M = { }; M.yui = { };
var moodleConfigFn = function ( me ) { var p = me . path , b = me . name . replace ( /^ moodle -/ , '' ) . split ( '-' , 3 ), n = b . pop (); if ( / ( skin | core ) /. test ( n )) { n = b . pop (); me . type = 'css' ;}; me . path = b . join ( '-' ) + '/' + n + '/' + n + '.' + me . type ;};
var galleryConfigFn = function ( me ) { var p = me . path , v = M . yui . galleryversion , f ; if ( /- ( skin | core ) /. test ( me . name )) { me . type = 'css' ; p = p . replace ( /- ( skin | core ) / , '' ) . replace ( / \ . js / , '.css' ) . split ( '/' ), f = p . pop () . replace ( / ( \ - ( min | debug )) / , '' ); if ( /- skin /. test ( me . name )) { p . splice ( p . length , 0 , v , 'assets' , 'skins' , 'sam' , f );} else { p . splice ( p . length , 0 , v , 'assets' , f );};} else { p = p . split ( '/' ), f = p . pop (); p . splice ( p . length , 0 , v , f );}; me . path = p . join ( '/' );};
var yui2in3ConfigFn = function ( me ) { if ( /- skin | reset | fonts | grids | base /. test ( me . name )){ me . type = 'css' ; me . path = me . path . replace ( / \ . js / , '.css' ); me . path = me . path . replace ( / \ / yui2 - skin / , '/assets/skins/sam/yui2-skin' );}}; \n " ;
2012-08-06 10:21:37 +02:00
$js .= js_writer :: set_variable ( 'YUI_config' , $this -> YUI_config , false ) . " \n " ;
2012-08-12 11:41:53 +02:00
$js .= " M.yui.loader = { modules: { }}; \n " ; // Backwards compatibility only, not used any more.
2010-02-07 08:45:21 +00:00
$js .= js_writer :: set_variable ( 'M.cfg' , $this -> M_cfg , false );
2010-07-01 02:26:21 +00:00
$js = str_replace ( '"@GALLERYCONFIGFN@"' , 'galleryConfigFn' , $js );
$js = str_replace ( '"@MOODLECONFIGFN@"' , 'moodleConfigFn' , $js );
2012-08-05 21:30:18 +02:00
$js = str_replace ( '"@2IN3CONFIGFN@"' , 'yui2in3ConfigFn' , $js );
2010-07-01 02:26:21 +00:00
2010-02-07 08:45:21 +00:00
$output .= html_writer :: script ( $js );
2012-08-12 11:41:53 +02:00
// Link our main JS file, all core stuff should be there.
2010-02-07 12:50:53 +00:00
$output .= html_writer :: script ( '' , $this -> js_fix_url ( '/lib/javascript-static.js' ));
2010-02-07 08:45:21 +00:00
2012-08-12 11:41:53 +02:00
// Add variables.
2010-02-07 08:45:21 +00:00
if ( $this -> jsinitvariables [ 'head' ]) {
$js = '' ;
foreach ( $this -> jsinitvariables [ 'head' ] as $data ) {
list ( $var , $value ) = $data ;
$js .= js_writer :: set_variable ( $var , $value , true );
}
$output .= html_writer :: script ( $js );
}
2012-08-12 11:41:53 +02:00
// All the other linked things from HEAD - there should be as few as possible.
2010-02-07 08:45:21 +00:00
if ( $this -> jsincludes [ 'head' ]) {
foreach ( $this -> jsincludes [ 'head' ] as $url ) {
$output .= html_writer :: script ( '' , $url );
}
}
2012-08-12 11:41:53 +02:00
// Mark head sending done, it is not possible to anything there.
2010-02-07 08:45:21 +00:00
$this -> headdone = true ;
return $output ;
}
/**
* Generate any HTML that needs to go at the start of the < body > tag .
*
* Normally , this method is called automatically by the code that prints the
* < head > tag . You should not normally need to call it in your own code .
*
* @ return string the HTML code to go at the start of the < body > tag .
*/
public function get_top_of_body_code () {
2012-08-12 11:41:53 +02:00
// First the skip links.
2010-02-07 08:45:21 +00:00
$links = '' ;
$attributes = array ( 'class' => 'skip' );
foreach ( $this -> skiplinks as $url => $text ) {
$attributes [ 'href' ] = '#' . $url ;
2010-02-18 18:15:56 +00:00
$links .= html_writer :: tag ( 'a' , $text , $attributes );
2010-02-07 08:45:21 +00:00
}
2010-02-18 18:15:56 +00:00
$output = html_writer :: tag ( 'div' , $links , array ( 'class' => 'skiplinks' )) . " \n " ;
2010-02-07 08:45:21 +00:00
2012-08-12 11:41:53 +02:00
// Then the clever trick for hiding of things not needed when JS works.
2010-02-07 08:45:21 +00:00
$output .= html_writer :: script ( " document.body.className += ' jsenabled'; " ) . " \n " ;
$this -> topofbodydone = true ;
return $output ;
}
/**
* Generate any HTML that needs to go at the end of the page .
*
* Normally , this method is called automatically by the code that prints the
* page footer . You should not normally need to call it in your own code .
*
* @ return string the HTML code to to at the end of the page .
*/
public function get_end_code () {
2010-05-04 08:29:05 +00:00
global $CFG ;
2012-08-12 11:41:53 +02:00
// Add other requested modules.
2010-02-07 08:45:21 +00:00
$output = $this -> get_extra_modules_code ();
2012-08-12 11:41:53 +02:00
// All the other linked scripts - there should be as few as possible.
2010-02-07 08:45:21 +00:00
if ( $this -> jsincludes [ 'footer' ]) {
foreach ( $this -> jsincludes [ 'footer' ] as $url ) {
$output .= html_writer :: script ( '' , $url );
}
}
2012-08-12 11:41:53 +02:00
// Add all needed strings.
2010-02-07 08:45:21 +00:00
if ( ! empty ( $this -> stringsforjs )) {
2012-06-21 09:58:45 +02:00
$strings = array ();
foreach ( $this -> stringsforjs as $component => $v ) {
foreach ( $v as $indentifier => $langstring ) {
$strings [ $component ][ $indentifier ] = $langstring -> out ();
}
}
$output .= html_writer :: script ( js_writer :: set_variable ( 'M.str' , $strings ));
2010-02-07 08:45:21 +00:00
}
2012-08-12 11:41:53 +02:00
// Add variables.
2010-02-07 08:45:21 +00:00
if ( $this -> jsinitvariables [ 'footer' ]) {
$js = '' ;
foreach ( $this -> jsinitvariables [ 'footer' ] as $data ) {
list ( $var , $value ) = $data ;
$js .= js_writer :: set_variable ( $var , $value , true );
}
$output .= html_writer :: script ( $js );
}
$inyuijs = $this -> get_javascript_code ( false );
$ondomreadyjs = $this -> get_javascript_code ( true );
$jsinit = $this -> get_javascript_init_code ();
$handlersjs = $this -> get_event_handler_code ();
2012-08-12 11:41:53 +02:00
// There is no global Y, make sure it is available in your scope.
2012-08-06 10:21:37 +02:00
$js = " YUI().use('node', function(Y) { \n { $inyuijs } { $ondomreadyjs } { $jsinit } { $handlersjs } \n }); " ;
2010-02-07 08:45:21 +00:00
$output .= html_writer :: script ( $js );
return $output ;
}
/**
2012-01-05 13:28:24 +13:00
* Have we already output the code in the < head > tag ?
*
2012-01-12 12:55:50 +13:00
* @ return bool
2010-02-07 08:45:21 +00:00
*/
public function is_head_done () {
return $this -> headdone ;
}
/**
2012-01-05 13:28:24 +13:00
* Have we already output the code at the start of the < body > tag ?
*
2012-01-12 12:55:50 +13:00
* @ return bool
2010-02-07 08:45:21 +00:00
*/
public function is_top_of_body_done () {
return $this -> topofbodydone ;
}
}
2010-02-07 12:50:53 +00:00
/**
* Invalidate all server and client side JS caches .
*/
function js_reset_all_caches () {
global $CFG ;
require_once ( " $CFG->libdir /filelib.php " );
2012-05-23 11:11:13 +02:00
$next = time ();
if ( isset ( $CFG -> jsrev ) and $next <= $CFG -> jsrev and $CFG -> jsrev - $next < 60 * 60 ) {
// This resolves problems when reset is requested repeatedly within 1s,
// the < 1h condition prevents accidental switching to future dates
// because we might not recover from it.
$next = $CFG -> jsrev + 1 ;
}
set_config ( 'jsrev' , $next );
2011-08-11 03:25:38 +09:30
fulldelete ( " $CFG->cachedir /js " );
2012-05-12 04:14:53 +08:00
}