|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00002 00003 // This file is part of Moodle - http://moodle.org/ 00004 // 00005 // Moodle is free software: you can redistribute it and/or modify 00006 // it under the terms of the GNU General Public License as published by 00007 // the Free Software Foundation, either version 3 of the License, or 00008 // (at your option) any later version. 00009 // 00010 // Moodle is distributed in the hope that it will be useful, 00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 // GNU General Public License for more details. 00014 // 00015 // You should have received a copy of the GNU General Public License 00016 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 00017 00018 00028 defined('MOODLE_INTERNAL') || die(); 00029 00030 // note: you can find history of this file in lib/ajax/ajaxlib.php 00031 00059 class page_requirements_manager { 00061 protected $stringsforjs = array(); 00063 protected $jsinitvariables = array('head'=>array(), 'footer'=>array()); 00065 protected $jsincludes = array('head'=>array(), 'footer'=>array()); 00067 protected $jscalls = array('normal'=>array(), 'ondomready'=>array()); 00072 protected $skiplinks = array(); 00077 protected $jsinitcode = array(); 00082 protected $cssthemeurls = array(); 00088 protected $cssurls = array(); 00093 protected $eventhandlers = array(); 00098 protected $extramodules = array(); 00100 protected $headdone = false; 00102 protected $topofbodydone = false; 00103 00105 protected $yui2loader; 00107 protected $yui3loader; 00109 protected $M_yui_loader; 00111 protected $M_cfg; 00113 protected $debug_moduleloadstacktraces = array(); 00114 00118 public function __construct() { 00119 global $CFG; 00120 00121 require_once("$CFG->libdir/yui/phploader/phploader/loader.php"); 00122 00123 $this->yui3loader = new stdClass(); 00124 $this->yui2loader = new YAHOO_util_Loader($CFG->yui2version); 00125 00126 // set up some loader options 00127 if (debugging('', DEBUG_DEVELOPER)) { 00128 $this->yui3loader->filter = YUI_RAW; // for more detailed logging info use YUI_DEBUG here 00129 $this->yui2loader->filter = YUI_RAW; // for more detailed logging info use YUI_DEBUG here 00130 $this->yui2loader->allowRollups = false; 00131 } else { 00132 $this->yui3loader->filter = null; 00133 $this->yui2loader->filter = null; 00134 } 00135 if (!empty($CFG->useexternalyui) and strpos($CFG->httpswwwroot, 'https:') !== 0) { 00136 $this->yui3loader->base = 'http://yui.yahooapis.com/' . $CFG->yui3version . '/build/'; 00137 $this->yui2loader->base = 'http://yui.yahooapis.com/' . $CFG->yui2version . '/build/'; 00138 $this->yui3loader->comboBase = 'http://yui.yahooapis.com/combo?'; 00139 $this->yui2loader->comboBase = 'http://yui.yahooapis.com/combo?'; 00140 } else { 00141 $this->yui3loader->base = $CFG->httpswwwroot . '/lib/yui/'. $CFG->yui3version . '/build/'; 00142 $this->yui2loader->base = $CFG->httpswwwroot . '/lib/yui/'. $CFG->yui2version . '/build/'; 00143 $this->yui3loader->comboBase = $CFG->httpswwwroot . '/theme/yui_combo.php?'; 00144 $this->yui2loader->comboBase = $CFG->httpswwwroot . '/theme/yui_combo.php?'; 00145 } 00146 00147 // enable combo loader? this significantly helps with caching and performance! 00148 $this->yui3loader->combine = !empty($CFG->yuicomboloading); 00149 $this->yui2loader->combine = !empty($CFG->yuicomboloading); 00150 00151 if (empty($CFG->cachejs)) { 00152 $jsrev = -1; 00153 } else if (empty($CFG->jsrev)) { 00154 $jsrev = 1; 00155 } else { 00156 $jsrev = $CFG->jsrev; 00157 } 00158 00159 // set up JS YUI loader helper object 00160 $this->M_yui_loader = new stdClass(); 00161 $this->M_yui_loader->base = $this->yui3loader->base; 00162 $this->M_yui_loader->comboBase = $this->yui3loader->comboBase; 00163 $this->M_yui_loader->combine = $this->yui3loader->combine; 00164 $this->M_yui_loader->filter = (string)$this->yui3loader->filter; 00165 $this->M_yui_loader->insertBefore = 'firstthemesheet'; 00166 $this->M_yui_loader->modules = array(); 00167 $this->M_yui_loader->groups = array( 00168 'moodle' => array( 00169 'name' => 'moodle', 00170 'base' => $CFG->httpswwwroot . '/theme/yui_combo.php?moodle/'.$jsrev.'/', 00171 'comboBase' => $CFG->httpswwwroot . '/theme/yui_combo.php?', 00172 'combine' => $this->yui3loader->combine, 00173 'filter' => '', 00174 'ext' => false, 00175 'root' => 'moodle/'.$jsrev.'/', // Add the rev to the root path so that we can control caching 00176 'patterns' => array( 00177 'moodle-' => array( 00178 'group' => 'moodle', 00179 'configFn' => '@MOODLECONFIGFN@' 00180 ), 00181 'root' => 'moodle' 00182 ) 00183 ), 00184 'local' => array( 00185 'name' => 'gallery', 00186 'base' => $CFG->wwwroot.'/lib/yui/gallery/', 00187 'comboBase' => $CFG->httpswwwroot . '/theme/yui_combo.php?', 00188 'combine' => $this->yui3loader->combine, 00189 'filter' => $this->M_yui_loader->filter, 00190 'ext' => false, 00191 'root' => 'gallery/', 00192 'patterns' => array( 00193 'gallery-' => array( 00194 'group' => 'gallery', 00195 'configFn' => '@GALLERYCONFIGFN@', 00196 ), 00197 'root' => 'gallery' 00198 ) 00199 ) 00200 ); 00201 $this->add_yui2_modules(); // adds loading info for all YUI2 modules 00202 $this->js_module($this->find_module('core_filepicker')); 00203 $this->js_module($this->find_module('core_dock')); 00204 00205 } 00206 00211 protected function add_yui2_modules() { 00212 //note: this function is definitely not perfect, because 00213 // it adds tons of markup into each page, but it can be 00214 // abstracted into separate JS file with proper headers 00215 global $CFG; 00216 00217 $GLOBALS['yui_current'] = array(); 00218 require($CFG->libdir.'/yui/phploader/lib/meta/config_'.$CFG->yui2version.'.php'); 00219 $info = $GLOBALS['yui_current']; 00220 unset($GLOBALS['yui_current']); 00221 00222 if (empty($CFG->yuicomboloading)) { 00223 $urlbase = $this->yui2loader->base; 00224 } else { 00225 $urlbase = $this->yui2loader->comboBase.$CFG->yui2version.'/build/'; 00226 } 00227 00228 $modules = array(); 00229 $ignored = array(); // list of CSS modules that are not needed 00230 foreach ($info['moduleInfo'] as $name => $module) { 00231 if ($module['type'] === 'css') { 00232 $ignored[$name] = true; 00233 } else { 00234 $modules['yui2-'.$name] = $module; 00235 } 00236 } 00237 foreach ($modules as $name=>$module) { 00238 $module['fullpath'] = $urlbase.$module['path']; // fix path to point to correct location 00239 unset($module['path']); 00240 unset($module['skinnable']); // we load all YUI2 css automatically, this prevents weird missing css loader problems 00241 foreach(array('requires', 'optional', 'supersedes') as $fixme) { 00242 if (!empty($module[$fixme])) { 00243 $fixed = false; 00244 foreach ($module[$fixme] as $key=>$dep) { 00245 if (isset($ignored[$dep])) { 00246 unset($module[$fixme][$key]); 00247 $fixed = true; 00248 } else { 00249 $module[$fixme][$key] = 'yui2-'.$dep; 00250 } 00251 } 00252 if ($fixed) { 00253 $module[$fixme] = array_merge($module[$fixme]); // fix keys 00254 } 00255 } 00256 } 00257 $this->M_yui_loader->modules[$name] = $module; 00258 if (debugging('', DEBUG_DEVELOPER)) { 00259 if (!array_key_exists($name, $this->debug_moduleloadstacktraces)) { 00260 $this->debug_moduleloadstacktraces[$name] = array(); 00261 } 00262 $this->debug_moduleloadstacktraces[$name][] = format_backtrace(debug_backtrace()); 00263 } 00264 } 00265 } 00266 00273 protected function init_requirements_data(moodle_page $page, core_renderer $renderer) { 00274 global $CFG; 00275 00276 // JavaScript should always work with $CFG->httpswwwroot rather than $CFG->wwwroot. 00277 // Otherwise, in some situations, users will get warnings about insecure content 00278 // on secure pages from their web browser. 00279 00280 $this->M_cfg = array( 00281 'wwwroot' => $CFG->httpswwwroot, // Yes, really. See above. 00282 'sesskey' => sesskey(), 00283 'loadingicon' => $renderer->pix_url('i/loading_small', 'moodle')->out(false), 00284 'themerev' => theme_get_revision(), 00285 'theme' => $page->theme->name, 00286 'jsrev' => ((empty($CFG->cachejs) or empty($CFG->jsrev)) ? -1 : $CFG->jsrev), 00287 ); 00288 if (debugging('', DEBUG_DEVELOPER)) { 00289 $this->M_cfg['developerdebug'] = true; 00290 $this->yui2_lib('logger'); 00291 } 00292 00293 // accessibility stuff 00294 $this->skip_link_to('maincontent', get_string('tocontent', 'access')); 00295 00296 // to be removed soon 00297 $this->yui2_lib('dom'); // at least javascript-static.js needs to be migrated to YUI3 00298 00299 $this->string_for_js('confirmation', 'admin'); 00300 $this->string_for_js('cancel', 'moodle'); 00301 $this->string_for_js('yes', 'moodle'); 00302 00303 if ($page->pagelayout === 'frametop') { 00304 $this->js_init_call('M.util.init_frametop'); 00305 } 00306 } 00307 00324 public function js($url, $inhead=false) { 00325 $url = $this->js_fix_url($url); 00326 $where = $inhead ? 'head' : 'footer'; 00327 $this->jsincludes[$where][$url->out()] = $url; 00328 } 00329 00347 public function yui2_lib($libname) { 00348 $libnames = (array)$libname; 00349 foreach ($libnames as $lib) { 00350 $this->yui2loader->load($lib); 00351 } 00352 } 00353 00359 protected function js_fix_url($url) { 00360 global $CFG; 00361 00362 if ($url instanceof moodle_url) { 00363 return $url; 00364 } else if (strpos($url, '/') === 0) { 00365 if (debugging()) { 00366 // check file existence only when in debug mode 00367 if (!file_exists($CFG->dirroot . strtok($url, '?'))) { 00368 throw new coding_exception('Attempt to require a JavaScript file that does not exist.', $url); 00369 } 00370 } 00371 if (!empty($CFG->cachejs) and !empty($CFG->jsrev) and strpos($url, '/lib/editor/') !== 0 and substr($url, -3) === '.js') { 00372 return new moodle_url($CFG->httpswwwroot.'/lib/javascript.php', array('file'=>$url, 'rev'=>$CFG->jsrev)); 00373 } else { 00374 return new moodle_url($CFG->httpswwwroot.$url); 00375 } 00376 } else { 00377 throw new coding_exception('Invalid JS url, it has to be shortened url starting with / or moodle_url instance.', $url); 00378 } 00379 } 00380 00386 protected function find_module($component) { 00387 global $CFG; 00388 00389 $module = null; 00390 00391 00392 if (strpos($component, 'core_') === 0) { 00393 // must be some core stuff - list here is not complete, this is just the stuff used from multiple places 00394 // so that we do nto have to repeat the definition of these modules over and over again 00395 switch($component) { 00396 case 'core_filepicker': 00397 $module = array('name' => 'core_filepicker', 00398 'fullpath' => '/repository/filepicker.js', 00399 'requires' => array('base', 'node', 'node-event-simulate', 'json', 'async-queue', 'io-base', 'io-upload-iframe', 'io-form', 'yui2-button', 'yui2-container', 'yui2-layout', 'yui2-menu', 'yui2-treeview', 'yui2-dragdrop', 'yui2-cookie'), 00400 'strings' => array(array('add', 'repository'), array('back', 'repository'), array('cancel', 'moodle'), array('close', 'repository'), 00401 array('cleancache', 'repository'), array('copying', 'repository'), array('date', 'repository'), array('downloadsucc', 'repository'), 00402 array('emptylist', 'repository'), array('error', 'repository'), array('federatedsearch', 'repository'), 00403 array('filenotnull', 'repository'), array('getfile', 'repository'), array('help', 'moodle'), array('iconview', 'repository'), 00404 array('invalidjson', 'repository'), array('linkexternal', 'repository'), array('listview', 'repository'), 00405 array('loading', 'repository'), array('login', 'repository'), array('logout', 'repository'), array('noenter', 'repository'), 00406 array('noresult', 'repository'), array('manageurl', 'repository'), array('popup', 'repository'), array('preview', 'repository'), 00407 array('refresh', 'repository'), array('save', 'repository'), array('saveas', 'repository'), array('saved', 'repository'), 00408 array('saving', 'repository'), array('search', 'repository'), array('searching', 'repository'), array('size', 'repository'), 00409 array('submit', 'repository'), array('sync', 'repository'), array('title', 'repository'), array('upload', 'repository'), 00410 array('uploading', 'repository'), array('xhtmlerror', 'repository'), 00411 array('cancel'), array('chooselicense', 'repository'), array('author', 'repository'), 00412 array('ok', 'moodle'), array('error', 'moodle'), array('info', 'moodle'), array('norepositoriesavailable', 'repository'), array('norepositoriesexternalavailable', 'repository'), 00413 array('nofilesattached', 'repository'), array('filepicker', 'repository'), 00414 array('nofilesavailable', 'repository'), array('overwrite', 'repository'), 00415 array('renameto', 'repository'), array('fileexists', 'repository'), 00416 array('fileexistsdialogheader', 'repository'), array('fileexistsdialog_editor', 'repository'), 00417 array('fileexistsdialog_filemanager', 'repository') 00418 )); 00419 break; 00420 case 'core_comment': 00421 $module = array('name' => 'core_comment', 00422 'fullpath' => '/comment/comment.js', 00423 'requires' => array('base', 'io-base', 'node', 'json', 'yui2-animation', 'overlay'), 00424 'strings' => array(array('confirmdeletecomments', 'admin'), array('yes', 'moodle'), array('no', 'moodle')) 00425 ); 00426 break; 00427 case 'core_role': 00428 $module = array('name' => 'core_role', 00429 'fullpath' => '/admin/roles/module.js', 00430 'requires' => array('node', 'cookie')); 00431 break; 00432 case 'core_completion': 00433 $module = array('name' => 'core_completion', 00434 'fullpath' => '/course/completion.js'); 00435 break; 00436 case 'core_dock': 00437 $module = array('name' => 'core_dock', 00438 'fullpath' => '/blocks/dock.js', 00439 'requires' => array('base', 'node', 'event-custom', 'event-mouseenter', 'event-resize'), 00440 'strings' => array(array('addtodock', 'block'),array('undockitem', 'block'),array('undockall', 'block'),array('thisdirectionvertical', 'langconfig'))); 00441 break; 00442 case 'core_message': 00443 $module = array('name' => 'core_message', 00444 'requires' => array('base', 'node', 'event', 'node-event-simulate'), 00445 'fullpath' => '/message/module.js'); 00446 break; 00447 case 'core_group': 00448 $module = array('name' => 'core_group', 00449 'fullpath' => '/group/module.js', 00450 'requires' => array('node', 'overlay', 'event-mouseenter')); 00451 break; 00452 case 'core_question_engine': 00453 $module = array('name' => 'core_question_engine', 00454 'fullpath' => '/question/qengine.js', 00455 'requires' => array('node', 'event')); 00456 break; 00457 case 'core_rating': 00458 $module = array('name' => 'core_rating', 00459 'fullpath' => '/rating/module.js', 00460 'requires' => array('node', 'event', 'overlay', 'io-base', 'json')); 00461 break; 00462 case 'core_filetree': 00463 $module = array('name' => 'core_filetree', 00464 'fullpath' => '/files/module.js', 00465 'requires' => array('node', 'event', 'overlay', 'io-base', 'json', 'yui2-treeview')); 00466 break; 00467 } 00468 00469 } else { 00470 if ($dir = get_component_directory($component)) { 00471 if (file_exists("$dir/module.js")) { 00472 if (strpos($dir, $CFG->dirroot.'/') === 0) { 00473 $dir = substr($dir, strlen($CFG->dirroot)); 00474 $module = array('name'=>$component, 'fullpath'=>"$dir/module.js", 'requires' => array()); 00475 } 00476 } 00477 } 00478 } 00479 00480 return $module; 00481 } 00482 00489 public function js_module($module) { 00490 global $CFG; 00491 00492 if (empty($module)) { 00493 throw new coding_exception('Missing YUI3 module name or full description.'); 00494 } 00495 00496 if (is_string($module)) { 00497 $module = $this->find_module($module); 00498 } 00499 00500 if (empty($module) or empty($module['name']) or empty($module['fullpath'])) { 00501 throw new coding_exception('Missing YUI3 module details.'); 00502 } 00503 00504 // Don't load this module if we already have, no need to! 00505 if ($this->js_module_loaded($module['name'])) { 00506 if (debugging('', DEBUG_DEVELOPER)) { 00507 $this->debug_moduleloadstacktraces[$module['name']][] = format_backtrace(debug_backtrace()); 00508 } 00509 return; 00510 } 00511 00512 $module['fullpath'] = $this->js_fix_url($module['fullpath'])->out(false); 00513 // add all needed strings 00514 if (!empty($module['strings'])) { 00515 foreach ($module['strings'] as $string) { 00516 $identifier = $string[0]; 00517 $component = isset($string[1]) ? $string[1] : 'moodle'; 00518 $a = isset($string[2]) ? $string[2] : null; 00519 $this->string_for_js($identifier, $component, $a); 00520 } 00521 } 00522 unset($module['strings']); 00523 00524 // Process module requirements and attempt to load each. This allows 00525 // moodle modules to require each other. 00526 if (!empty($module['requires'])){ 00527 foreach ($module['requires'] as $requirement) { 00528 $rmodule = $this->find_module($requirement); 00529 if (is_array($rmodule)) { 00530 $this->js_module($rmodule); 00531 } 00532 } 00533 } 00534 00535 if ($this->headdone) { 00536 $this->extramodules[$module['name']] = $module; 00537 } else { 00538 $this->M_yui_loader->modules[$module['name']] = $module; 00539 } 00540 if (debugging('', DEBUG_DEVELOPER)) { 00541 if (!array_key_exists($module['name'], $this->debug_moduleloadstacktraces)) { 00542 $this->debug_moduleloadstacktraces[$module['name']] = array(); 00543 } 00544 $this->debug_moduleloadstacktraces[$module['name']][] = format_backtrace(debug_backtrace()); 00545 } 00546 } 00547 00554 protected function js_module_loaded($module) { 00555 if (is_string($module)) { 00556 $modulename = $module; 00557 } else { 00558 $modulename = $module['name']; 00559 } 00560 return array_key_exists($modulename, $this->M_yui_loader->modules) || 00561 array_key_exists($modulename, $this->extramodules); 00562 } 00563 00568 public function get_loaded_modules() { 00569 return $this->debug_moduleloadstacktraces; 00570 } 00571 00592 public function css($stylesheet) { 00593 global $CFG; 00594 00595 if ($this->headdone) { 00596 throw new coding_exception('Cannot require a CSS file after <head> has been printed.', $stylesheet); 00597 } 00598 00599 if ($stylesheet instanceof moodle_url) { 00600 // ok 00601 } else if (strpos($stylesheet, '/') === 0) { 00602 $stylesheet = new moodle_url($CFG->httpswwwroot.$stylesheet); 00603 } else { 00604 throw new coding_exception('Invalid stylesheet parameter.', $stylesheet); 00605 } 00606 00607 $this->cssurls[$stylesheet->out()] = $stylesheet; // overrides 00608 } 00609 00616 public function css_theme(moodle_url $stylesheet) { 00617 $this->cssthemeurls[] = $stylesheet; 00618 } 00619 00635 public function skip_link_to($target, $linktext) { 00636 if ($this->topofbodydone) { 00637 debugging('Page header already printed, can not add skip links any more, code needs to be fixed.'); 00638 return; 00639 } 00640 $this->skiplinks[$target] = $linktext; 00641 } 00642 00668 public function js_function_call($function, array $arguments = null, $ondomready = false, $delay = 0) { 00669 $where = $ondomready ? 'ondomready' : 'normal'; 00670 $this->jscalls[$where][] = array($function, $arguments, $delay); 00671 } 00672 00684 public function js_gallery_module($modules, $version, $function, array $arguments = null, $ondomready = false) { 00685 global $CFG; 00686 debugging('This function will be removed before 2.0 is released please change it from js_gallery_module to yui_module', DEBUG_DEVELOPER); 00687 $this->yui_module($modules, $function, $arguments, $version, $ondomready); 00688 } 00689 00705 public function yui_module($modules, $function, array $arguments = null, $galleryversion = '2010.04.08-12-35', $ondomready = false) { 00706 global $CFG; 00707 00708 if (!is_array($modules)) { 00709 $modules = array($modules); 00710 } 00711 if (empty($CFG->useexternalyui) || true) { 00712 // We need to set the M.yui.galleryversion to the correct version 00713 $jscode = 'M.yui.galleryversion='.json_encode($galleryversion).';'; 00714 } else { 00715 // Set Y's config.gallery to the version 00716 $jscode = 'Y.config.gallery='.json_encode($galleryversion).';'; 00717 } 00718 $jscode .= 'Y.use('.join(',', array_map('json_encode', $modules)).',function() {'.js_writer::function_call($function, $arguments).'});'; 00719 if ($ondomready) { 00720 $jscode = "Y.on('domready', function() { $jscode });"; 00721 } 00722 $this->jsinitcode[] = $jscode; 00723 } 00724 00738 public function js_init_call($function, array $extraarguments = null, $ondomready = false, array $module = null) { 00739 $jscode = js_writer::function_call_with_Y($function, $extraarguments); 00740 if (!$module) { 00741 // detect module automatically 00742 if (preg_match('/M\.([a-z0-9]+_[^\.]+)/', $function, $matches)) { 00743 $module = $this->find_module($matches[1]); 00744 } 00745 } 00746 00747 $this->js_init_code($jscode, $ondomready, $module); 00748 } 00749 00760 public function js_init_code($jscode, $ondomready = false, array $module = null) { 00761 $jscode = trim($jscode, " ;\n"). ';'; 00762 00763 if ($module) { 00764 $this->js_module($module); 00765 $modulename = $module['name']; 00766 $jscode = "Y.use('$modulename', function(Y) { $jscode });"; 00767 } 00768 00769 if ($ondomready) { 00770 $jscode = "Y.on('domready', function() { $jscode });"; 00771 } 00772 00773 $this->jsinitcode[] = $jscode; 00774 } 00775 00825 public function string_for_js($identifier, $component, $a = NULL) { 00826 $string = get_string($identifier, $component, $a); 00827 if (!$component) { 00828 throw new coding_exception('The $module parameter is required for page_requirements_manager::string_for_js.'); 00829 } 00830 if (isset($this->stringsforjs[$component][$identifier]) && $this->stringsforjs[$component][$identifier] !== $string) { 00831 throw new coding_exception("Attempt to re-define already required string '$identifier' " . 00832 "from lang file '$component'. Did you already ask for it with a different \$a? {$this->stringsforjs[$component][$identifier]} !== $string"); 00833 } 00834 $this->stringsforjs[$component][$identifier] = $string; 00835 } 00836 00861 public function strings_for_js($identifiers, $component, $a=NULL) { 00862 foreach ($identifiers as $key => $identifier) { 00863 if (is_array($a) && array_key_exists($key, $a)) { 00864 $extra = $a[$key]; 00865 } else { 00866 $extra = $a; 00867 } 00868 $this->string_for_js($identifier, $component, $extra); 00869 } 00870 } 00871 00890 public function data_for_js($variable, $data, $inhead=false) { 00891 $where = $inhead ? 'head' : 'footer'; 00892 $this->jsinitvariables[$where][] = array($variable, $data); 00893 } 00894 00904 public function event_handler($selector, $event, $function, array $arguments = null) { 00905 $this->eventhandlers[] = array('selector'=>$selector, 'event'=>$event, 'function'=>$function, 'arguments'=>$arguments); 00906 } 00907 00912 protected function get_event_handler_code() { 00913 $output = ''; 00914 foreach ($this->eventhandlers as $h) { 00915 $output .= js_writer::event_handler($h['selector'], $h['event'], $h['function'], $h['arguments']); 00916 } 00917 return $output; 00918 } 00919 00925 protected function get_javascript_code($ondomready) { 00926 $where = $ondomready ? 'ondomready' : 'normal'; 00927 $output = ''; 00928 if ($this->jscalls[$where]) { 00929 foreach ($this->jscalls[$where] as $data) { 00930 $output .= js_writer::function_call($data[0], $data[1], $data[2]); 00931 } 00932 if (!empty($ondomready)) { 00933 $output = " Y.on('domready', function() {\n$output\n });"; 00934 } 00935 } 00936 return $output; 00937 } 00938 00943 protected function get_javascript_init_code() { 00944 if (count($this->jsinitcode)) { 00945 return implode("\n", $this->jsinitcode) . "\n"; 00946 } 00947 return ''; 00948 } 00949 00959 protected function get_yui3lib_headcode() { 00960 global $CFG; 00961 00962 $code = ''; 00963 00964 if ($this->yui3loader->combine) { 00965 $code .= '<link rel="stylesheet" type="text/css" href="'.$this->yui3loader->comboBase 00966 .$CFG->yui3version.'/build/cssreset/reset-min.css&' 00967 .$CFG->yui3version.'/build/cssfonts/fonts-min.css&' 00968 .$CFG->yui3version.'/build/cssgrids/grids-min.css&' 00969 .$CFG->yui3version.'/build/cssbase/base-min.css" />'; 00970 } else { 00971 $code .= '<link rel="stylesheet" type="text/css" href="'.$this->yui3loader->base.'cssreset/reset-min.css" />'; 00972 $code .= '<link rel="stylesheet" type="text/css" href="'.$this->yui3loader->base.'cssfonts/fonts-min.css" />'; 00973 $code .= '<link rel="stylesheet" type="text/css" href="'.$this->yui3loader->base.'cssgrids/grids-min.css" />'; 00974 $code .= '<link rel="stylesheet" type="text/css" href="'.$this->yui3loader->base.'cssbase/base-min.css" />'; 00975 } 00976 00977 $code .= '<script type="text/javascript" src="'.$this->yui3loader->base.'yui/yui-min.js"></script>'; 00978 00979 if ($this->yui3loader->filter === YUI_RAW) { 00980 $code = str_replace('-min.css', '.css', $code); 00981 $code = str_replace('-min.js', '.js', $code); 00982 } else if ($this->yui3loader->filter === YUI_DEBUG) { 00983 $code = str_replace('-min.css', '.css', $code); 00984 $code = str_replace('-min.js', '-debug.js', $code); 00985 } 00986 00987 return $code; 00988 } 00989 01002 public function get_yui2lib_code() { 01003 global $CFG; 01004 01005 if ($this->headdone) { 01006 $code = $this->yui2loader->script(); 01007 } else { 01008 $code = $this->yui2loader->script(); 01009 if ($this->yui2loader->combine) { 01010 $skinurl = $this->yui2loader->comboBase . $CFG->yui2version . '/build/assets/skins/sam/skin.css'; 01011 } else { 01012 $skinurl = $this->yui2loader->base . 'assets/skins/sam/skin.css'; 01013 } 01014 // please note this is a temporary hack until we fully migrate to later YUI3 that has all the widgets 01015 $attributes = array('rel'=>'stylesheet', 'type'=>'text/css', 'href'=>$skinurl); 01016 $code .= "\n" . html_writer::empty_tag('link', $attributes) . "\n"; 01017 } 01018 $code = str_replace('&', '&', $code); 01019 $code = str_replace('&', '&', $code); 01020 return $code; 01021 } 01022 01027 protected function get_css_code() { 01028 // First of all the theme CSS, then any custom CSS 01029 // Please note custom CSS is strongly discouraged, 01030 // because it can not be overridden by themes! 01031 // It is suitable only for things like mod/data which accepts CSS from teachers. 01032 $attributes = array('rel'=>'stylesheet', 'type'=>'text/css'); 01033 01034 // This line of code may look funny but it is currently required in order 01035 // to avoid MASSIVE display issues in Internet Explorer. 01036 // As of IE8 + YUI3.1.1 the reference stylesheet (firstthemesheet) gets 01037 // ignored whenever another resource is added until such time as a redraw 01038 // is forced, usually by moving the mouse over the affected element. 01039 $code = html_writer::tag('script', '/** Required in order to fix style inclusion problems in IE with YUI **/', array('id'=>'firstthemesheet', 'type'=>'text/css')); 01040 01041 $urls = $this->cssthemeurls + $this->cssurls; 01042 foreach ($urls as $url) { 01043 $attributes['href'] = $url; 01044 $code .= html_writer::empty_tag('link', $attributes) . "\n"; 01045 // this id is needed in first sheet only so that theme may override YUI sheets laoded on the fly 01046 unset($attributes['id']); 01047 } 01048 01049 return $code; 01050 } 01051 01056 protected function get_extra_modules_code() { 01057 if (empty($this->extramodules)) { 01058 return ''; 01059 } 01060 return html_writer::script(js_writer::function_call('M.yui.add_module', array($this->extramodules))); 01061 } 01062 01073 public function get_head_code(moodle_page $page, core_renderer $renderer) { 01074 global $CFG; 01075 01076 // note: the $page and $output are not stored here because it would 01077 // create circular references in memory which prevents garbage collection 01078 $this->init_requirements_data($page, $renderer); 01079 01080 // yui3 JS and CSS is always loaded first - it is cached in browser 01081 $output = $this->get_yui3lib_headcode(); 01082 01083 // BC: load basic YUI2 for now, all yui2 things should be loaded via Y.use('yui2-oldmodulename') 01084 $output .= $this->get_yui2lib_code(); 01085 01086 // now theme CSS + custom CSS in this specific order 01087 $output .= $this->get_css_code(); 01088 01089 // set up global YUI3 loader object - this should contain all code needed by plugins 01090 // note: in JavaScript just use "YUI(M.yui.loader).use('overlay', function(Y) { .... });" 01091 // this needs to be done before including any other script 01092 $js = "var M = {}; M.yui = {}; var moodleConfigFn = function(me) {var p = me.path, b = me.name.replace(/^moodle-/,'').split('-', 3), n = b.pop();if (/(skin|core)/.test(n)) {n = b.pop();me.type = 'css';};me.path = b.join('-')+'/'+n+'/'+n+'.'+me.type;}; var galleryConfigFn = function(me) {var p = me.path,v=M.yui.galleryversion,f;if(/-(skin|core)/.test(me.name)) {me.type = 'css';p = p.replace(/-(skin|core)/, '').replace(/\.js/, '.css').split('/'), f = p.pop().replace(/(\-(min|debug))/, '');if (/-skin/.test(me.name)) {p.splice(p.length,0,v,'assets','skins','sam', f);} else {p.splice(p.length,0,v,'assets', f);};} else {p = p.split('/'), f = p.pop();p.splice(p.length,0,v, f);};me.path = p.join('/');};\n"; 01093 $js .= js_writer::set_variable('M.yui.loader', $this->M_yui_loader, false) . "\n"; 01094 $js .= js_writer::set_variable('M.cfg', $this->M_cfg, false); 01095 $js = str_replace('"@GALLERYCONFIGFN@"', 'galleryConfigFn', $js); 01096 $js = str_replace('"@MOODLECONFIGFN@"', 'moodleConfigFn', $js); 01097 01098 $output .= html_writer::script($js); 01099 01100 // link our main JS file, all core stuff should be there 01101 $output .= html_writer::script('', $this->js_fix_url('/lib/javascript-static.js')); 01102 01103 // add variables 01104 if ($this->jsinitvariables['head']) { 01105 $js = ''; 01106 foreach ($this->jsinitvariables['head'] as $data) { 01107 list($var, $value) = $data; 01108 $js .= js_writer::set_variable($var, $value, true); 01109 } 01110 $output .= html_writer::script($js); 01111 } 01112 01113 // all the other linked things from HEAD - there should be as few as possible 01114 if ($this->jsincludes['head']) { 01115 foreach ($this->jsincludes['head'] as $url) { 01116 $output .= html_writer::script('', $url); 01117 } 01118 } 01119 01120 // mark head sending done, it is not possible to anything there 01121 $this->headdone = true; 01122 01123 return $output; 01124 } 01125 01134 public function get_top_of_body_code() { 01135 // first the skip links 01136 $links = ''; 01137 $attributes = array('class'=>'skip'); 01138 foreach ($this->skiplinks as $url => $text) { 01139 $attributes['href'] = '#' . $url; 01140 $links .= html_writer::tag('a', $text, $attributes); 01141 } 01142 $output = html_writer::tag('div', $links, array('class'=>'skiplinks')) . "\n"; 01143 01144 // then the clever trick for hiding of things not needed when JS works 01145 $output .= html_writer::script("document.body.className += ' jsenabled';") . "\n"; 01146 $this->topofbodydone = true; 01147 return $output; 01148 } 01149 01158 public function get_end_code() { 01159 global $CFG; 01160 // add other requested modules 01161 $output = $this->get_extra_modules_code(); 01162 01163 // add missing YUI2 YUI - to be removed once we convert everything to YUI3! 01164 $output .= $this->get_yui2lib_code(); 01165 01166 // all the other linked scripts - there should be as few as possible 01167 if ($this->jsincludes['footer']) { 01168 foreach ($this->jsincludes['footer'] as $url) { 01169 $output .= html_writer::script('', $url); 01170 } 01171 } 01172 01173 // add all needed strings 01174 if (!empty($this->stringsforjs)) { 01175 $output .= html_writer::script(js_writer::set_variable('M.str', $this->stringsforjs)); 01176 } 01177 01178 // add variables 01179 if ($this->jsinitvariables['footer']) { 01180 $js = ''; 01181 foreach ($this->jsinitvariables['footer'] as $data) { 01182 list($var, $value) = $data; 01183 $js .= js_writer::set_variable($var, $value, true); 01184 } 01185 $output .= html_writer::script($js); 01186 } 01187 01188 $inyuijs = $this->get_javascript_code(false); 01189 $ondomreadyjs = $this->get_javascript_code(true); 01190 $jsinit = $this->get_javascript_init_code(); 01191 $handlersjs = $this->get_event_handler_code(); 01192 01193 // there is no global Y, make sure it is available in your scope 01194 $js = "YUI(M.yui.loader).use('node', function(Y) {\n{$inyuijs}{$ondomreadyjs}{$jsinit}{$handlersjs}\n});"; 01195 01196 $output .= html_writer::script($js); 01197 01198 return $output; 01199 } 01200 01204 public function is_head_done() { 01205 return $this->headdone; 01206 } 01207 01211 public function is_top_of_body_done() { 01212 return $this->topofbodydone; 01213 } 01214 } 01215 01220 function js_reset_all_caches() { 01221 global $CFG; 01222 require_once("$CFG->libdir/filelib.php"); 01223 01224 set_config('jsrev', empty($CFG->jsrev) ? 1 : $CFG->jsrev+1); 01225 fulldelete("$CFG->cachedir/js"); 01226 } 01227