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