MDL-29766 Add drag and drop upload to filemanager / filepicker elements
[moodle.git] / lib / outputrequirementslib.php
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
19 /**
20  * Library functions to facilitate the use of JavaScript in Moodle.
21  *
22  * @package    core
23  * @subpackage lib
24  * @copyright  2009 Tim Hunt, 2010 Petr Skoda
25  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26  */
28 defined('MOODLE_INTERNAL') || die();
30 // note: you can find history of this file in lib/ajax/ajaxlib.php
32 /**
33  * This class tracks all the things that are needed by the current page.
34  *
35  * Normally, the only instance of this  class you will need to work with is the
36  * one accessible via $PAGE->requires.
37  *
38  * Typical usage would be
39  * <pre>
40  *     $PAGE->requires->js_init_call('M.mod_forum.init_view');
41  * </pre>
42  *
43  * It also supports obsoleted coding style withouth YUI3 modules.
44  * <pre>
45  *     $PAGE->requires->css('/mod/mymod/userstyles.php?id='.$id); // not overridable via themes!
46  *     $PAGE->requires->js('/mod/mymod/script.js');
47  *     $PAGE->requires->js('/mod/mymod/small_but_urgent.js', true);
48  *     $PAGE->requires->js_function_call('init_mymod', array($data), true);
49  * </pre>
50  *
51  * There are some natural restrictions on some methods. For example, {@link css()}
52  * can only be called before the <head> tag is output. See the comments on the
53  * individual methods for details.
54  *
55  * @copyright 2009 Tim Hunt, 2010 Petr Skoda
56  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
57  * @since Moodle 2.0
58  */
59 class page_requirements_manager {
60     /** List of string available from JS */
61     protected $stringsforjs = array();
62     /** List of JS variables to be initialised */
63     protected $jsinitvariables = array('head'=>array(), 'footer'=>array());
64     /** Included JS scripts */
65     protected $jsincludes = array('head'=>array(), 'footer'=>array());
66     /** List of needed function calls */
67     protected $jscalls = array('normal'=>array(), 'ondomready'=>array());
68     /**
69      * List of skip links, those are needed for accessibility reasons
70      * @var array
71      */
72     protected $skiplinks = array();
73     /**
74      * Javascript code used for initialisation of page, it should be relatively small
75      * @var array
76      */
77     protected $jsinitcode = array();
78     /**
79      * Theme sheets, initialised only from core_renderer
80      * @var array of moodle_url
81      */
82     protected $cssthemeurls = array();
83     /**
84      * List of custom theme sheets, these are strongly discouraged!
85      * Useful mostly only for CSS submitted by teachers that is not part of the theme.
86      * @var array of moodle_url
87      */
88     protected $cssurls = array();
89     /**
90      * List of requested event handlers
91      * @var array
92      */
93     protected $eventhandlers = array();
94     /**
95      * Extra modules
96      * @var array
97      */
98     protected $extramodules = array();
99     /** Flag indicated head stuff already printed */
100     protected $headdone = false;
101     /** Flag indicating top of body already printed */
102     protected $topofbodydone = false;
104     /** YUI PHPLoader instance responsible for YUI2 loading from PHP only */
105     protected $yui2loader;
106     /** YUI PHPLoader instance responsible for YUI3 loading from PHP only */
107     protected $yui3loader;
108     /** YUI loader information for YUI3 loading from javascript */
109     protected $M_yui_loader;
110     /** some config vars exposed in JS, please no secret stuff there */
111     protected $M_cfg;
112     /** stores debug backtraces from when JS modules were included in the page */
113     protected $debug_moduleloadstacktraces = array();
115     /**
116      * Page requirements constructor.
117      */
118     public function __construct() {
119         global $CFG;
121         require_once("$CFG->libdir/yui/phploader/phploader/loader.php");
123         $this->yui3loader = new stdClass();
124         $this->yui2loader = new YAHOO_util_Loader($CFG->yui2version);
126         // set up some loader options
127         if (debugging('', DEBUG_DEVELOPER)) {
128             $this->yui3loader->filter = YUI_RAW; // for more detailed logging info use YUI_DEBUG here
129             $this->yui2loader->filter = YUI_RAW; // for more detailed logging info use YUI_DEBUG here
130             $this->yui2loader->allowRollups = false;
131         } else {
132             $this->yui3loader->filter = null;
133             $this->yui2loader->filter = null;
134         }
135         if (!empty($CFG->useexternalyui)) {
136             $this->yui3loader->base = 'http://yui.yahooapis.com/' . $CFG->yui3version . '/build/';
137             $this->yui2loader->base = 'http://yui.yahooapis.com/' . $CFG->yui2version . '/build/';
138             $this->yui3loader->comboBase = 'http://yui.yahooapis.com/combo?';
139             $this->yui2loader->comboBase = 'http://yui.yahooapis.com/combo?';
140         } else {
141             $this->yui3loader->base = $CFG->httpswwwroot . '/lib/yui/'. $CFG->yui3version . '/build/';
142             $this->yui2loader->base = $CFG->httpswwwroot . '/lib/yui/'. $CFG->yui2version . '/build/';
143             $this->yui3loader->comboBase = $CFG->httpswwwroot . '/theme/yui_combo.php?';
144             $this->yui2loader->comboBase = $CFG->httpswwwroot . '/theme/yui_combo.php?';
145         }
147         // enable combo loader? this significantly helps with caching and performance!
148         $this->yui3loader->combine = !empty($CFG->yuicomboloading);
149         $this->yui2loader->combine = !empty($CFG->yuicomboloading);
151         if (empty($CFG->cachejs)) {
152             $jsrev = -1;
153         } else if (empty($CFG->jsrev)) {
154             $jsrev = 1;
155         } else {
156             $jsrev = $CFG->jsrev;
157         }
159         // set up JS YUI loader helper object
160         $this->M_yui_loader = new stdClass();
161         $this->M_yui_loader->base         = $this->yui3loader->base;
162         $this->M_yui_loader->comboBase    = $this->yui3loader->comboBase;
163         $this->M_yui_loader->combine      = $this->yui3loader->combine;
164         $this->M_yui_loader->filter       = (string)$this->yui3loader->filter;
165         $this->M_yui_loader->insertBefore = 'firstthemesheet';
166         $this->M_yui_loader->modules      = array();
167         $this->M_yui_loader->groups       = array(
168             'moodle' => array(
169                 'name' => 'moodle',
170                 'base' => $CFG->httpswwwroot . '/theme/yui_combo.php?moodle/'.$jsrev.'/',
171                 'comboBase' => $CFG->httpswwwroot . '/theme/yui_combo.php?',
172                 'combine' => $this->yui3loader->combine,
173                 'filter' => '',
174                 'ext' => false,
175                 'root' => 'moodle/'.$jsrev.'/', // Add the rev to the root path so that we can control caching
176                 'patterns' => array(
177                     'moodle-' => array(
178                         'group' => 'moodle',
179                         'configFn' => '@MOODLECONFIGFN@'
180                     ),
181                     'root' => 'moodle'
182                 )
183             ),
184             'local' => array(
185                 'name' => 'gallery',
186                 'base' => $CFG->wwwroot.'/lib/yui/gallery/',
187                 'comboBase' => $CFG->httpswwwroot . '/theme/yui_combo.php?',
188                 'combine' => $this->yui3loader->combine,
189                 'filter' => $this->M_yui_loader->filter,
190                 'ext' => false,
191                 'root' => 'gallery/',
192                 'patterns' => array(
193                     'gallery-' => array(
194                         'group' => 'gallery',
195                         'configFn' => '@GALLERYCONFIGFN@',
196                     ),
197                     'root' => 'gallery'
198                 )
199             )
200         );
201         $this->add_yui2_modules(); // adds loading info for all YUI2 modules
202         $this->js_module($this->find_module('core_filepicker'));
203         $this->js_module($this->find_module('core_dock'));
205     }
207     /**
208      * This method adds yui2 modules into the yui3 JS loader-
209      * @return void
210      */
211     protected function add_yui2_modules() {
212         //note: this function is definitely not perfect, because
213         //      it adds tons of markup into each page, but it can be
214         //      abstracted into separate JS file with proper headers
215         global $CFG;
217         $GLOBALS['yui_current'] = array();
218         require($CFG->libdir.'/yui/phploader/lib/meta/config_'.$CFG->yui2version.'.php');
219         $info = $GLOBALS['yui_current'];
220         unset($GLOBALS['yui_current']);
222         if (empty($CFG->yuicomboloading)) {
223             $urlbase = $this->yui2loader->base;
224         } else {
225             $urlbase = $this->yui2loader->comboBase.$CFG->yui2version.'/build/';
226         }
228         $modules = array();
229         $ignored = array(); // list of CSS modules that are not needed
230         foreach ($info['moduleInfo'] as $name => $module) {
231             if ($module['type'] === 'css') {
232                 $ignored[$name] = true;
233             } else {
234                 $modules['yui2-'.$name] = $module;
235             }
236         }
237         foreach ($modules as $name=>$module) {
238             $module['fullpath'] = $urlbase.$module['path']; // fix path to point to correct location
239             unset($module['path']);
240             unset($module['skinnable']); // we load all YUI2 css automatically, this prevents weird missing css loader problems
241             foreach(array('requires', 'optional', 'supersedes') as $fixme) {
242                 if (!empty($module[$fixme])) {
243                     $fixed = false;
244                     foreach ($module[$fixme] as $key=>$dep) {
245                         if (isset($ignored[$dep])) {
246                             unset($module[$fixme][$key]);
247                             $fixed = true;
248                         } else {
249                             $module[$fixme][$key] = 'yui2-'.$dep;
250                         }
251                     }
252                     if ($fixed) {
253                         $module[$fixme] = array_merge($module[$fixme]); // fix keys
254                     }
255                 }
256             }
257             $this->M_yui_loader->modules[$name] = $module;
258             if (debugging('', DEBUG_DEVELOPER)) {
259                 if (!array_key_exists($name, $this->debug_moduleloadstacktraces)) {
260                     $this->debug_moduleloadstacktraces[$name] = array();
261                 }
262                 $this->debug_moduleloadstacktraces[$name][] = format_backtrace(debug_backtrace());
263             }
264         }
265     }
267     /**
268      * Initialise with the bits of JavaScript that every Moodle page should have.
269      *
270      * @param moodle_page $page
271      * @param core_renderer $renderer
272      */
273     protected function init_requirements_data(moodle_page $page, core_renderer $renderer) {
274         global $CFG;
276         // JavaScript should always work with $CFG->httpswwwroot rather than $CFG->wwwroot.
277         // Otherwise, in some situations, users will get warnings about insecure content
278         // on secure pages from their web browser.
280         $this->M_cfg = array(
281             'wwwroot'             => $CFG->httpswwwroot, // Yes, really. See above.
282             'sesskey'             => sesskey(),
283             'loadingicon'         => $renderer->pix_url('i/loading_small', 'moodle')->out(false),
284             'themerev'            => theme_get_revision(),
285             'theme'               => $page->theme->name,
286             'jsrev'               => ((empty($CFG->cachejs) or empty($CFG->jsrev)) ? -1 : $CFG->jsrev),
287         );
288         if (debugging('', DEBUG_DEVELOPER)) {
289             $this->M_cfg['developerdebug'] = true;
290             $this->yui2_lib('logger');
291         }
293         // accessibility stuff
294         $this->skip_link_to('maincontent', get_string('tocontent', 'access'));
296         // to be removed soon
297         $this->yui2_lib('dom');        // at least javascript-static.js needs to be migrated to YUI3
299         $this->string_for_js('confirmation', 'admin');
300         $this->string_for_js('cancel', 'moodle');
301         $this->string_for_js('yes', 'moodle');
303         if ($page->pagelayout === 'frametop') {
304             $this->js_init_call('M.util.init_frametop');
305         }
306     }
308     /**
309      * Ensure that the specified JavaScript file is linked to from this page.
310      *
311      * NOTE: This function is to be used in rare cases only, please store your JS in module.js file
312      * and use $PAGE->requires->js_init_call() instead.
313      *
314      * By default the link is put at the end of the page, since this gives best page-load performance.
315      *
316      * Even if a particular script is requested more than once, it will only be linked
317      * to once.
318      *
319      * @param string|moodle_url $url The path to the .js file, relative to $CFG->dirroot / $CFG->wwwroot.
320      *      For example '/mod/mymod/customscripts.js'; use moodle_url for external scripts
321      * @param bool $inhead initialise in head
322      * @return void
323      */
324     public function js($url, $inhead=false) {
325         $url = $this->js_fix_url($url);
326         $where = $inhead ? 'head' : 'footer';
327         $this->jsincludes[$where][$url->out()] = $url;
328     }
330     /**
331      * Ensure that the specified YUI2 library file, and all its required dependencies,
332      * are linked to from this page.
333      *
334      * By default the link is put at the end of the page, since this gives best page-load
335      * performance. Optional dependencies are not loaded automatically - if you want
336      * them you will need to load them first with other calls to this method.
337      *
338      * Even if a particular library is requested more than once (perhaps as a dependency
339      * of other libraries) it will only be linked to once.
340      *
341      * The library is leaded as soon as possible, if $OUTPUT->header() not used yet it
342      * is put into the page header, otherwise it is loaded in the page footer.
343      *
344      * @param string|array $libname the name of the YUI2 library you require. For example 'autocomplete'.
345      * @return void
346      */
347     public function yui2_lib($libname) {
348         $libnames = (array)$libname;
349         foreach ($libnames as $lib) {
350             $this->yui2loader->load($lib);
351         }
352     }
354     /**
355      * Returns the actual url through which a script is served.
356      * @param moodle_url|string $url full moodle url, or shortened path to script
357      * @return moodle_url
358      */
359     protected function js_fix_url($url) {
360         global $CFG;
362         if ($url instanceof moodle_url) {
363             return $url;
364         } else if (strpos($url, '/') === 0) {
365             if (debugging()) {
366                 // check file existence only when in debug mode
367                 if (!file_exists($CFG->dirroot . strtok($url, '?'))) {
368                     throw new coding_exception('Attempt to require a JavaScript file that does not exist.', $url);
369                 }
370             }
371             if (!empty($CFG->cachejs) and !empty($CFG->jsrev) and strpos($url, '/lib/editor/') !== 0 and substr($url, -3) === '.js') {
372                 return new moodle_url($CFG->httpswwwroot.'/lib/javascript.php', array('file'=>$url, 'rev'=>$CFG->jsrev));
373             } else {
374                 return new moodle_url($CFG->httpswwwroot.$url);
375             }
376         } else {
377             throw new coding_exception('Invalid JS url, it has to be shortened url starting with / or moodle_url instance.', $url);
378         }
379     }
381     /**
382      * Find out if JS module present and return details.
383      * @param string $component name of component in frankenstyle, ex: core_group, mod_forum
384      * @return array description of module or null if not found
385      */
386     protected function find_module($component) {
387         global $CFG;
389         $module = null;
392         if (strpos($component, 'core_') === 0) {
393             // must be some core stuff - list here is not complete, this is just the stuff used from multiple places
394             // so that we do nto have to repeat the definition of these modules over and over again
395             switch($component) {
396                 case 'core_filepicker':
397                     $module = array('name'     => 'core_filepicker',
398                                     'fullpath' => '/repository/filepicker.js',
399                                     'requires' => array('base', 'node', 'node-event-simulate', 'json', 'async-queue', 'io-base', 'io-upload-iframe', 'io-form', 'yui2-button', 'yui2-container', 'yui2-layout', 'yui2-menu', 'yui2-treeview', 'yui2-dragdrop', 'yui2-cookie'),
400                                     'strings'  => array(array('add', 'repository'), array('back', 'repository'), array('cancel', 'moodle'), array('close', 'repository'),
401                                                         array('cleancache', 'repository'), array('copying', 'repository'), array('date', 'repository'), array('downloadsucc', 'repository'),
402                                                         array('emptylist', 'repository'), array('error', 'repository'), array('federatedsearch', 'repository'),
403                                                         array('filenotnull', 'repository'), array('getfile', 'repository'), array('help', 'moodle'), array('iconview', 'repository'),
404                                                         array('invalidjson', 'repository'), array('linkexternal', 'repository'), array('listview', 'repository'),
405                                                         array('loading', 'repository'), array('login', 'repository'), array('logout', 'repository'), array('noenter', 'repository'),
406                                                         array('noresult', 'repository'), array('manageurl', 'repository'), array('popup', 'repository'), array('preview', 'repository'),
407                                                         array('refresh', 'repository'), array('save', 'repository'), array('saveas', 'repository'), array('saved', 'repository'),
408                                                         array('saving', 'repository'), array('search', 'repository'), array('searching', 'repository'), array('size', 'repository'),
409                                                         array('submit', 'repository'), array('sync', 'repository'), array('title', 'repository'), array('upload', 'repository'),
410                                                         array('uploading', 'repository'), array('xhtmlerror', 'repository'),
411                                                         array('cancel'), array('chooselicense', 'repository'), array('author', 'repository'),
412                                                         array('ok', 'moodle'), array('error', 'moodle'), array('info', 'moodle'), array('norepositoriesavailable', 'repository'), array('norepositoriesexternalavailable', 'repository'),
413                                                         array('nofilesattached', 'repository'), array('filepicker', 'repository'),
414                                                         array('nofilesavailable', 'repository'), array('overwrite', 'repository'),
415                                                         array('renameto', 'repository'), array('fileexists', 'repository'),
416                                                         array('fileexistsdialogheader', 'repository'), array('fileexistsdialog_editor', 'repository'),
417                                                         array('fileexistsdialog_filemanager', 'repository')
418                                                     ));
419                     break;
420                 case 'core_comment':
421                     $module = array('name'     => 'core_comment',
422                                     'fullpath' => '/comment/comment.js',
423                                     'requires' => array('base', 'io-base', 'node', 'json', 'yui2-animation', 'overlay'),
424                                     'strings' => array(array('confirmdeletecomments', 'admin'), array('yes', 'moodle'), array('no', 'moodle'))
425                                 );
426                     break;
427                 case 'core_role':
428                     $module = array('name'     => 'core_role',
429                                     'fullpath' => '/admin/roles/module.js',
430                                     'requires' => array('node', 'cookie'));
431                     break;
432                 case 'core_completion':
433                     $module = array('name'     => 'core_completion',
434                                     'fullpath' => '/course/completion.js');
435                     break;
436                 case 'core_dock':
437                     $module = array('name'     => 'core_dock',
438                                     'fullpath' => '/blocks/dock.js',
439                                     'requires' => array('base', 'node', 'event-custom', 'event-mouseenter', 'event-resize'),
440                                     'strings' => array(array('addtodock', 'block'),array('undockitem', 'block'),array('undockall', 'block'),array('thisdirectionvertical', 'langconfig')));
441                     break;
442                 case 'core_message':
443                     $module = array('name'     => 'core_message',
444                                     'requires' => array('base', 'node', 'event', 'node-event-simulate'),
445                                     'fullpath' => '/message/module.js');
446                     break;
447                 case 'core_group':
448                     $module = array('name'     => 'core_group',
449                                     'fullpath' => '/group/module.js',
450                                     'requires' => array('node', 'overlay', 'event-mouseenter'));
451                     break;
452                 case 'core_question_engine':
453                     $module = array('name'     => 'core_question_engine',
454                                     'fullpath' => '/question/qengine.js',
455                                     'requires' => array('node', 'event'));
456                     break;
457                 case 'core_rating':
458                     $module = array('name'     => 'core_rating',
459                                     'fullpath' => '/rating/module.js',
460                                     'requires' => array('node', 'event', 'overlay', 'io-base', 'json'));
461                     break;
462                 case 'core_filetree':
463                     $module = array('name'     => 'core_filetree',
464                                     'fullpath' => '/files/module.js',
465                                     'requires' => array('node', 'event', 'overlay', 'io-base', 'json', 'yui2-treeview'));
466                     break;
467                 case 'core_dndupload':
468                     $module = array('name'     => 'core_dndupload',
469                                     'fullpath' => '/lib/form/dndupload.js',
470                                     'requires' => array('node', 'event', 'json'),
471                                     'strings'  => array(array('uploadformlimit', 'moodle')));
472                     break;
473             }
475         } else {
476             if ($dir = get_component_directory($component)) {
477                 if (file_exists("$dir/module.js")) {
478                     if (strpos($dir, $CFG->dirroot.'/') === 0) {
479                         $dir = substr($dir, strlen($CFG->dirroot));
480                         $module = array('name'=>$component, 'fullpath'=>"$dir/module.js", 'requires' => array());
481                     }
482                 }
483             }
484         }
486         return $module;
487     }
489     /**
490      * Append YUI3 module to default YUI3 JS loader.
491      * The structure of module array is described at http://developer.yahoo.com/yui/3/yui/:
492      * @param string|array $module name of module (details are autodetected), or full module specification as array
493      * @return void
494      */
495     public function js_module($module) {
496         global $CFG;
498         if (empty($module)) {
499             throw new coding_exception('Missing YUI3 module name or full description.');
500         }
502         if (is_string($module)) {
503             $module = $this->find_module($module);
504         }
506         if (empty($module) or empty($module['name']) or empty($module['fullpath'])) {
507             throw new coding_exception('Missing YUI3 module details.');
508         }
510         // Don't load this module if we already have, no need to!
511         if ($this->js_module_loaded($module['name'])) {
512             if (debugging('', DEBUG_DEVELOPER)) {
513                 $this->debug_moduleloadstacktraces[$module['name']][] = format_backtrace(debug_backtrace());
514             }
515             return;
516         }
518         $module['fullpath'] = $this->js_fix_url($module['fullpath'])->out(false);
519         // add all needed strings
520         if (!empty($module['strings'])) {
521             foreach ($module['strings'] as $string) {
522                 $identifier = $string[0];
523                 $component = isset($string[1]) ? $string[1] : 'moodle';
524                 $a = isset($string[2]) ? $string[2] : null;
525                 $this->string_for_js($identifier, $component, $a);
526             }
527         }
528         unset($module['strings']);
530         // Process module requirements and attempt to load each. This allows
531         // moodle modules to require each other.
532         if (!empty($module['requires'])){
533             foreach ($module['requires'] as $requirement) {
534                 $rmodule = $this->find_module($requirement);
535                 if (is_array($rmodule)) {
536                     $this->js_module($rmodule);
537                 }
538             }
539         }
541         if ($this->headdone) {
542             $this->extramodules[$module['name']] = $module;
543         } else {
544             $this->M_yui_loader->modules[$module['name']] = $module;
545         }
546         if (debugging('', DEBUG_DEVELOPER)) {
547             if (!array_key_exists($module['name'], $this->debug_moduleloadstacktraces)) {
548                 $this->debug_moduleloadstacktraces[$module['name']] = array();
549             }
550             $this->debug_moduleloadstacktraces[$module['name']][] = format_backtrace(debug_backtrace());
551         }
552     }
554     /**
555      * Returns true if the module has already been loaded.
556      *
557      * @param string|array $module
558      * @return bool True if the module has already been loaded
559      */
560     protected function js_module_loaded($module) {
561         if (is_string($module)) {
562             $modulename = $module;
563         } else {
564             $modulename = $module['name'];
565         }
566         return array_key_exists($modulename, $this->M_yui_loader->modules) ||
567                array_key_exists($modulename, $this->extramodules);
568     }
570     /**
571      * Returns the stacktraces from loading js modules.
572      * @return array
573      */
574     public function get_loaded_modules() {
575         return $this->debug_moduleloadstacktraces;
576     }
578     /**
579      * Ensure that the specified CSS file is linked to from this page.
580      *
581      * Because stylesheet links must go in the <head> part of the HTML, you must call
582      * this function before {@link get_head_code()} is called. That normally means before
583      * the call to print_header. If you call it when it is too late, an exception
584      * will be thrown.
585      *
586      * Even if a particular style sheet is requested more than once, it will only
587      * be linked to once.
588      *
589      * Please note use of this feature is strongly discouraged,
590      * it is suitable only for places where CSS is submitted directly by teachers.
591      * (Students must not be allowed to submit any external CSS because it may
592      * contain embedded javascript!). Example of correct use is mod/data.
593      *
594      * @param string $stylesheet The path to the .css file, relative to $CFG->wwwroot.
595      *   For example:
596      *      $PAGE->requires->css('mod/data/css.php?d='.$data->id);
597      */
598     public function css($stylesheet) {
599         global $CFG;
601         if ($this->headdone) {
602             throw new coding_exception('Cannot require a CSS file after &lt;head> has been printed.', $stylesheet);
603         }
605         if ($stylesheet instanceof moodle_url) {
606             // ok
607         } else if (strpos($stylesheet, '/') === 0) {
608             $stylesheet = new moodle_url($CFG->httpswwwroot.$stylesheet);
609         } else {
610             throw new coding_exception('Invalid stylesheet parameter.', $stylesheet);
611         }
613         $this->cssurls[$stylesheet->out()] = $stylesheet; // overrides
614     }
616     /**
617      * Add theme stylkesheet to page - do not use from plugin code,
618      * this should be called only from the core renderer!
619      * @param moodle_url $stylesheet
620      * @return void
621      */
622     public function css_theme(moodle_url $stylesheet) {
623         $this->cssthemeurls[] = $stylesheet;
624     }
626     /**
627      * Ensure that a skip link to a given target is printed at the top of the <body>.
628      *
629      * You must call this function before {@link get_top_of_body_code()}, (if not, an exception
630      * will be thrown). That normally means you must call this before the call to print_header.
631      *
632      * If you ask for a particular skip link to be printed, it is then your responsibility
633      * to ensure that the appropriate <a name="..."> tag is printed in the body of the
634      * page, so that the skip link goes somewhere.
635      *
636      * Even if a particular skip link is requested more than once, only one copy of it will be output.
637      *
638      * @param $target the name of anchor this link should go to. For example 'maincontent'.
639      * @param $linktext The text to use for the skip link. Normally get_string('skipto', 'access', ...);
640      */
641     public function skip_link_to($target, $linktext) {
642         if ($this->topofbodydone) {
643             debugging('Page header already printed, can not add skip links any more, code needs to be fixed.');
644             return;
645         }
646         $this->skiplinks[$target] = $linktext;
647     }
649     /**
650      * !!!DEPRECATED!!! please use js_init_call() if possible
651      * Ensure that the specified JavaScript function is called from an inline script
652      * somewhere on this page.
653      *
654      * By default the call will be put in a script tag at the
655      * end of the page after initialising Y instance, since this gives best page-load
656      * performance and allows you to use YUI3 library.
657      *
658      * If you request that a particular function is called several times, then
659      * that is what will happen (unlike linking to a CSS or JS file, where only
660      * one link will be output).
661      *
662      * The main benefit of the method is the automatic encoding of all function parameters.
663      *
664      * @param string $function the name of the JavaScritp function to call. Can
665      *      be a compound name like 'Y.Event.purgeElement'. Can also be
666      *      used to create and object by using a 'function name' like 'new user_selector'.
667      * @param array $arguments and array of arguments to be passed to the function.
668      *      When generating the function call, this will be escaped using json_encode,
669      *      so passing objects and arrays should work.
670      * @param bool $ondomready
671      * @param int $delay
672      * @return void
673      */
674     public function js_function_call($function, array $arguments = null, $ondomready = false, $delay = 0) {
675         $where = $ondomready ? 'ondomready' : 'normal';
676         $this->jscalls[$where][] = array($function, $arguments, $delay);
677     }
679     /**
680      * Adds a call to make use of a YUI gallery module. DEPRECATED DO NOT USE!!!
681      *
682      * @deprecated DO NOT USE
683      *
684      * @param string|array $modules One or more gallery modules to require
685      * @param string $version
686      * @param string $function
687      * @param array $arguments
688      * @param bool $ondomready
689      */
690     public function js_gallery_module($modules, $version, $function, array $arguments = null, $ondomready = false) {
691         global $CFG;
692         debugging('This function will be removed before 2.0 is released please change it from js_gallery_module to yui_module', DEBUG_DEVELOPER);
693         $this->yui_module($modules, $function, $arguments, $version, $ondomready);
694     }
696     /**
697      * Creates a JavaScript function call that requires one or more modules to be loaded
698      *
699      * This function can be used to include all of the standard YUI module types within JavaScript:
700      *     - YUI3 modules    [node, event, io]
701      *     - YUI2 modules    [yui2-*]
702      *     - Moodle modules  [moodle-*]
703      *     - Gallery modules [gallery-*]
704      *
705      * @param array|string $modules One or more modules
706      * @param string $function The function to call once modules have been loaded
707      * @param array $arguments An array of arguments to pass to the function
708      * @param string $galleryversion The gallery version to use
709      * @param bool $ondomready
710      */
711     public function yui_module($modules, $function, array $arguments = null, $galleryversion = '2010.04.08-12-35', $ondomready = false) {
712         global $CFG;
714         if (!is_array($modules)) {
715             $modules = array($modules);
716         }
717         if (empty($CFG->useexternalyui) || true) {
718             // We need to set the M.yui.galleryversion to the correct version
719             $jscode = 'M.yui.galleryversion='.json_encode($galleryversion).';';
720         } else {
721             // Set Y's config.gallery to the version
722             $jscode = 'Y.config.gallery='.json_encode($galleryversion).';';
723         }
724         $jscode .= 'Y.use('.join(',', array_map('json_encode', $modules)).',function() {'.js_writer::function_call($function, $arguments).'});';
725         if ($ondomready) {
726             $jscode = "Y.on('domready', function() { $jscode });";
727         }
728         $this->jsinitcode[] = $jscode;
729     }
731     /**
732      * Ensure that the specified JavaScript function is called from an inline script
733      * from page footer.
734      *
735      * @param string $function the name of the JavaScritp function to with init code,
736      *      usually something like 'M.mod_mymodule.init'
737      * @param array $extraarguments and array of arguments to be passed to the function.
738      *      The first argument is always the YUI3 Y instance with all required dependencies
739      *      already loaded.
740      * @param bool $ondomready wait for dom ready (helps with some IE problems when modifying DOM)
741      * @param array $module JS module specification array
742      * @return void
743      */
744     public function js_init_call($function, array $extraarguments = null, $ondomready = false, array $module = null) {
745         $jscode = js_writer::function_call_with_Y($function, $extraarguments);
746         if (!$module) {
747             // detect module automatically
748             if (preg_match('/M\.([a-z0-9]+_[^\.]+)/', $function, $matches)) {
749                 $module = $this->find_module($matches[1]);
750             }
751         }
753         $this->js_init_code($jscode, $ondomready, $module);
754     }
756     /**
757      * Add short static javascript code fragment to page footer.
758      * This is intended primarily for loading of js modules and initialising page layout.
759      * Ideally the JS code fragment should be stored in plugin renderer so that themes
760      * may override it.
761      * @param string $jscode
762      * @param bool $ondomready wait for dom ready (helps with some IE problems when modifying DOM)
763      * @param array $module JS module specification array
764      * @return void
765      */
766     public function js_init_code($jscode, $ondomready = false, array $module = null) {
767         $jscode = trim($jscode, " ;\n"). ';';
769         if ($module) {
770             $this->js_module($module);
771             $modulename = $module['name'];
772             $jscode = "Y.use('$modulename', function(Y) { $jscode });";
773         }
775         if ($ondomready) {
776             $jscode = "Y.on('domready', function() { $jscode });";
777         }
779         $this->jsinitcode[] = $jscode;
780     }
782     /**
783      * Make a language string available to JavaScript.
784      *
785      * All the strings will be available in a M.str object in the global namespace.
786      * So, for example, after a call to $PAGE->requires->string_for_js('course', 'moodle');
787      * then the JavaScript variable M.str.moodle.course will be 'Course', or the
788      * equivalent in the current language.
789      *
790      * The arguments to this function are just like the arguments to get_string
791      * except that $component is not optional, and there are some aspects to consider
792      * when the string contains {$a} placeholder.
793      *
794      * If the string does not contain any {$a} placeholder, you can simply use
795      * M.str.component.identifier to obtain it. If you prefer, you can call
796      * M.util.get_string(identifier, component) to get the same result.
797      *
798      * If you need to use {$a} placeholders, there are two options. Either the
799      * placeholder should be substituted in PHP on server side or it should
800      * be substituted in Javascript at client side.
801      *
802      * To substitute the placeholder at server side, just provide the required
803      * value for the placeholder when you require the string. Because each string
804      * is only stored once in the JavaScript (based on $identifier and $module)
805      * you cannot get the same string with two different values of $a. If you try,
806      * an exception will be thrown. Once the placeholder is substituted, you can
807      * use M.str or M.util.get_string() as shown above:
808      *
809      *     // require the string in PHP and replace the placeholder
810      *     $PAGE->requires->string_for_js('fullnamedisplay', 'moodle', $USER);
811      *     // use the result of the substitution in Javascript
812      *     alert(M.str.moodle.fullnamedisplay);
813      *
814      * To substitute the placeholder at client side, use M.util.get_string()
815      * function. It implements the same logic as {@see get_string()}:
816      *
817      *     // require the string in PHP but keep {$a} as it is
818      *     $PAGE->requires->string_for_js('fullnamedisplay', 'moodle');
819      *     // provide the values on the fly in Javascript
820      *     user = { firstname : 'Harry', lastname : 'Potter' }
821      *     alert(M.util.get_string('fullnamedisplay', 'moodle', user);
822      *
823      * If you do need the same string expanded with different $a values in PHP
824      * on server side, then the solution is to put them in your own data structure
825      * (e.g. and array) that you pass to JavaScript with {@link data_for_js()}.
826      *
827      * @param string $identifier the desired string.
828      * @param string $component the language file to look in.
829      * @param mixed $a any extra data to add into the string (optional).
830      */
831     public function string_for_js($identifier, $component, $a = NULL) {
832         $string = get_string($identifier, $component, $a);
833         if (!$component) {
834             throw new coding_exception('The $module parameter is required for page_requirements_manager::string_for_js.');
835         }
836         if (isset($this->stringsforjs[$component][$identifier]) && $this->stringsforjs[$component][$identifier] !== $string) {
837             throw new coding_exception("Attempt to re-define already required string '$identifier' " .
838                     "from lang file '$component'. Did you already ask for it with a different \$a? {$this->stringsforjs[$component][$identifier]} !== $string");
839         }
840         $this->stringsforjs[$component][$identifier] = $string;
841     }
843     /**
844      * Make an array of language strings available for JS
845      *
846      * This function calls the above function {@link string_for_js()} for each requested
847      * string in the $identifiers array that is passed to the argument for a single module
848      * passed in $module.
849      *
850      * <code>
851      * $PAGE->requires->strings_for_js(array('one', 'two', 'three'), 'mymod', array('a', null, 3));
852      *
853      * // The above is identitical to calling
854      *
855      * $PAGE->requires->string_for_js('one', 'mymod', 'a');
856      * $PAGE->requires->string_for_js('two', 'mymod');
857      * $PAGE->requires->string_for_js('three', 'mymod', 3);
858      * </code>
859      *
860      * @param array $identifiers An array of desired strings
861      * @param string $component The module to load for
862      * @param mixed $a This can either be a single variable that gets passed as extra
863      *         information for every string or it can be an array of mixed data where the
864      *         key for the data matches that of the identifier it is meant for.
865      *
866      */
867     public function strings_for_js($identifiers, $component, $a=NULL) {
868         foreach ($identifiers as $key => $identifier) {
869             if (is_array($a) && array_key_exists($key, $a)) {
870                 $extra = $a[$key];
871             } else {
872                 $extra = $a;
873             }
874             $this->string_for_js($identifier, $component, $extra);
875         }
876     }
878     /**
879      * !!!!!!DEPRECATED!!!!!! please use js_init_call() for everything now.
880      *
881      * Make some data from PHP available to JavaScript code.
882      *
883      * For example, if you call
884      * <pre>
885      *      $PAGE->requires->data_for_js('mydata', array('name' => 'Moodle'));
886      * </pre>
887      * then in JavsScript mydata.name will be 'Moodle'.
888      * @param string $variable the the name of the JavaScript variable to assign the data to.
889      *      Will probably work if you use a compound name like 'mybuttons.button[1]', but this
890      *      should be considered an experimental feature.
891      * @param mixed $data The data to pass to JavaScript. This will be escaped using json_encode,
892      *      so passing objects and arrays should work.
893      * @param bool $inhead initialise in head
894      * @return void
895      */
896     public function data_for_js($variable, $data, $inhead=false) {
897         $where = $inhead ? 'head' : 'footer';
898         $this->jsinitvariables[$where][] = array($variable, $data);
899     }
901     /**
902      * Creates a YUI event handler.
903      *
904      * @param mixed $selector standard YUI selector for elemnts, may be array or string, element id is in the form "#idvalue"
905      * @param string $event A valid DOM event (click, mousedown, change etc.)
906      * @param string $function The name of the function to call
907      * @param array  $arguments An optional array of argument parameters to pass to the function
908      * @return void
909      */
910     public function event_handler($selector, $event, $function, array $arguments = null) {
911         $this->eventhandlers[] = array('selector'=>$selector, 'event'=>$event, 'function'=>$function, 'arguments'=>$arguments);
912     }
914     /**
915      * Returns code needed for registering of event handlers.
916      * @return string JS code
917      */
918     protected function get_event_handler_code() {
919         $output = '';
920         foreach ($this->eventhandlers as $h) {
921             $output .= js_writer::event_handler($h['selector'], $h['event'], $h['function'], $h['arguments']);
922         }
923         return $output;
924     }
926     /**
927      * Get the inline JavaScript code that need to appear in a particular place.
928      * @param bool $ondomready
929      * @return string
930      */
931     protected function get_javascript_code($ondomready) {
932         $where = $ondomready ? 'ondomready' : 'normal';
933         $output = '';
934         if ($this->jscalls[$where]) {
935             foreach ($this->jscalls[$where] as $data) {
936                 $output .= js_writer::function_call($data[0], $data[1], $data[2]);
937             }
938             if (!empty($ondomready)) {
939                 $output = "    Y.on('domready', function() {\n$output\n    });";
940             }
941         }
942         return $output;
943     }
945     /**
946      * Returns js code to be executed when Y is available.
947      * @return unknown_type
948      */
949     protected function get_javascript_init_code() {
950         if (count($this->jsinitcode)) {
951             return implode("\n", $this->jsinitcode) . "\n";
952         }
953         return '';
954     }
956     /**
957      * Returns basic YUI3 JS loading code.
958      * YUI3 is using autoloading of both CSS and JS code.
959      *
960      * Major benefit of this compared to standard js/csss loader is much improved
961      * caching, better browser cache utilisation, much fewer http requests.
962      *
963      * @return string
964      */
965     protected function get_yui3lib_headcode() {
966         global $CFG;
968         $code = '';
970         if ($this->yui3loader->combine) {
971             $code .= '<link rel="stylesheet" type="text/css" href="'.$this->yui3loader->comboBase
972                      .$CFG->yui3version.'/build/cssreset/reset-min.css&amp;'
973                      .$CFG->yui3version.'/build/cssfonts/fonts-min.css&amp;'
974                      .$CFG->yui3version.'/build/cssgrids/grids-min.css&amp;'
975                      .$CFG->yui3version.'/build/cssbase/base-min.css" />';
976         } else {
977             $code .= '<link rel="stylesheet" type="text/css" href="'.$this->yui3loader->base.'cssreset/reset-min.css" />';
978             $code .= '<link rel="stylesheet" type="text/css" href="'.$this->yui3loader->base.'cssfonts/fonts-min.css" />';
979             $code .= '<link rel="stylesheet" type="text/css" href="'.$this->yui3loader->base.'cssgrids/grids-min.css" />';
980             $code .= '<link rel="stylesheet" type="text/css" href="'.$this->yui3loader->base.'cssbase/base-min.css" />';
981         }
983         $code .= '<script type="text/javascript" src="'.$this->yui3loader->base.'yui/yui-min.js"></script>';
985         if ($this->yui3loader->filter === YUI_RAW) {
986             $code = str_replace('-min.css', '.css', $code);
987             $code = str_replace('-min.js', '.js', $code);
988         } else if ($this->yui3loader->filter === YUI_DEBUG) {
989             $code = str_replace('-min.css', '.css', $code);
990             $code = str_replace('-min.js', '-debug.js', $code);
991         }
993         return $code;
994     }
996     /**
997      * Returns basic YUI2 JS loading code.
998      * It can be called manually at any time.
999      * If called manually the result needs to be output using echo().
1000      *
1001      * Major benefit of this compared to standard js loader is much improved
1002      * caching, better browser cache utilisation, much fewer http requests.
1003      *
1004      * All YUI2 CSS is loaded automatically.
1005      *
1006      * @return string JS embedding code
1007      */
1008     public function get_yui2lib_code() {
1009         global $CFG;
1011         if ($this->headdone) {
1012             $code = $this->yui2loader->script();
1013         } else {
1014             $code = $this->yui2loader->script();
1015             if ($this->yui2loader->combine) {
1016                 $skinurl = $this->yui2loader->comboBase . $CFG->yui2version . '/build/assets/skins/sam/skin.css';
1017             } else {
1018                 $skinurl = $this->yui2loader->base . 'assets/skins/sam/skin.css';
1019             }
1020             // please note this is a temporary hack until we fully migrate to later YUI3 that has all the widgets
1021             $attributes = array('rel'=>'stylesheet', 'type'=>'text/css', 'href'=>$skinurl);
1022             $code .= "\n" . html_writer::empty_tag('link', $attributes) . "\n";
1023         }
1024         $code = str_replace('&amp;', '&', $code);
1025         $code = str_replace('&', '&amp;', $code);
1026         return $code;
1027     }
1029     /**
1030      * Returns html tags needed for inclusion of theme CSS
1031      * @return string
1032      */
1033     protected function get_css_code() {
1034         // First of all the theme CSS, then any custom CSS
1035         // Please note custom CSS is strongly discouraged,
1036         // because it can not be overridden by themes!
1037         // It is suitable only for things like mod/data which accepts CSS from teachers.
1038         $attributes = array('rel'=>'stylesheet', 'type'=>'text/css');
1040         // This line of code may look funny but it is currently required in order
1041         // to avoid MASSIVE display issues in Internet Explorer.
1042         // As of IE8 + YUI3.1.1 the reference stylesheet (firstthemesheet) gets
1043         // ignored whenever another resource is added until such time as a redraw
1044         // is forced, usually by moving the mouse over the affected element.
1045         $code = html_writer::tag('script', '/** Required in order to fix style inclusion problems in IE with YUI **/', array('id'=>'firstthemesheet', 'type'=>'text/css'));
1047         $urls = $this->cssthemeurls + $this->cssurls;
1048         foreach ($urls as $url) {
1049             $attributes['href'] = $url;
1050             $code .= html_writer::empty_tag('link', $attributes) . "\n";
1051             // this id is needed in first sheet only so that theme may override YUI sheets laoded on the fly
1052             unset($attributes['id']);
1053         }
1055         return $code;
1056     }
1058     /**
1059      * Adds extra modules specified after printing of page header
1060      * @return string
1061      */
1062     protected function get_extra_modules_code() {
1063         if (empty($this->extramodules)) {
1064             return '';
1065         }
1066         return html_writer::script(js_writer::function_call('M.yui.add_module', array($this->extramodules)));
1067     }
1069     /**
1070      * Generate any HTML that needs to go inside the <head> tag.
1071      *
1072      * Normally, this method is called automatically by the code that prints the
1073      * <head> tag. You should not normally need to call it in your own code.
1074      *
1075      * @param moodle_page $page
1076      * @param core_renderer $renderer
1077      * @return string the HTML code to to inside the <head> tag.
1078      */
1079     public function get_head_code(moodle_page $page, core_renderer $renderer) {
1080         global $CFG;
1082         // note: the $page and $output are not stored here because it would
1083         // create circular references in memory which prevents garbage collection
1084         $this->init_requirements_data($page, $renderer);
1086         // yui3 JS and CSS is always loaded first - it is cached in browser
1087         $output = $this->get_yui3lib_headcode();
1089         // BC: load basic YUI2 for now, all yui2 things should be loaded via Y.use('yui2-oldmodulename')
1090         $output .= $this->get_yui2lib_code();
1092         // now theme CSS + custom CSS in this specific order
1093         $output .= $this->get_css_code();
1095         // set up global YUI3 loader object - this should contain all code needed by plugins
1096         // note: in JavaScript just use "YUI(M.yui.loader).use('overlay', function(Y) { .... });"
1097         // this needs to be done before including any other script
1098         $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('/');};\n";
1099         $js .= js_writer::set_variable('M.yui.loader', $this->M_yui_loader, false) . "\n";
1100         $js .= js_writer::set_variable('M.cfg', $this->M_cfg, false);
1101         $js = str_replace('"@GALLERYCONFIGFN@"', 'galleryConfigFn', $js);
1102         $js = str_replace('"@MOODLECONFIGFN@"', 'moodleConfigFn', $js);
1104         $output .= html_writer::script($js);
1106         // link our main JS file, all core stuff should be there
1107         $output .= html_writer::script('', $this->js_fix_url('/lib/javascript-static.js'));
1109         // add variables
1110         if ($this->jsinitvariables['head']) {
1111             $js = '';
1112             foreach ($this->jsinitvariables['head'] as $data) {
1113                 list($var, $value) = $data;
1114                 $js .= js_writer::set_variable($var, $value, true);
1115             }
1116             $output .= html_writer::script($js);
1117         }
1119         // all the other linked things from HEAD - there should be as few as possible
1120         if ($this->jsincludes['head']) {
1121             foreach ($this->jsincludes['head'] as $url) {
1122                 $output .= html_writer::script('', $url);
1123             }
1124         }
1126         // mark head sending done, it is not possible to anything there
1127         $this->headdone = true;
1129         return $output;
1130     }
1132     /**
1133      * Generate any HTML that needs to go at the start of the <body> tag.
1134      *
1135      * Normally, this method is called automatically by the code that prints the
1136      * <head> tag. You should not normally need to call it in your own code.
1137      *
1138      * @return string the HTML code to go at the start of the <body> tag.
1139      */
1140     public function get_top_of_body_code() {
1141         // first the skip links
1142         $links = '';
1143         $attributes = array('class'=>'skip');
1144         foreach ($this->skiplinks as $url => $text) {
1145             $attributes['href'] = '#' . $url;
1146             $links .= html_writer::tag('a', $text, $attributes);
1147         }
1148         $output = html_writer::tag('div', $links, array('class'=>'skiplinks')) . "\n";
1150         // then the clever trick for hiding of things not needed when JS works
1151         $output .= html_writer::script("document.body.className += ' jsenabled';") . "\n";
1152         $this->topofbodydone = true;
1153         return $output;
1154     }
1156     /**
1157      * Generate any HTML that needs to go at the end of the page.
1158      *
1159      * Normally, this method is called automatically by the code that prints the
1160      * page footer. You should not normally need to call it in your own code.
1161      *
1162      * @return string the HTML code to to at the end of the page.
1163      */
1164     public function get_end_code() {
1165         global $CFG;
1166         // add other requested modules
1167         $output = $this->get_extra_modules_code();
1169         // add missing YUI2 YUI - to be removed once we convert everything to YUI3!
1170         $output .= $this->get_yui2lib_code();
1172         // all the other linked scripts - there should be as few as possible
1173         if ($this->jsincludes['footer']) {
1174             foreach ($this->jsincludes['footer'] as $url) {
1175                 $output .= html_writer::script('', $url);
1176             }
1177         }
1179         // add all needed strings
1180         if (!empty($this->stringsforjs)) {
1181             $output .= html_writer::script(js_writer::set_variable('M.str', $this->stringsforjs));
1182         }
1184         // add variables
1185         if ($this->jsinitvariables['footer']) {
1186             $js = '';
1187             foreach ($this->jsinitvariables['footer'] as $data) {
1188                 list($var, $value) = $data;
1189                 $js .= js_writer::set_variable($var, $value, true);
1190             }
1191             $output .= html_writer::script($js);
1192         }
1194         $inyuijs = $this->get_javascript_code(false);
1195         $ondomreadyjs = $this->get_javascript_code(true);
1196         $jsinit = $this->get_javascript_init_code();
1197         $handlersjs = $this->get_event_handler_code();
1199         // there is no global Y, make sure it is available in your scope
1200         $js = "YUI(M.yui.loader).use('node', function(Y) {\n{$inyuijs}{$ondomreadyjs}{$jsinit}{$handlersjs}\n});";
1202         $output .= html_writer::script($js);
1204         return $output;
1205     }
1207     /**
1208      * @return boolean Have we already output the code in the <head> tag?
1209      */
1210     public function is_head_done() {
1211         return $this->headdone;
1212     }
1214     /**
1215      * @return boolean Have we already output the code at the start of the <body> tag?
1216      */
1217     public function is_top_of_body_done() {
1218         return $this->topofbodydone;
1219     }
1222 /**
1223  * Invalidate all server and client side JS caches.
1224  * @return void
1225  */
1226 function js_reset_all_caches() {
1227     global $CFG;
1228     require_once("$CFG->libdir/filelib.php");
1230     set_config('jsrev', empty($CFG->jsrev) ? 1 : $CFG->jsrev+1);
1231     fulldelete("$CFG->cachedir/js");