MDL-21400 removing inhead - not tested
[moodle.git] / lib / ajax / ajaxlib.php
CommitLineData
f72f94a2 1<?php
b2330db6 2
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/>.
17
18
19/**
20 * Library functions to facilitate the use of JavaScript in Moodle.
21 *
22 * @package moodlecore
23 * @copyright 2009 Tim Hunt
24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 */
26
27
b2330db6 28/**
29 * This class tracks all the things that are needed by the current page.
30 *
31 * Normally, the only instance of this class you will need to work with is the
32 * one accessible via $PAGE->requires.
33 *
34 * Typical useage would be
9ca13950 35 * <pre>
9dec75db
PS
36 * $PAGE->requires->css('/mod/mymod/userstyles.php?id='.$id); // not overriddable via themes!
37 * $PAGE->requires->js('/mod/mymod/script.js');
38 * $PAGE->requires->js('/mod/mymod/small_but_urgent.js')->in_head();
b2330db6 39 * $PAGE->requires->js_function_call('init_mymod', array($data))->on_dom_ready();
9ca13950 40 * </pre>
b2330db6 41 *
2c144fc3 42 * There are some natural restrictions on some methods. For example, {@link css()}
43 * can only be called before the <head> tag is output. See the comments on the
44 * individual methods for details.
45 *
b2330db6 46 * @copyright 2009 Tim Hunt
47 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5c5418fe 48 * @since Moodle 2.0
b2330db6 49 */
50class page_requirements_manager {
51 const WHEN_IN_HEAD = 0;
f5de8ee7
PS
52 const WHEN_AT_END = 10;
53 const WHEN_IN_YUI = 20;
b2330db6 54 const WHEN_ON_DOM_READY = 30;
55
9e43d2b3 56 protected $linkedrequirements = array();
b2330db6 57 protected $stringsforjs = array();
58 protected $requiredjscode = array();
59
f5de8ee7
PS
60 /**
61 * List of skip links, those are needed for accessibility reasons
62 * @var array
63 */
64 protected $skiplinks = array();
08fac734
PS
65 /**
66 * Javascript code used for initialisation of page, it shoudl be relatively small
67 * @var array
68 */
69 protected $jsinitcode = array();
c0467479
PS
70 /**
71 * Theme sheets, initialised only from core_renderer
72 * @var array of moodle_url
73 */
e50b4c89 74 protected $cssthemeurls = array();
c0467479
PS
75 /**
76 * List of custom theme sheets, these are strongly discouraged!
77 * Useful mostly only for CSS submitted by teachers that is not part of the theme.
78 * @var array of moodle_url
79 */
e50b4c89 80 protected $cssurls = array();
227255b8
PS
81 /**
82 * List of requested event handlers
83 * @var array
84 */
85 protected $eventhandlers = array();
e50b4c89
PS
86 /**
87 * Extra modules
88 * @var array
89 */
90 protected $extramodules = array();
91
b2330db6 92 protected $variablesinitialised = array('mstr' => 1); // 'mstr' is special. See string_for_js.
93
94 protected $headdone = false;
95 protected $topofbodydone = false;
96
945f19f7 97 /** YUI PHPLoader instance responsible for YUI2 loading from PHP only */
985d1d1d 98 protected $yui2loader;
945f19f7
PS
99 /** YUI PHPLoader instance responsible for YUI3 loading from PHP only */
100 protected $yui3loader;
101 /** YUI PHPLoader instance responsible for YUI3 loading from javascript */
e50b4c89 102 protected $M_yui_loader;
1be3ec80 103 /** some config vars exposed in JS, please no secret stuff there */
9598d578 104 protected $M_cfg;
4c907141
PS
105
106 /**
107 * Page requirements constructor.
108 */
109 public function __construct() {
110 global $CFG;
945f19f7 111
4c907141
PS
112 require_once("$CFG->libdir/yui/phploader/phploader/loader.php");
113
114 $this->yui3loader = new YAHOO_util_Loader($CFG->yui3version);
985d1d1d 115 $this->yui2loader = new YAHOO_util_Loader($CFG->yui2version);
4c907141
PS
116
117 // set up some loader options
4c907141
PS
118 if (debugging('', DEBUG_DEVELOPER)) {
119 $this->yui3loader->filter = YUI_DEBUG; // alternatively we could use just YUI_RAW here
985d1d1d 120 $this->yui2loader->filter = YUI_DEBUG; // alternatively we could use just YUI_RAW here
4c907141
PS
121 } else {
122 $this->yui3loader->filter = null;
985d1d1d 123 $this->yui2loader->filter = null;
95fbe49b 124 }
4c907141
PS
125 if (!empty($CFG->useexternalyui)) {
126 $this->yui3loader->base = 'http://yui.yahooapis.com/' . $CFG->yui3version . '/build/';
985d1d1d 127 $this->yui2loader->base = 'http://yui.yahooapis.com/' . $CFG->yui2version . '/build/';
945f19f7
PS
128 $this->yui3loader->comboBase = 'http://yui.yahooapis.com/combo?';
129 $this->yui2loader->comboBase = 'http://yui.yahooapis.com/combo?';
4c907141 130 } else {
aa42314d
PS
131 $this->yui3loader->base = $CFG->httpswwwroot . '/lib/yui/'. $CFG->yui3version . '/build/';
132 $this->yui2loader->base = $CFG->httpswwwroot . '/lib/yui/'. $CFG->yui2version . '/build/';
945f19f7
PS
133 $this->yui3loader->comboBase = $CFG->httpswwwroot . '/theme/yui_combo.php?';
134 $this->yui2loader->comboBase = $CFG->httpswwwroot . '/theme/yui_combo.php?';
4c907141
PS
135 }
136
945f19f7 137 // enable combo loader? this significantly helps with caching and performance!
aa42314d
PS
138 $this->yui3loader->combine = !empty($CFG->yuicomboloading);
139 $this->yui2loader->combine = !empty($CFG->yuicomboloading);
945f19f7 140
2212eb7a 141 // set up JS YUI loader helper object
e50b4c89
PS
142 $this->M_yui_loader = new stdClass();
143 $this->M_yui_loader->base = $this->yui3loader->base;
144 $this->M_yui_loader->comboBase = $this->yui3loader->comboBase;
145 $this->M_yui_loader->combine = $this->yui3loader->combine;
146 $this->M_yui_loader->filter = ($this->yui3loader->filter == YUI_DEBUG) ? 'debug' : '';
147 $this->M_yui_loader->insertBefore = 'firstthemesheet';
148 $this->M_yui_loader->modules = array();
c8870e68 149 $this->add_yui2_modules(); // adds loading info for YUI2
4c508047 150 $this->js_module($this->find_module('core_filepicker'));
e3f4e3ca
PS
151
152 // YUI3 init code
153 $libs = array('cssreset', 'cssbase', 'cssfonts', 'cssgrids', 'node', 'loader'); // full CSS reset + basic libs
154 foreach ($libs as $lib) {
155 $this->yui3loader->load($lib);
156 }
c8870e68
PS
157 }
158
159 /**
160 * This method adds yui2 modules into the yui3 JS loader-
161 * @return void
162 */
163 protected function add_yui2_modules() {
164 //note: this function is definitely not perfect, because
165 // it adds tons of markup into each page, but it can be
166 // abstracted into separate JS file with proper headers
167 global $CFG;
168
169 $GLOBALS['yui_current'] = array();
170 require($CFG->libdir.'/yui/phploader/lib/meta/config_'.$CFG->yui2version.'.php');
171 $info = $GLOBALS['yui_current'];
172 unset($GLOBALS['yui_current']);
173
174 if (empty($CFG->yuicomboloading)) {
175 $urlbase = $this->yui2loader->base;
176 } else {
177 $urlbase = $this->yui2loader->comboBase.$CFG->yui2version.'/build/';
178 }
179
180 $modules = array();
181 $ignored = array(); // list of CSS modules that are not needed
182 foreach ($info['moduleInfo'] as $name => $module) {
183 if ($module['type'] === 'css') {
184 $ignored[$name] = true;
185 } else {
186 $modules['yui2-'.$name] = $module;
187 }
188 }
189 foreach ($modules as $name=>$module) {
190 $module['fullpath'] = $urlbase.$module['path']; // fix path to point to correct location
191 unset($module['path']);
192 foreach(array('requires', 'optional', 'supersedes') as $fixme) {
193 if (!empty($module[$fixme])) {
194 $fixed = false;
195 foreach ($module[$fixme] as $key=>$dep) {
196 if (isset($ignored[$dep])) {
197 unset($module[$fixme][$key]);
198 $fixed = true;
199 } else {
200 $module[$fixme][$key] = 'yui2-'.$dep;
201 }
202 }
203 if ($fixed) {
204 $module[$fixme] = array_merge($module[$fixme]); // fix keys
205 }
206 }
207 }
e50b4c89 208 $this->M_yui_loader->modules[$name] = $module;
c8870e68 209 }
945f19f7
PS
210 }
211
212 /**
213 * Initialise with the bits of JavaScript that every Moodle page should have.
214 *
215 * @param moodle_page $page
216 * @param core_renderer $output
217 */
227255b8 218 protected function init_requirements_data(moodle_page $page, core_renderer $renderer) {
945f19f7
PS
219 global $CFG;
220
221 // JavaScript should always work with $CFG->httpswwwroot rather than $CFG->wwwroot.
222 // Otherwise, in some situations, users will get warnings about insecure content
223 // on sercure pages from their web browser.
224
9598d578 225 $this->M_cfg = array(
945f19f7
PS
226 'wwwroot' => $CFG->httpswwwroot, // Yes, really. See above.
227 'sesskey' => sesskey(),
227255b8 228 'loadingicon' => $renderer->pix_url('i/loading_small', 'moodle')->out(false),
945f19f7
PS
229 'themerev' => theme_get_revision(),
230 'theme' => $page->theme->name,
945f19f7
PS
231 );
232 if (debugging('', DEBUG_DEVELOPER)) {
9598d578 233 $this->M_cfg['developerdebug'] = true;
945f19f7 234 }
945f19f7 235
945f19f7
PS
236 if (debugging('', DEBUG_DEVELOPER)) {
237 $this->yui2_lib('logger');
238 }
239
7b42e81a 240 // accessibility stuff
945f19f7
PS
241 $this->skip_link_to('maincontent', get_string('tocontent', 'access'));
242
88515081 243 // For now include YUI2, this will be removed before beta.
2574fae6
PS
244 $this->yui2_lib('dom'); // needs to be migrated to YUI3 before we release 2.0
245 $this->yui2_lib('container'); // needs to be migrated to YUI3 before we release 2.0
246 $this->yui2_lib('connection'); // needs to be migrated to YUI3 before we release 2.0
247
945f19f7
PS
248 $this->string_for_js('confirmation', 'admin');
249 $this->string_for_js('cancel', 'moodle');
250 $this->string_for_js('yes', 'moodle');
251 $this->js_function_call('init_help_icons');
4c907141
PS
252 }
253
b2330db6 254 /**
2c144fc3 255 * Ensure that the specified JavaScript file is linked to from this page.
256 *
c66a13b2
PS
257 * NOTE: This function is to be used in rare cases only, please store your JS in module.js file
258 * and use $PAGE->requires->js_init_call() instead.
259 *
2c144fc3 260 * By default the link is put at the end of the page, since this gives best page-load performance.
b2330db6 261 *
262 * Even if a particular script is requested more than once, it will only be linked
263 * to once.
264 *
c66a13b2
PS
265 * @param string|moodle_url $url The path to the .js file, relative to $CFG->dirroot / $CFG->wwwroot.
266 * For example '/mod/mymod/customscripts.js'; use moodle_url for external scripts
2c144fc3 267 * @return required_js The required_js object. This allows you to control when the
88515081 268 * link to the script is output by calling methods like {@link required_js::in_head()}.
b2330db6 269 */
c66a13b2
PS
270 public function js($url) {
271 $url = $this->js_fix_url($url);
272 if (!isset($this->linkedrequirements[$url->out()])) {
c6f5f39a 273 $this->linkedrequirements[$url->out(false)] = new required_js($this, $url->out(false));
b2330db6 274 }
c6f5f39a 275 return $this->linkedrequirements[$url->out(false)];
b2330db6 276 }
277
278 /**
4c907141 279 * Ensure that the specified YUI2 library file, and all its required dependancies,
2c144fc3 280 * are linked to from this page.
281 *
282 * By default the link is put at the end of the page, since this gives best page-load
283 * performance. Optional dependencies are not loaded automatically - if you want
284 * them you will need to load them first with other calls to this method.
b2330db6 285 *
b2330db6 286 * Even if a particular library is requested more than once (perhaps as a dependancy
287 * of other libraries) it will only be linked to once.
288 *
985d1d1d 289 * The library is leaded as soon as possible, if $OUTPUT->header() not used yet it
945f19f7 290 * is put into the page header, otherwise it is loaded in the page footer.
985d1d1d
PS
291 *
292 * @param string|array $libname the name of the YUI2 library you require. For example 'autocomplete'.
293 * @return void
b2330db6 294 */
f44b10ed 295 public function yui2_lib($libname) {
985d1d1d
PS
296 $libnames = (array)$libname;
297 foreach ($libnames as $lib) {
298 $this->yui2loader->load($lib);
b2330db6 299 }
b2330db6 300 }
301
c8870e68 302 /**
2b8c3f8c
PS
303 * Returns the actual url through which a script is served.
304 * @param moodle_url|string $url full moodle url, or shortened path to script
305 * @return moodle_url
c8870e68 306 */
c66a13b2 307 protected function js_fix_url($url) {
c8870e68
PS
308 global $CFG;
309
2b8c3f8c
PS
310 if ($url instanceof moodle_url) {
311 return $url;
c66a13b2
PS
312 } else if (strpos($url, '/') === 0) {
313 if (debugging()) {
314 // check file existence only when in debug mode
315 if (!file_exists($CFG->dirroot . strtok($url, '?'))) {
316 throw new coding_exception('Attept to require a JavaScript file that does not exist.', $url);
317 }
318 }
2b8c3f8c
PS
319 //return new moodle_url($CFG->httpswwwroot.'/lib/javascript.php', array('file'=>$url, 'rev'=>$CFG->jsrev));
320 return new moodle_url($CFG->httpswwwroot.$url);
c66a13b2
PS
321 } else {
322 throw new coding_exception('Invalid JS url, it has to be shortened url starting with / or moodle_url instance.', $url);
c8870e68 323 }
2b8c3f8c 324 }
c8870e68 325
2b8c3f8c
PS
326 /**
327 * Find out if JS modulke present and return details.
328 * @param string $name name of module, ex: core_group, mod_forum
329 * @return array description of module or null if not found
330 */
331 protected function find_module($name) {
332 global $CFG;
333
334 $module = null;
335
336 if (strpos($name, 'core_') === 0) {
337 // must be some core stuff
338 if ($name === 'core_filepicker') {
4c508047 339 $module = array('name'=>$name, 'fullpath'=>'/repository/filepicker.js', 'requires' => array('base', 'node', 'json', 'async-queue', 'io', 'yui2-button', 'yui2-container', 'yui2-layout', 'yui2-menu', 'yui2-treeview'));
2b8c3f8c
PS
340 } else if($name === 'core_comment') {
341 $module = array('name'=>$name, 'fullpath'=>'/comment/comment.js', 'requires' => array('base', 'io', 'node', 'json', 'yui2-animation'));
342 } else if($name === 'core_role') {
343 $module = array('name'=>$name, 'fullpath'=>'/admin/roles/module.js');
76c0123b
PS
344 } else if($name === 'core_completion') {
345 $module = array('name'=>$name, 'fullpath'=>'/course/completion.js');
53fc3e70
SH
346 } else if ($name === 'core_dock') {
347 $module = array('name'=>'core_dock', 'fullpath'=>'/blocks/dock.js', 'requires'=>array('base', 'cookie', 'dom', 'io', 'node', 'event-custom'));
348 } else if ($name === 'core_calendar') {
349 $module = array('name'=>'core_calendar', 'fullpath'=>'/calendar/calendar.js', 'requires'=>array('dom', 'event', 'node', 'yui2-container','event-mouseenter'));
4abd5b9b
PS
350 } else if ($name === 'core_message') {
351 $module = array('name'=>'core_message', 'fullpath'=>'/message/module.js');
352 } else if ($name === 'core_flashdetect') {
353 $module = array('name'=>'core_flashdetect', 'fullpath'=>'/lib/flashdetect/flashdetect.js', 'requires'=>array('io'));
2b8c3f8c
PS
354 }
355 } else {
356 if ($dir = get_component_directory($name, false)) {
357 if (file_exists("$CFG->dirroot/$dir/module.js")) {
76418c14 358 $module = array('name'=>$name, 'fullpath'=>"/$dir/module.js", 'requires' => array());
3b01539c
PS
359 }
360 }
c8870e68 361 }
2b8c3f8c
PS
362 return $module;
363 }
364
365 /**
366 * Append YUI3 module to default YUI3 JS loader.
367 * The structure of module array is described at http://developer.yahoo.com/yui/3/yui/:
368 * @param string|array $module name of module (details are autodetected), or full module specification as array
369 * @return void
370 */
371 public function js_module($module) {
372 global $CFG;
c8870e68 373
3b01539c 374 if (empty($module)) {
2b8c3f8c
PS
375 throw new coding_exception('Missing YUI3 module name or full description.');
376 }
377
378 if (is_string($module)) {
ebfb8f13 379 $module = $this->find_module($module);
2b8c3f8c
PS
380 }
381
382 if (empty($module) or empty($module['name']) or empty($module['fullpath'])) {
3b01539c 383 throw new coding_exception('Missing YUI3 module details.');
e50b4c89
PS
384 }
385
c66a13b2 386 $module['fullpath'] = $this->js_fix_url($module['fullpath'])->out();
2b8c3f8c 387
e50b4c89 388 if ($this->headdone) {
2b8c3f8c 389 $this->extramodules[$module['name']] = $module;
e50b4c89 390 } else {
2b8c3f8c 391 $this->M_yui_loader->modules[$module['name']] = $module;
e50b4c89 392 }
c8870e68
PS
393 }
394
b2330db6 395 /**
2c144fc3 396 * Ensure that the specified CSS file is linked to from this page.
397 *
398 * Because stylesheet links must go in the <head> part of the HTML, you must call
399 * this function before {@link get_head_code()} is called. That normally means before
b2330db6 400 * the call to print_header. If you call it when it is too late, an exception
401 * will be thrown.
402 *
403 * Even if a particular style sheet is requested more than once, it will only
404 * be linked to once.
405 *
c0467479
PS
406 * Please note sue of this feature is strongly discouraged,
407 * it is suitable only for places where CSS is submitted directly by teachers.
408 * (Students must not be allowed to submit any external CSS because it may
409 * contain embedded javascript!). Example of correct use is mod/data.
410 *
411 * @param string $stylesheet The path to the .css file, relative to $CFG->wwwroot.
412 * For example:
413 * $PAGE->requires->css('mod/data/css.php?d='.$data->id);
b2330db6 414 */
c0467479 415 public function css($stylesheet) {
b2330db6 416 global $CFG;
c0467479
PS
417
418 if ($this->headdone) {
419 throw new coding_exception('Cannot require a CSS file after &lt;head> has been printed.', $stylesheet);
9f509643 420 }
b2330db6 421
c0467479
PS
422 if ($stylesheet instanceof moodle_url) {
423 // ok
424 } else if (strpos($stylesheet, '/') === 0) {
425 $stylesheet = new moodle_url($CFG->httpswwwroot.$stylesheet);
b2330db6 426 } else {
c0467479 427 throw new coding_exception('Invalid stylesheet parameter.', $stylesheet);
b2330db6 428 }
c0467479 429
e50b4c89 430 $this->cssurls[$stylesheet->out()] = $stylesheet; // overrides
b2330db6 431 }
432
2212eb7a
PS
433 /**
434 * Add theme stylkesheet to page - do not use from plugin code,
c0467479 435 * this should be called only from the core renderer!
2212eb7a
PS
436 * @param moodle_url $stylesheet
437 * @return void
438 */
c0467479 439 public function css_theme(moodle_url $stylesheet) {
e50b4c89 440 $this->cssthemeurls[] = $stylesheet;
2212eb7a
PS
441 }
442
b2330db6 443 /**
2c144fc3 444 * Ensure that a skip link to a given target is printed at the top of the <body>.
445 *
446 * You must call this function before {@link get_top_of_body_code()}, (if not, an exception
b2330db6 447 * will be thrown). That normally means you must call this before the call to print_header.
448 *
449 * If you ask for a particular skip link to be printed, it is then your responsibility
2c144fc3 450 * to ensure that the appropraite <a name="..."> tag is printed in the body of the
b2330db6 451 * page, so that the skip link goes somewhere.
452 *
453 * Even if a particular skip link is requested more than once, only one copy of it will be output.
454 *
455 * @param $target the name of anchor this link should go to. For example 'maincontent'.
456 * @param $linktext The text to use for the skip link. Normally get_string('skipto', 'access', ...);
457 */
458 public function skip_link_to($target, $linktext) {
f5de8ee7
PS
459 if ($this->topofbodydone) {
460 debugging('Page header already printed, can not add skip links any more, code needs to be fixed.');
461 return;
b2330db6 462 }
f5de8ee7 463 $this->skiplinks[$target] = $linktext;
b2330db6 464 }
465
466 /**
467 * Ensure that the specified JavaScript function is called from an inline script
2c144fc3 468 * somewhere on this page.
469 *
470 * By default the call will be put in a script tag at the
08fac734
PS
471 * end of the page after initialising Y instance, since this gives best page-load
472 * performance and allows you to use YUI3 library.
b2330db6 473 *
474 * If you request that a particular function is called several times, then
475 * that is what will happen (unlike linking to a CSS or JS file, where only
476 * one link will be output).
477 *
08fac734
PS
478 * The main benefit of the mehtod is the automatic encoding of all function parameters.
479 *
b2330db6 480 * @param string $function the name of the JavaScritp function to call. Can
08fac734 481 * be a compound name like 'Y.Event.purgeElement'. Can also be
cf615522 482 * used to create and object by using a 'function name' like 'new user_selector'.
b2330db6 483 * @param array $arguments and array of arguments to be passed to the function.
484 * When generating the function call, this will be escaped using json_encode,
485 * so passing objects and arrays should work.
2c144fc3 486 * @return required_js_function_call The required_js_function_call object.
b2330db6 487 * This allows you to control when the link to the script is output by
2c144fc3 488 * calling methods like {@link required_js_function_call::in_head()},
2c144fc3 489 * {@link required_js_function_call::on_dom_ready()} or
490 * {@link required_js_function_call::after_delay()} methods.
b2330db6 491 */
9786fbfa 492 public function js_function_call($function, array $arguments = null) {
3b01539c 493 $requirement = new required_js_function_call($this, $function, $arguments);
b2330db6 494 $this->requiredjscode[] = $requirement;
495 return $requirement;
496 }
497
3b01539c
PS
498 /**
499 * Ensure that the specified JavaScript function is called from an inline script
500 * from page footer.
501 *
502 * @param string $function the name of the JavaScritp function to with init code,
503 * usually something like 'M.mod_mymodule.init'
504 * @param array $extraarguments and array of arguments to be passed to the function.
505 * The first argument is always the YUI3 Y instance with all required dependencies
506 * already loaded.
3b01539c 507 * @param bool $ondomready wait for dom ready (helps with some IE problems when modifying DOM)
4c508047 508 * @param array $module JS module specification array
3b01539c
PS
509 * @return void
510 */
4c508047 511 public function js_init_call($function, array $extraarguments = null, $ondomready = false, array $module = null) {
3b01539c 512 $jscode = js_writer::function_call_with_Y($function, $extraarguments);
4c508047
PS
513 if (!$module) {
514 // detect module automatically
515 if (preg_match('/M\.([a-z0-9]+_[^\.]+)/', $function, $matches)) {
516 $module = $this->find_module($matches[1]);
517 }
2b8c3f8c
PS
518 }
519
520 $this->js_init_code($jscode, $ondomready, $module);
3b01539c
PS
521 }
522
08fac734
PS
523 /**
524 * Add short static javascript code fragment to page footer.
525 * This is intended primarily for loading of js modules and initialising page layout.
526 * Ideally the JS code fragment should be stored in plugin renderer so that themes
527 * may override it.
08fac734 528 * @param string $jscode
3b01539c 529 * @param bool $ondomready wait for dom ready (helps with some IE problems when modifying DOM)
2b8c3f8c 530 * @param array $module JS module specification array
08fac734
PS
531 * @return void
532 */
2b8c3f8c 533 public function js_init_code($jscode, $ondomready = false, array $module = null) {
3b01539c
PS
534 $jscode = trim($jscode, " ;\n"). ';';
535
536 if ($module) {
2b8c3f8c
PS
537 $this->js_module($module);
538 $modulename = $module['name'];
3b01539c
PS
539 $jscode = "Y.use('$modulename', function(Y) { $jscode });";
540 }
541
542 if ($ondomready) {
543 $jscode = "Y.on('domready', function() { $jscode });";
544 }
545
546 $this->jsinitcode[] = $jscode;
08fac734
PS
547 }
548
1ce15fda
SH
549 /**
550 * Adds a required JavaScript object initialisation to the page.
e50b4c89 551 *
1ce15fda
SH
552 * @param string|null $var If null the object is not assigned to any variable
553 * @param string $class
554 * @param array $arguments
555 * @param array $requirements
556 * @return required_js_object_init
557 */
558 public function js_object_init($var, $class, array $arguments = null, array $requirements = null) {
559 $requirement = new required_js_object_init($this, $var, $class, $arguments, $requirements);
560 $this->requiredjscode[] = $requirement;
561 return $requirement;
562 }
563
b2330db6 564 /**
2c144fc3 565 * Make a language string available to JavaScript.
117bd748 566 *
2c144fc3 567 * All the strings will be available in a mstr object in the global namespace.
568 * So, for example, after a call to $PAGE->requires->string_for_js('course', 'moodle');
b2330db6 569 * then the JavaScript variable mstr.moodle.course will be 'Course', or the
570 * equivalent in the current language.
571 *
572 * The arguments to this function are just like the arguments to get_string
ef502357 573 * except that $component is not optional, and there are limitations on how you
b2330db6 574 * use $a. Because each string is only stored once in the JavaScript (based
575 * on $identifier and $module) you cannot get the same string with two different
576 * values of $a. If you try, an exception will be thrown.
577 *
578 * If you do need the same string expanded with different $a values, then
579 * the solution is to put them in your own data structure (e.g. and array)
2c144fc3 580 * that you pass to JavaScript with {@link data_for_js()}.
b2330db6 581 *
582 * @param string $identifier the desired string.
583 * @param string $module the language file to look in.
584 * @param mixed $a any extra data to add into the string (optional).
585 */
ef502357
PS
586 public function string_for_js($identifier, $component, $a = NULL) {
587 $string = get_string($identifier, $component, $a);
588 if (!$component) {
2c144fc3 589 throw new coding_exception('The $module parameter is required for page_requirements_manager::string_for_js.');
b2330db6 590 }
ef502357 591 if (isset($this->stringsforjs[$component][$identifier]) && $this->stringsforjs[$component][$identifier] != $string) {
b2330db6 592 throw new coding_exception("Attempt to re-define already required string '$identifier' " .
ef502357 593 "from lang file '$component'. Did you already ask for it with a different \$a?");
b2330db6 594 }
ef502357 595 $this->stringsforjs[$component][$identifier] = $string;
b2330db6 596 }
597
747b85cb 598 /**
599 * Make an array of language strings available for JS
600 *
601 * This function calls the above function {@link string_for_js()} for each requested
602 * string in the $identifiers array that is passed to the argument for a single module
603 * passed in $module.
604 *
605 * <code>
606 * $PAGE->strings_for_js(Array('one', 'two', 'three'), 'mymod', Array('a', null, 3));
607 *
608 * // The above is identifical to calling
609 *
610 * $PAGE->string_for_js('one', 'mymod', 'a');
611 * $PAGE->string_for_js('two', 'mymod');
612 * $PAGE->string_for_js('three', 'mymod', 3);
613 * </code>
614 *
615 * @param array $identifiers An array of desired strings
ef502357 616 * @param string $component The module to load for
747b85cb 617 * @param mixed $a This can either be a single variable that gets passed as extra
618 * information for every string or it can be an array of mixed data where the
619 * key for the data matches that of the identifier it is meant for.
620 *
621 */
ef502357 622 public function strings_for_js($identifiers, $component, $a=NULL) {
747b85cb 623 foreach ($identifiers as $key => $identifier) {
624 if (is_array($a) && array_key_exists($key, $a)) {
625 $extra = $a[$key];
626 } else {
627 $extra = $a;
628 }
ef502357 629 $this->string_for_js($identifier, $component, $extra);
747b85cb 630 }
631 }
632
b2330db6 633 /**
5a197e91
PS
634 * !!!!!!DEPRECATED!!!!!! please use js_init_call() for everything now.
635 *
2c144fc3 636 * Make some data from PHP available to JavaScript code.
117bd748 637 *
2c144fc3 638 * For example, if you call
639 * <pre>
b2330db6 640 * $PAGE->requires->data_for_js('mydata', array('name' => 'Moodle'));
2c144fc3 641 * </pre>
b2330db6 642 * then in JavsScript mydata.name will be 'Moodle'.
643 *
644 * You cannot call this function more than once with the same variable name
645 * (if you try, it will throw an exception). Your code should prepare all the
646 * date you want, and then pass it to this method. There is no way to change
647 * the value associated with a particular variable later.
648 *
649 * @param string $variable the the name of the JavaScript variable to assign the data to.
650 * Will probably work if you use a compound name like 'mybuttons.button[1]', but this
651 * should be considered an experimental feature.
652 * @param mixed $data The data to pass to JavaScript. This will be escaped using json_encode,
653 * so passing objects and arrays should work.
2c144fc3 654 * @return required_data_for_js The required_data_for_js object.
b2330db6 655 * This allows you to control when the link to the script is output by
88515081 656 * calling methods like {@link required_data_for_js::in_head()} or
b2330db6 657 */
658 public function data_for_js($variable, $data) {
659 if (isset($this->variablesinitialised[$variable])) {
660 throw new coding_exception("A variable called '" . $variable .
661 "' has already been passed ot JavaScript. You cannot overwrite it.");
662 }
663 $requirement = new required_data_for_js($this, $variable, $data);
664 $this->requiredjscode[] = $requirement;
665 $this->variablesinitialised[$variable] = 1;
666 return $requirement;
667 }
117bd748 668
647df7e3 669 /**
670 * Creates a YUI event handler.
671 *
d96d8f03 672 * @param mixed $selector standard YUI selector for elemnts, may be array or string, element id is in the form "#idvalue"
647df7e3 673 * @param string $event A valid DOM event (click, mousedown, change etc.)
674 * @param string $function The name of the function to call
675 * @param array $arguments An optional array of argument parameters to pass to the function
227255b8 676 * @return void
647df7e3 677 */
d96d8f03 678 public function event_handler($selector, $event, $function, array $arguments = null) {
227255b8
PS
679 $this->eventhandlers[] = array('selector'=>$selector, 'event'=>$event, 'function'=>$function, 'arguments'=>$arguments);
680 }
681
682 /**
683 * Returns code needed for registering of event handlers.
684 * @return string JS code
685 */
686 protected function get_event_handler_code() {
687 $output = '';
688 foreach ($this->eventhandlers as $h) {
689 $output .= js_writer::event_handler($h['selector'], $h['event'], $h['function'], $h['arguments']);
690 }
691 return $output;
647df7e3 692 }
b2330db6 693
694 /**
695 * Get the code for the linked resources that need to appear in a particular place.
696 * @param $when one of the WHEN_... constants.
697 * @return string the HTML that should be output in that place.
698 */
699 protected function get_linked_resources_code($when) {
700 $output = '';
9e43d2b3 701 foreach ($this->linkedrequirements as $requirement) {
b2330db6 702 if (!$requirement->is_done() && $requirement->get_when() == $when) {
703 $output .= $requirement->get_html();
704 $requirement->mark_done();
705 }
706 }
707 return $output;
708 }
709
710 /**
2c144fc3 711 * Get the inline JavaScript code that need to appear in a particular place.
b2330db6 712 * @param $when one of the WHEN_... constants.
713 * @return string the javascript that should be output in that place.
714 */
715 protected function get_javascript_code($when, $indent = '') {
716 $output = '';
717 foreach ($this->requiredjscode as $requirement) {
718 if (!$requirement->is_done() && $requirement->get_when() == $when) {
719 $output .= $indent . $requirement->get_js_code();
720 $requirement->mark_done();
721 }
722 }
723 return $output;
724 }
725
08fac734
PS
726 /**
727 * Returns js code to be executed when Y is available.
728 * @return unknown_type
729 */
730 protected function get_javascript_init_code() {
1ce15fda 731 if (count($this->jsinitcode)) {
3b01539c 732 return implode("\n", $this->jsinitcode) . "\n";
1ce15fda
SH
733 }
734 return '';
08fac734
PS
735 }
736
4c907141
PS
737 /**
738 * Returns basic YUI3 JS loading code.
985d1d1d 739 * YUI3 is using autoloading of both CSS and JS code.
4c907141 740 *
c724f835
PS
741 * Major benefit of this compared to standard js/csss loader is much improved
742 * caching, better browser cache utilisation, much fewer http requests.
743 *
4c907141
PS
744 * @return string
745 */
985d1d1d 746 protected function get_yui3lib_headcode() {
636365f8
PS
747 $code = $this->yui3loader->css() . $this->yui3loader->script();
748 // unfortunately yui loader does not produce xhtml strict code, so let's fix it for now
749 $code = str_replace('&amp;', '&', $code);
750 $code = str_replace('&', '&amp;', $code);
751 return $code;
985d1d1d
PS
752 }
753
754 /**
755 * Returns basic YUI2 JS loading code.
756 * It can be called manually at any time.
c724f835
PS
757 * If called manually the result needs to be output using echo().
758 *
759 * Major benefit of this compared to standard js loader is much improved
760 * caching, better browser cache utilisation, much fewer http requests.
761 *
762 * All YUI2 CSS is loaded automatically.
985d1d1d
PS
763 *
764 * @return string JS embedding code
765 */
766 public function get_yui2lib_code() {
c724f835
PS
767 global $CFG;
768
985d1d1d 769 if ($this->headdone) {
636365f8 770 $code = $this->yui2loader->script_embed();
985d1d1d 771 } else {
636365f8 772 $code = $this->yui2loader->script();
c724f835
PS
773 if ($this->yui2loader->combine) {
774 $skinurl = $this->yui2loader->comboBase . $CFG->yui2version . '/build/assets/skins/sam/skin.css';
775 } else {
776 $skinurl = $this->yui2loader->base . 'assets/skins/sam/skin.css';
777 }
778 // please note this is a temporary hack until we fully migrate to later YUI3 that has all the widgets
c0467479
PS
779 $attributes = array('rel'=>'stylesheet', 'type'=>'text/css', 'href'=>$skinurl);
780 $code .= "\n" . html_writer::empty_tag('link', $attributes) . "\n";
985d1d1d 781 }
636365f8
PS
782 $code = str_replace('&amp;', '&', $code);
783 $code = str_replace('&', '&amp;', $code);
784 return $code;
4c907141
PS
785 }
786
2212eb7a
PS
787 /**
788 * Returns html tags needed for inclusion of theme CSS
789 * @return string
790 */
c0467479
PS
791 protected function get_css_code() {
792 // First of all the theme CSS, then any custom CSS
793 // Please note custom CSS is strongly discouraged,
794 // because it can not be overridden by themes!
795 // It is suitable only for things like mod/data which accepts CSS from teachers.
796
2212eb7a 797 $code = '';
c0467479
PS
798 $attributes = array('id'=>'firstthemesheet', 'rel'=>'stylesheet', 'type'=>'text/css');
799
e50b4c89 800 $urls = $this->cssthemeurls + $this->cssurls;
c0467479
PS
801
802 foreach ($urls as $url) {
803 $attributes['href'] = $url;
804 $code .= html_writer::empty_tag('link', $attributes) . "\n";
805 // this id is needed in first sheet only so that theme may override YUI sheets laoded on the fly
806 unset($attributes['id']);
2212eb7a 807 }
c0467479 808
2212eb7a
PS
809 return $code;
810 }
811
e50b4c89
PS
812 /**
813 * Adds extra modules specified after printing of page header
814 */
815 protected function get_extra_modules_code() {
816 if (empty($this->extramodules)) {
817 return '';
818 }
7b42e81a 819 return html_writer::script(js_writer::function_call('M.yui.add_module', array($this->extramodules)));
e50b4c89
PS
820 }
821
b2330db6 822 /**
2c144fc3 823 * Generate any HTML that needs to go inside the <head> tag.
824 *
825 * Normally, this method is called automatically by the code that prints the
826 * <head> tag. You should not normally need to call it in your own code.
b2330db6 827 *
2c144fc3 828 * @return string the HTML code to to inside the <head> tag.
b2330db6 829 */
227255b8 830 public function get_head_code(moodle_page $page, core_renderer $renderer) {
e50b4c89
PS
831 global $CFG;
832
945f19f7
PS
833 // note: the $page and $output are not stored here because it would
834 // create circular references in memory which prevents garbage collection
227255b8 835 $this->init_requirements_data($page, $renderer);
77774f6a 836
c0467479 837 // yui3 JS and CSS is always loaded first - it is cached in browser
985d1d1d 838 $output = $this->get_yui3lib_headcode();
77774f6a
PS
839
840 // BC: load basic YUI2 for now, all yui2 things should be loaded via Y.use('yui2-oldmodulename')
985d1d1d 841 $output .= $this->get_yui2lib_code();
77774f6a
PS
842
843 // now theme CSS + custom CSS in this specific order
c0467479 844 $output .= $this->get_css_code();
77774f6a 845
e50b4c89
PS
846 // set up global YUI3 loader object - this should contain all code needed by plugins
847 // note: in JavaScript just use "YUI(M.yui.loader).use('overlay', function(Y) { .... });"
848 // this needs to be done before including any other script
9598d578
PS
849 $js = "var M = {}; M.yui = {}; ";
850 $js .= js_writer::set_variable('M.yui.loader', $this->M_yui_loader, false) . "\n";
851 $js .= js_writer::set_variable('M.cfg', $this->M_cfg, false);
e50b4c89
PS
852 $output .= html_writer::script($js);
853
854 // link our main JS file, all core stuff should be there
855 $output .= html_writer::script('', $CFG->httpswwwroot.'/lib/javascript-static.js');
856
77774f6a
PS
857 // all the other linked things from HEAD - there should be as few as possible
858 // because we need to minimise number of http requests,
4c907141 859 $output .= $this->get_linked_resources_code(self::WHEN_IN_HEAD);
77774f6a 860
d96d8f03 861 // finally all JS that should go directly into head tag - mostly global config
77774f6a
PS
862 $output .= html_writer::script($this->get_javascript_code(self::WHEN_IN_HEAD));
863
864 // mark head sending done, it is not possible to anything there
b2330db6 865 $this->headdone = true;
77774f6a 866
b2330db6 867 return $output;
868 }
869
870 /**
2c144fc3 871 * Generate any HTML that needs to go at the start of the <body> tag.
872 *
873 * Normally, this method is called automatically by the code that prints the
874 * <head> tag. You should not normally need to call it in your own code.
b2330db6 875 *
2c144fc3 876 * @return string the HTML code to go at the start of the <body> tag.
b2330db6 877 */
878 public function get_top_of_body_code() {
f5de8ee7
PS
879 // first the skip links
880 $links = '';
881 $attributes = array('class'=>'skip');
882 foreach ($this->skiplinks as $url => $text) {
883 $attributes['href'] = '#' . $url;
884 $links .= html_writer::tag('a', $attributes, $text);
885 }
886 $output = html_writer::tag('div', array('class'=>'skiplinks'), $links) . "\n";
887
888 // then the clever trick for hiding of things not needed when JS works
889 $output .= html_writer::script("document.body.className += ' jsenabled';") . "\n";
b2330db6 890 $this->topofbodydone = true;
891 return $output;
892 }
893
894 /**
895 * Generate any HTML that needs to go at the end of the page.
896 *
2c144fc3 897 * Normally, this method is called automatically by the code that prints the
898 * page footer. You should not normally need to call it in your own code.
899 *
b2330db6 900 * @return string the HTML code to to at the end of the page.
901 */
902 public function get_end_code() {
9d897331 903 global $CFG;
e50b4c89
PS
904 // add other requested modules
905 $output = $this->get_extra_modules_code();
c8870e68 906
e50b4c89
PS
907 // add missing YUI2 YUI - to be removed once we convert everything to YUI3!
908 $output .= $this->get_yui2lib_code();
c8870e68
PS
909
910 // now print all the stuff that was added through ->requires
985d1d1d 911 $output .= $this->get_linked_resources_code(self::WHEN_AT_END);
b2330db6 912
cf615522 913 if (!empty($this->stringsforjs)) {
914 array_unshift($this->requiredjscode, new required_data_for_js($this, 'mstr', $this->stringsforjs));
915 }
916
b2330db6 917 $js = $this->get_javascript_code(self::WHEN_AT_END);
918
d96d8f03
PS
919 $inyuijs = $this->get_javascript_code(self::WHEN_IN_YUI, ' ');
920 $ondomreadyjs = $this->get_javascript_code(self::WHEN_ON_DOM_READY, ' ');
08fac734 921 $jsinit = $this->get_javascript_init_code();
227255b8 922 $handlersjs = $this->get_event_handler_code();
1ce15fda
SH
923
924 if (!empty($ondomreadyjs)) {
925 $ondomreadyjs = " Y.on('domready', function() {\n$ondomreadyjs\n });";
926 }
927
227255b8
PS
928 // the global Y can be used only after it is fully loaded, that means
929 // from code executed from the following block
2169e664 930 $js .= "YUI(M.yui.loader).use('node', function(Y) {\n{$inyuijs}{$ondomreadyjs}{$jsinit}{$handlersjs}\n});";
b2330db6 931
77774f6a 932 $output .= html_writer::script($js);
b2330db6 933
934 return $output;
935 }
936
937 /**
2c144fc3 938 * @return boolean Have we already output the code in the <head> tag?
b2330db6 939 */
940 public function is_head_done() {
941 return $this->headdone;
942 }
943
944 /**
2c144fc3 945 * @return boolean Have we already output the code at the start of the <body> tag?
b2330db6 946 */
947 public function is_top_of_body_done() {
948 return $this->topofbodydone;
949 }
950}
951
952
953/**
954 * This is the base class for all sorts of requirements. just to factor out some
955 * common code.
956 *
957 * @copyright 2009 Tim Hunt
958 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5c5418fe 959 * @since Moodle 2.0
b2330db6 960 */
961abstract class requirement_base {
962 protected $manager;
963 protected $when;
964 protected $done = false;
965
966 /**
967 * Constructor. Normally the class and its subclasses should not be created
968 * directly. Client code should create them via a page_requirements_manager
969 * method like ->js(...).
970 *
971 * @param page_requirements_manager $manager the page_requirements_manager we are associated with.
972 */
973 protected function __construct(page_requirements_manager $manager) {
974 $this->manager = $manager;
975 }
976
977 /**
978 * Mark that this requirement has been satisfied (that is, that the HTML
2c144fc3 979 * returned by {@link get_html()} has been output.
b2330db6 980 * @return boolean has this requirement been satisfied yet? That is, has
2c144fc3 981 * that the HTML returned by {@link get_html()} has been output already.
b2330db6 982 */
983 public function is_done() {
984 return $this->done;
985 }
986
987 /**
988 * Mark that this requirement has been satisfied (that is, that the HTML
2c144fc3 989 * returned by {@link get_html()} has been output.
b2330db6 990 */
991 public function mark_done() {
992 $this->done = true;
993 }
994
995 /**
996 * Where on the page the HTML this requirement is meant to go.
2c144fc3 997 * @return integer One of the {@link page_requirements_manager}::WHEN_... constants.
b2330db6 998 */
999 public function get_when() {
1000 return $this->when;
1001 }
1002}
1003
1004/**
1005 * This class represents something that must be output somewhere in the HTML.
1006 *
1007 * Examples include links to JavaScript or CSS files. However, it should not
1008 * necessarily be output immediately, we may have to wait for an appropriate time.
1009 *
1010 * @copyright 2009 Tim Hunt
1011 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5c5418fe 1012 * @since Moodle 2.0
b2330db6 1013 */
1014abstract class linked_requirement extends requirement_base {
1015 protected $url;
1016
1017 /**
1018 * Constructor. Normally the class and its subclasses should not be created
1019 * directly. Client code should create them via a page_requirements_manager
1020 * method like ->js(...).
1021 *
1022 * @param page_requirements_manager $manager the page_requirements_manager we are associated with.
1023 * @param string $url The URL of the thing we are linking to.
1024 */
1025 protected function __construct(page_requirements_manager $manager, $url) {
1026 parent::__construct($manager);
1027 $this->url = $url;
1028 }
1029
1030 /**
1031 * @return string the HTML needed to satisfy this requirement.
1032 */
1033 abstract public function get_html();
1034}
1035
1036
1037/**
2c144fc3 1038 * A subclass of {@link linked_requirement} to represent a requried JavaScript file.
b2330db6 1039 *
1040 * You should not create instances of this class directly. Instead you should
2c144fc3 1041 * work with a {@link page_requirements_manager} - and probably the only
b2330db6 1042 * page_requirements_manager you will ever need is the one at $PAGE->requires.
1043 *
88515081 1044 * The methods {@link in_head()}
2c144fc3 1045 * are indented to be used as a fluid API, so you can say things like
9dec75db 1046 * $PAGE->requires->js('/mod/mymod/script.js')->in_head();
b2330db6 1047 *
1048 * However, by default JavaScript files are included at the end of the HTML.
1049 * This is recommended practice because it means that the web browser will only
1050 * start loading the javascript files after the rest of the page is loaded, and
1051 * that gives the best performance for users.
1052 *
1053 * @copyright 2009 Tim Hunt
1054 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5c5418fe 1055 * @since Moodle 2.0
b2330db6 1056 */
1057class required_js extends linked_requirement {
1058 /**
1059 * Constructor. Normally instances of this class should not be created
1060 * directly. Client code should create them via the page_requirements_manager
2c144fc3 1061 * method {@link page_requirements_manager::js()}.
b2330db6 1062 *
1063 * @param page_requirements_manager $manager the page_requirements_manager we are associated with.
1064 * @param string $url The URL of the JavaScript file we are linking to.
1065 */
1066 public function __construct(page_requirements_manager $manager, $url) {
1067 parent::__construct($manager, $url);
1068 $this->when = page_requirements_manager::WHEN_AT_END;
1069 }
1070
1071 public function get_html() {
e50b4c89 1072 return html_writer::script('', $this->url);
b2330db6 1073 }
1074
b2330db6 1075 /**
1076 * Indicate that the link to this JavaScript file should be output in the
2c144fc3 1077 * <head> section of the HTML. If it too late for this request to be
b2330db6 1078 * satisfied, an exception is thrown.
1079 */
1080 public function in_head() {
1081 if ($this->is_done() || $this->when <= page_requirements_manager::WHEN_IN_HEAD) {
1082 return;
1083 }
1084 if ($this->manager->is_head_done()) {
1085 throw new coding_exception('Too late to ask for a JavaScript file to be linked to from &lt;head>.');
1086 }
1087 $this->when = page_requirements_manager::WHEN_IN_HEAD;
1088 }
b2330db6 1089}
1090
1091
1092/**
1093 * This is the base class for requirements that are JavaScript code.
1094 *
1095 * @copyright 2009 Tim Hunt
1096 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5c5418fe 1097 * @since Moodle 2.0
b2330db6 1098 */
1099abstract class required_js_code extends requirement_base {
1100
1101 /**
2c144fc3 1102 * Constructor.
b2330db6 1103 * @param page_requirements_manager $manager the page_requirements_manager we are associated with.
1104 */
1105 protected function __construct(page_requirements_manager $manager) {
1106 parent::__construct($manager);
1107 $this->when = page_requirements_manager::WHEN_AT_END;
1108 }
1109
1110 /**
1111 * @return string the JavaScript code needed to satisfy this requirement.
1112 */
1113 abstract public function get_js_code();
1114
b2330db6 1115 /**
1116 * Indicate that the link to this JavaScript file should be output in the
2c144fc3 1117 * <head> section of the HTML. If it too late for this request to be
b2330db6 1118 * satisfied, an exception is thrown.
1119 */
1120 public function in_head() {
1121 if ($this->is_done() || $this->when <= page_requirements_manager::WHEN_IN_HEAD) {
1122 return;
1123 }
1124 if ($this->manager->is_head_done()) {
1125 throw new coding_exception('Too late to ask for some JavaScript code to be output in &lt;head>.');
1126 }
1127 $this->when = page_requirements_manager::WHEN_IN_HEAD;
1128 }
b2330db6 1129}
1130
1ce15fda
SH
1131/**
1132 * This class is used to manage an object initialisation in JavaScript.
1133 *
1134 * @copyright 2010 Sam Hemelryk
1135 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1136 * @since Moodle 2.0
1137 */
1138class required_js_object_init extends required_js_code {
1139 /**
1140 * The variable to assign the object to or null for none
1141 * @var string|null
1142 */
1143 protected $var;
1144 /**
1145 * The class of the object to initialise
1146 * @var string
1147 */
1148 protected $class;
1149 /**
1150 * Arguments to pass in to the constructor
1151 */
1152 protected $arguments;
1153 /**
1154 * Required YUI modules
1155 */
1156 protected $requirements;
1157 protected $delay = 0;
1158
1159 /**
1160 * Constructor. Normally instances of this class should not be created directly.
1161 * Client code should create them via the page_requirements_manager
1162 * method {@link page_requirements_manager::js_object_init()}.
1163 *
1164 * @param page_requirements_manager $manager the page_requirements_manager we are associated with.
1165 * @param string|null $var
1166 * @param string $class
1167 * @param array|null $arguments
1168 * @param array|null $requirements
1169 */
1170 public function __construct(page_requirements_manager $manager, $var, $class, array $arguments = null, array $requirements = null) {
1171 parent::__construct($manager);
1172 $this->when = page_requirements_manager::WHEN_IN_YUI;
1173 $this->var = $var;
1174 $this->class = $class;
1175 $this->arguments = $arguments;
1176 $this->requirements = $requirements;
1177 }
1178
1179 /**
1180 * Gets the actual JavaScript code for the required object initialisation
1181 * @return string
1182 */
1183 public function get_js_code() {
1184 return js_writer::object_init($this->var, $this->class, $this->arguments, $this->requirements, $this->delay);
1185 }
1186
1187 /**
1188 * Indicate that this initalisation should be called in YUI's onDomReady event.
1189 *
1190 * Thisis needed mostly for buggy IE browsers because they have problems
1191 * when JS starts modifying DOM structure before the DOM is ready.
1192 */
1193 public function on_dom_ready() {
1194 if ($this->is_done() || $this->when < page_requirements_manager::WHEN_IN_YUI) {
1195 return;
1196 }
1197 $this->when = page_requirements_manager::WHEN_ON_DOM_READY;
1198 }
1199
1200 /**
1201 * Indicate that this function should be called a certain number of seconds
1202 * after the page has finished loading. (More exactly, a number of seconds
1203 * after the onDomReady event fires.)
1204 *
1205 * @param integer $seconds the number of seconds delay.
1206 */
1207 public function after_delay($seconds) {
1208 if ($seconds) {
1209 $this->on_dom_ready();
1210 }
1211 $this->delay = $seconds;
1212 }
1213}
b2330db6 1214
1215/**
1216 * This class represents a JavaScript function that must be called from the HTML
d96d8f03 1217 * page. By default the call will be made at the end of the page when YUI initialised,
88515081 1218 * but you can chage that using the {@link in_head()}, etc. methods.
b2330db6 1219 *
1220 * @copyright 2009 Tim Hunt
1221 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5c5418fe 1222 * @since Moodle 2.0
b2330db6 1223 */
1224class required_js_function_call extends required_js_code {
1225 protected $function;
1226 protected $arguments;
cf615522 1227 protected $delay = 0;
b2330db6 1228
1229 /**
2c144fc3 1230 * Constructor. Normally instances of this class should not be created directly.
1231 * Client code should create them via the page_requirements_manager
1232 * method {@link page_requirements_manager::js_function_call()}.
b2330db6 1233 *
1234 * @param page_requirements_manager $manager the page_requirements_manager we are associated with.
1235 * @param string $function the name of the JavaScritp function to call.
08fac734 1236 * Can be a compound name like 'Y.Event.purgeElement'. Do not use old YUI2 YAHOO. function names.
b2330db6 1237 * @param array $arguments and array of arguments to be passed to the function.
1238 * When generating the function call, this will be escaped using json_encode,
1239 * so passing objects and arrays should work.
1240 */
3b01539c 1241 public function __construct(page_requirements_manager $manager, $function, $arguments) {
b2330db6 1242 parent::__construct($manager);
d96d8f03 1243 $this->when = page_requirements_manager::WHEN_IN_YUI;
b2330db6 1244 $this->function = $function;
1245 $this->arguments = $arguments;
1246 }
1247
1248 public function get_js_code() {
3b01539c 1249 return js_writer::function_call($this->function, $this->arguments, $this->delay);
b2330db6 1250 }
1251
1252 /**
1253 * Indicate that this function should be called in YUI's onDomReady event.
1254 *
d96d8f03
PS
1255 * Thisis needed mostly for buggy IE browsers because they have problems
1256 * when JS starts modifying DOM structure before the DOM is ready.
b2330db6 1257 */
1258 public function on_dom_ready() {
d96d8f03 1259 if ($this->is_done() || $this->when < page_requirements_manager::WHEN_IN_YUI) {
b2330db6 1260 return;
1261 }
b2330db6 1262 $this->when = page_requirements_manager::WHEN_ON_DOM_READY;
1263 }
cf615522 1264
1265 /**
1266 * Indicate that this function should be called a certain number of seconds
1267 * after the page has finished loading. (More exactly, a number of seconds
1268 * after the onDomReady event fires.)
1269 *
1270 * @param integer $seconds the number of seconds delay.
1271 */
1272 public function after_delay($seconds) {
1273 if ($seconds) {
1274 $this->on_dom_ready();
1275 }
1276 $this->delay = $seconds;
1277 }
b2330db6 1278}
1279
1280
1281/**
1282 * This class represents some data from PHP that needs to be made available in a
1283 * global JavaScript variable. By default the data will be output at the end of
88515081 1284 * the page, but you can chage that using the {@link in_head()}, etc. methods.
b2330db6 1285 *
1286 * @copyright 2009 Tim Hunt
1287 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5c5418fe 1288 * @since Moodle 2.0
b2330db6 1289 */
1290class required_data_for_js extends required_js_code {
1291 protected $variable;
1292 protected $data;
1293
1294 /**
2c144fc3 1295 * Constructor. Normally the class and its subclasses should not be created directly.
1296 * Client code should create them via the page_requirements_manager
1297 * method {@link page_requirements_manager::data_for_js()}.
b2330db6 1298 *
1299 * @param page_requirements_manager $manager the page_requirements_manager we are associated with.
1300 * @param string $variable the the name of the JavaScript variable to assign the data to.
1301 * Will probably work if you use a compound name like 'mybuttons.button[1]', but this
1302 * should be considered an experimental feature.
1303 * @param mixed $data The data to pass to JavaScript. This will be escaped using json_encode,
1304 * so passing objects and arrays should work.
1305 */
1306 public function __construct(page_requirements_manager $manager, $variable, $data) {
1307 parent::__construct($manager);
1308 $this->variable = $variable;
d194928e 1309 $this->data = $data;
b2330db6 1310 // json_encode immediately, so that if $data is an object (and therefore was
1311 // passed in by reference) we get the data at the time the call was made, and
1312 // not whatever the data happened to be when this is output.
1313 }
1314
1315 public function get_js_code() {
227255b8 1316 return js_writer::set_variable($this->variable, $this->data) . "\n";
647df7e3 1317 }
1318}
b2330db6 1319
88c5092a 1320
1321/**
1322 * Returns whether ajax is enabled/allowed or not.
1323 */
c2a9fc91 1324function ajaxenabled($browsers = array()) {
88c5092a 1325
1326 global $CFG, $USER;
483f3067 1327
c2a9fc91 1328 if (!empty($browsers)) {
1329 $valid = false;
1330 foreach ($browsers as $brand => $version) {
1331 if (check_browser_version($brand, $version)) {
1332 $valid = true;
483f3067 1333 }
c2a9fc91 1334 }
483f3067 1335
c2a9fc91 1336 if (!$valid) {
1337 return false;
1338 }
1339 }
483f3067 1340
d499142e 1341 $ie = check_browser_version('MSIE', 6.0);
1342 $ff = check_browser_version('Gecko', 20051106);
1343 $op = check_browser_version('Opera', 9.0);
1344 $sa = check_browser_version('Safari', 412);
1345
1346 if (!$ie && !$ff && !$op && !$sa) {
1347 /** @see http://en.wikipedia.org/wiki/User_agent */
483f3067 1348 // Gecko build 20051107 is what is in Firefox 1.5.
88c5092a 1349 // We still have issues with AJAX in other browsers.
1350 return false;
1351 }
1352
2f11bfc0 1353 if (!empty($CFG->enableajax) && (!empty($USER->ajax) || !isloggedin())) {
88c5092a 1354 return true;
1355 } else {
1356 return false;
1357 }
1358}
9bb74178 1359
35b974da 1360
1361/**
2469f7ea 1362 * Used to create view of document to be passed to JavaScript on pageload.
1363 * We use this class to pass data from PHP to JavaScript.
35b974da 1364 */
4fc45e1d 1365class jsportal {
9bb74178 1366
0a0bb380 1367 var $currentblocksection = null;
9bb74178 1368 var $blocks = array();
0a0bb380 1369
9bb74178 1370
35b974da 1371 /**
1372 * Takes id of block and adds it
1373 */
2469f7ea 1374 function block_add($id, $hidden=false){
0a0bb380 1375 $hidden_binary = 0;
9bb74178 1376
1377 if ($hidden) {
1378 $hidden_binary = 1;
1379 }
35b974da 1380 $this->blocks[count($this->blocks)] = array($this->currentblocksection, $id, $hidden_binary);
0a0bb380 1381 }
9bb74178 1382
1383
2469f7ea 1384 /**
1385 * Prints the JavaScript code needed to set up AJAX for the course.
1386 */
1387 function print_javascript($courseid, $return=false) {
ddc66060 1388 global $CFG, $USER, $OUTPUT, $COURSE;
9bb74178 1389
d4df8fdc 1390 $blocksoutput = $output = '';
35b974da 1391 for ($i=0; $i<count($this->blocks); $i++) {
2469f7ea 1392 $blocksoutput .= "['".$this->blocks[$i][0]."',
1393 '".$this->blocks[$i][1]."',
1394 '".$this->blocks[$i][2]."']";
1395
1396 if ($i != (count($this->blocks) - 1)) {
35b974da 1397 $blocksoutput .= ',';
9bb74178 1398 }
1399 }
32f0b38a 1400 $output .= "<script type=\"text/javascript\">\n";
72d28452 1401 $output .= " main.portal.id = ".$courseid.";\n";
2469f7ea 1402 $output .= " main.portal.blocks = new Array(".$blocksoutput.");\n";
ddc66060 1403 $output .= " main.portal.strings['courseformat']='".$COURSE->format."';\n";
2469f7ea 1404 $output .= " main.portal.strings['wwwroot']='".$CFG->wwwroot."';\n";
f8eaeffa 1405 $output .= " main.portal.strings['marker']='".get_string('markthistopic', '', '_var_')."';\n";
1406 $output .= " main.portal.strings['marked']='".get_string('markedthistopic', '', '_var_')."';\n";
b1b4c2d7 1407 $output .= " main.portal.numsections = ".$COURSE->numsections.";\n";
f8eaeffa 1408 $output .= " main.portal.strings['hide']='".get_string('hide')."';\n";
1409 $output .= " main.portal.strings['hidesection']='".get_string('hidesection', '', '_var_')."';\n";
1410 $output .= " main.portal.strings['show']='".get_string('show')."';\n";
1411 $output .= " main.portal.strings['delete']='".get_string('delete')."';\n";
d2a11d46 1412 $output .= " main.portal.strings['move']='".get_string('move')."';\n";
f8eaeffa 1413 $output .= " main.portal.strings['movesection']='".get_string('movesection', '', '_var_')."';\n";
d2a11d46 1414 $output .= " main.portal.strings['moveleft']='".get_string('moveleft')."';\n";
1415 $output .= " main.portal.strings['moveright']='".get_string('moveright')."';\n";
4fc45e1d 1416 $output .= " main.portal.strings['update']='".get_string('update')."';\n";
d2a11d46 1417 $output .= " main.portal.strings['groupsnone']='".get_string('groupsnone')."';\n";
1418 $output .= " main.portal.strings['groupsseparate']='".get_string('groupsseparate')."';\n";
1419 $output .= " main.portal.strings['groupsvisible']='".get_string('groupsvisible')."';\n";
1420 $output .= " main.portal.strings['clicktochange']='".get_string('clicktochange')."';\n";
2469f7ea 1421 $output .= " main.portal.strings['deletecheck']='".get_string('deletecheck','','_var_')."';\n";
1422 $output .= " main.portal.strings['resource']='".get_string('resource')."';\n";
1423 $output .= " main.portal.strings['activity']='".get_string('activity')."';\n";
d4a1fcaf 1424 $output .= " main.portal.strings['sesskey']='".sesskey()."';\n";
b5d0cafc
PS
1425 $output .= " main.portal.icons['spacerimg']='".$OUTPUT->pix_url('spaces')."';\n";
1426 $output .= " main.portal.icons['marker']='".$OUTPUT->pix_url('i/marker')."';\n";
1427 $output .= " main.portal.icons['ihide']='".$OUTPUT->pix_url('i/hide')."';\n";
1428 $output .= " main.portal.icons['move_2d']='".$OUTPUT->pix_url('i/move_2d')."';\n";
1429 $output .= " main.portal.icons['show']='".$OUTPUT->pix_url('t/show')."';\n";
1430 $output .= " main.portal.icons['hide']='".$OUTPUT->pix_url('t/hide')."';\n";
1431 $output .= " main.portal.icons['delete']='".$OUTPUT->pix_url('t/delete')."';\n";
1432 $output .= " main.portal.icons['groupn']='".$OUTPUT->pix_url('t/groupn')."';\n";
1433 $output .= " main.portal.icons['groups']='".$OUTPUT->pix_url('t/groups')."';\n";
1434 $output .= " main.portal.icons['groupv']='".$OUTPUT->pix_url('t/groupv')."';\n";
ddedf979 1435 if (right_to_left()) {
b5d0cafc
PS
1436 $output .= " main.portal.icons['backwards']='".$OUTPUT->pix_url('t/right')."';\n";
1437 $output .= " main.portal.icons['forwards']='".$OUTPUT->pix_url('t/left')."';\n";
ddedf979 1438 } else {
b5d0cafc
PS
1439 $output .= " main.portal.icons['backwards']='".$OUTPUT->pix_url('t/left')."';\n";
1440 $output .= " main.portal.icons['forwards']='".$OUTPUT->pix_url('t/right')."';\n";
ddedf979 1441 }
1442
2469f7ea 1443 $output .= " onloadobj.load();\n";
4fc45e1d 1444 $output .= " main.process_blocks();\n";
35b974da 1445 $output .= "</script>";
2469f7ea 1446 if ($return) {
1447 return $output;
1448 } else {
1449 echo $output;
1450 }
0a0bb380 1451 }
9bb74178 1452
2469f7ea 1453}