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