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