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