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