ajaxlib: MDL-16695 New page_requirements_manager class. Soon to replace require_js.

This new class does the work that require_js used to do, and more.
It can track a number of different things that may be required to
be output somewhere on the page, including:

    * Links to JS files
    * Links to CSS files
    * Links to YUI libraries (this class knows about the dependancies between the different libs).
    * Skip links that go from the top of <body> to various places in the content.
    * Calls to JavaScript functions (for example to initialise things)
    * Bits of data from PHP that need to be available to JavaScript
    * As a special case of that, an easy way to pass language strings to JS.

The new API looks like

$PAGE->requires->css('mod/mymod/styles.css');
$PAGE->requires->js('mod/mymod/script.js');
$PAGE->requires->js('mod/mymod/small_but_urgent.js')->in_head();
$PAGE->requires->js_function_call('init_mymod', array($data))->on_dom_ready();

$PAGE->requires is the canonical instances of this new class.

The commit also includes unit tests, and hopefully the PHP doc comments are
clear enough that it is easy to understand.
This commit is contained in:
tjhunt
2009-06-12 03:13:29 +00:00
parent 11d51756cc
commit b2330db606
4 changed files with 1500 additions and 31 deletions
+1
View File
@@ -21,6 +21,7 @@ $string['sitemap'] = 'Site map';
$string['skipa'] = 'Skip $a';
$string['skipblock'] = 'Skip block';
$string['skipnavigation'] = 'Skip navigation';
$string['skipto'] = 'Skip to $a';
$string['tabledata'] = 'Data table, $a';
$string['tablelayout'] = 'Layout table, $a';
$string['tocontent'] = 'Skip to main content';
+1099 -1
View File
File diff suppressed because it is too large Load Diff
+385 -30
View File
@@ -1,48 +1,403 @@
<?php // $Id$
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
///////////////////////////////////////////////////////////////////////////
// //
// NOTICE OF COPYRIGHT //
// //
// Moodle - Modular Object-Oriented Dynamic Learning Environment //
// http://moodle.org //
// //
// Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation; either version 2 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License for more details: //
// //
// http://www.gnu.org/copyleft/gpl.html //
// //
///////////////////////////////////////////////////////////////////////////
/**
* Unit tests for (some of) ../ajaxlib.php.
*
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package moodlecore
* @copyright 2009 Tim Hunt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
if (!defined('MOODLE_INTERNAL')) {
die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
}
require_once($CFG->libdir . '/ajax/ajaxlib.php');
/**
* Unit tests of mathslib wrapper and underlying EvalMath library.
* Helper class, adds some useful stuff to UnitTestCase that the other test cases
* classes in this file can benefit from.
*
* @author Petr Skoda (skodak)
* @version $Id$
* @copyright 2009 Tim Hunt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class ajax_test extends UnitTestCase {
abstract class ajaxlib_unit_test_base extends UnitTestCase {
protected $requires;
public function setUp() {
parent::setUp();
$this->requires = new page_requirements_manager();
}
public function tearDown() {
$this->requires = null;
parent::tearDown();
}
public function assertContains($actual, $expectedsubstring) {
$this->assertNotIdentical(strpos($actual, (string) $expectedsubstring), false,
"[$actual] does not containg the substring [$expectedsubstring].");
}
}
/**
* Unit tests for the requriement_base base class. Don't be confused by the
* fact we are using a specific subclass to test with.
*
* @copyright 2009 Tim Hunt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class requriement_base_test extends ajaxlib_unit_test_base {
protected $classname = 'required_css';
public function test_not_done_initially() {
$requirement = new $this->classname($this->requires, '');
$this->assertFalse($requirement->is_done());
}
public function test_done_when_marked() {
$requirement = new $this->classname($this->requires, '');
$requirement->mark_done();
$this->assertTrue($requirement->is_done());
}
}
/**
* Unit tests for the required_css class.
*
* @copyright 2009 Tim Hunt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class required_css_test extends ajaxlib_unit_test_base {
protected $cssurl = 'http://example.com/styles.css';
public function test_when() {
$requirement = new required_css($this->requires, $this->cssurl);
$this->assertEqual($requirement->get_when(), page_requirements_manager::WHEN_IN_HEAD);
}
public function test_round_trip_url_to_html() {
$requirement = new required_css($this->requires, $this->cssurl);
$html = $requirement->get_html();
$this->assertContains($html, $this->cssurl);
$this->assertContains($html, '<link ');
$this->assertContains($html, 'type="text/css"');
}
}
/**
* Unit tests for the required_skip_link class.
*
* @copyright 2009 Tim Hunt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class required_skip_link_test extends ajaxlib_unit_test_base {
protected $target = 'skiptarget';
protected $linktext = 'Skip to link text';
public function test_when() {
$requirement = new required_skip_link($this->requires, $this->target, $this->linktext);
$this->assertEqual($requirement->get_when(), page_requirements_manager::WHEN_TOP_OF_BODY);
}
public function test_round_trip_to_html() {
$requirement = new required_skip_link($this->requires, $this->target, $this->linktext);
$html = $requirement->get_html();
$this->assertContains($html, '>' . $this->linktext . '</a>');
$this->assertContains($html, '<a ');
$this->assertContains($html, 'class="skip"');
$this->assertContains($html, 'href="#' . $this->target . '"');
}
}
/**
* Unit tests for the required_js_code class. Once again we are tesing the
* behaviour of an abstract class by creating instances one particular subclass.
*
* @copyright 2009 Tim Hunt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class required_js_code_test extends ajaxlib_unit_test_base {
protected $classname = 'required_data_for_js';
public function test_when() {
$requirement = new $this->classname($this->requires, '', '');
$this->assertEqual($requirement->get_when(), page_requirements_manager::WHEN_AT_END);
}
public function test_setting_when_to_head() {
$requirement = new $this->classname($this->requires, '', '');
$requirement->in_head();
$this->assertEqual($requirement->get_when(), page_requirements_manager::WHEN_IN_HEAD);
}
public function test_setting_when_to_top_of_body() {
$requirement = new $this->classname($this->requires, '', '');
$requirement->at_top_of_body();
$this->assertEqual($requirement->get_when(), page_requirements_manager::WHEN_TOP_OF_BODY);
}
public function test_asap_before_head_is_head() {
$requirement = new $this->classname($this->requires, '', '');
$this->assertEqual($requirement->asap(), '');
$this->assertEqual($requirement->get_when(), page_requirements_manager::WHEN_IN_HEAD);
}
public function test_asap_after_head() {
$requirement = new $this->classname($this->requires, '', '');
$this->requires->get_head_code();
$this->assertNotEqual($requirement->asap(), '');
$this->assertTrue($requirement->is_done());
}
public function test_in_head_when_too_late_throws_exception() {
$requirement = new $this->classname($this->requires, '', '');
$this->requires->get_head_code();
$this->expectException();
$requirement->in_head();
}
public function test_in_head_when_too_late_no_exception_if_done() {
$requirement = new $this->classname($this->requires, '', '');
$requirement->mark_done();
$this->requires->get_head_code();
$requirement->in_head();
$this->pass('No exception thrown as expected.');
}
public function test_body_top_when_too_late_throws_exception() {
$requirement = new $this->classname($this->requires, '', '');
$this->requires->get_top_of_body_code();
$this->expectException();
$requirement->at_top_of_body();
}
public function test_body_top_when_too_late_no_exception_if_done() {
$requirement = new $this->classname($this->requires, '', '');
$requirement->mark_done();
$this->requires->get_top_of_body_code();
$requirement->at_top_of_body();
$this->pass('No exception thrown as expected.');
}
public function test_body_top_does_not_override_in_head() {
$requirement = new $this->classname($this->requires, '', '');
$requirement->in_head();
$requirement->at_top_of_body();
$this->assertEqual($requirement->get_when(), page_requirements_manager::WHEN_IN_HEAD);
}
}
/**
* Unit tests for the required_js_function_call class.
*
* @copyright 2009 Tim Hunt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class required_js_function_call_test extends ajaxlib_unit_test_base {
protected $function = 'object.method';
protected $params = array('arg1', 2);
public function test_round_trip_to_js_code() {
$requirement = new required_js_function_call($this->requires, $this->function, $this->params);
$js = $requirement->get_js_code();
$this->assertContains($js, $this->function . '(');
$this->assertContains($js, $this->params[0]);
$this->assertContains($js, $this->params[1]);
}
public function test_setting_when_on_dom_ready() {
$requirement = new required_js_function_call($this->requires, $this->function, $this->params);
$requirement->on_dom_ready();
$this->assertEqual($requirement->get_when(), page_requirements_manager::WHEN_ON_DOM_READY);
}
public function test_body_top_does_not_override_earlier_value() {
$requirement = new required_js_function_call($this->requires, $this->function, $this->params);
$requirement->at_top_of_body();
$requirement->on_dom_ready();
$this->assertEqual($requirement->get_when(), page_requirements_manager::WHEN_TOP_OF_BODY);
}
}
/**
* Unit tests for the required_data_for_js class.
*
* @copyright 2009 Tim Hunt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class required_data_for_js_test extends ajaxlib_unit_test_base {
public function test_round_trip_to_js_code() {
$requirement = new required_data_for_js($this->requires, 'varname', 'Value');
$js = $requirement->get_js_code();
$this->assertContains($js, 'var varname =');
$this->assertContains($js, 'Value');
}
public function test_no_var_for_complex_varname_1() {
$requirement = new required_data_for_js($this->requires, 'obj.field', 'Value');
$js = $requirement->get_js_code();
$this->assertPattern('/^obj\\.field =/', $js);
$this->assertContains($js, 'Value');
}
public function test_no_var_for_complex_varname_2() {
$requirement = new required_data_for_js($this->requires, 'arry[0]', 'Value');
$js = $requirement->get_js_code();
$this->assertPattern('/^arry\\[0\\] =/', $js);
$this->assertContains($js, 'Value');
}
}
/**
* Unit tests for the page_requirements_manager class.
*
* @copyright 2009 Tim Hunt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class page_requirements_manager_test extends ajaxlib_unit_test_base {
public function test_outputting_head_marks_it_done() {
$this->requires->get_head_code();
$this->assertTrue($this->requires->is_head_done());
}
public function test_outputting_body_top_marks_it_done() {
$this->requires->get_top_of_body_code();
$this->assertTrue($this->requires->is_top_of_body_done());
}
public function test_requiring_css() {
global $CFG;
$cssfile = 'theme/standard/styles_layout.css'; // Just needs to be a CSS file that exists.
$this->requires->css($cssfile);
$html = $this->requires->get_head_code();
$this->assertContains($html, $CFG->httpswwwroot . '/' . $cssfile);
}
public function test_nonexistant_css_throws_exception() {
$cssfile = 'css/file/that/does/not/exist.css';
$this->expectException();
$this->requires->css($cssfile);
}
public function test_requiring_js() {
global $CFG;
$jsfile = 'lib/javascript-static.js'; // Just needs to be a JS file that exists.
$this->requires->js($jsfile);
$html = $this->requires->get_end_code();
$this->assertContains($html, $CFG->httpswwwroot . '/' . $jsfile);
}
public function test_nonexistant_js_throws_exception() {
$cssfile = 'js/file/that/does/not/exist.js';
$this->expectException();
$this->requires->js($cssfile);
}
public function test_requiring_skip_link() {
$this->requires->skip_link_to('target', 'Link text');
$html = $this->requires->get_top_of_body_code();
$this->assertContains($html, 'target');
$this->assertContains($html, 'Link text');
}
public function test_requiring_js_function_call() {
$this->requires->js_function_call('fn');
$html = $this->requires->get_end_code();
$this->assertContains($html, 'fn()');
}
public function test_requiring_string_for_js() {
$this->requires->string_for_js('course', 'moodle');
$html = $this->requires->get_end_code();
$this->assertContains($html, 'mstr');
$this->assertContains($html, 'course');
$this->assertContains($html, 'moodle');
}
public function test_repeat_string_different_a_throws_exception() {
$this->requires->string_for_js('added', 'moodle', 'this');
$this->expectException();
$this->requires->string_for_js('added', 'moodle', 'that');
}
public function test_repeat_string_same_a_is_ok() {
$this->requires->string_for_js('added', 'moodle', 'same$a');
$this->requires->string_for_js('added', 'moodle', 'same$a');
$this->pass('No exception thrown as expected.');
}
public function test_requiring_data_for_js() {
$this->requires->data_for_js('varname', 'Value')->at_top_of_body();
$html = $this->requires->get_top_of_body_code();
$this->assertContains($html, 'varname');
$this->assertContains($html, 'Value');
}
public function test_requiring_js_function_call_on_dom_ready() {
$this->requires->js_function_call('fn')->on_dom_ready();
$html = $this->requires->get_end_code();
$this->assertPattern('/<script.*src=".*event.*\.js"/', $html);
$this->assertContains($html, 'YAHOO.util.Event.onDOMReady');
$this->assertContains($html, 'fn()');
}
}
/**
* Unit tests for ../ajaxlib.php functions.
*
* @copyright 2008 Tim Hunt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class ajax_test extends ajaxlib_unit_test_base {
function test_ajax_generate_script_tag() {
$html = ajax_generate_script_tag('');
$this->assertContains($html, '<script type="text/javascript">');
$this->assertContains($html, '<![CDATA[');
$this->assertContains($html, ']]>');
$this->assertContains($html, '</script>');
}
function test_ajax_get_lib() {
global $CFG;
$olddebug = $CFG->debug;
+15
View File
@@ -97,6 +97,8 @@ class moodle_page {
protected $_blocks = null;
protected $_requires = null;
protected $_blockseditingcap = 'moodle/site:manageblocks';
protected $_othereditingcaps = array();
@@ -291,6 +293,19 @@ class moodle_page {
return $this->_blocks;
}
/**
* Please do not call this method directly, use the ->blocks syntax. @see __get().
* @return blocks_manager the blocks manager object for this page.
*/
public function get_requires() {
global $CFG;
if (is_null($this->_requires)) {
$this->_requires = new page_requirements_manager();
setup_core_javascript($this->_requires);
}
return $this->_requires;
}
/**
* PHP overloading magic to make the $PAGE->course syntax work by redirecting
* it to the corresponding $PAGE->get_course() method if there is one, and