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