Merge branch 'MDL-43856-master' of git://github.com/damyon/moodle

This commit is contained in:
Dan Poltawski 2014-04-07 16:58:46 +08:00
commit 4834cfdf59
34 changed files with 1377 additions and 118 deletions

View File

@ -72,6 +72,8 @@ Y.extend(AUTOLINKER, Y.Base, {
alertpanel = new M.core.alert({title:data.entries[key].concept,
message:definition, modal:false, yesLabel: M.util.get_string('ok', 'moodle')});
alertpanel.show();
Y.fire(M.core.event.FILTER_CONTENT_UPDATED, {nodes: (new Y.NodeList(alertpanel.get('boundingBox')))});
Y.Node.one('#id_yuialertconfirm-' + alertpanel.get('COUNT')).focus();
}
@ -141,6 +143,7 @@ M.filter_glossary.init_filter_autolinking = function(config) {
"json-parse",
"event-delegate",
"overlay",
"moodle-core-event",
"moodle-core-notification-alert"
]
});

View File

@ -1 +1 @@
YUI.add("moodle-filter_glossary-autolinker",function(e,t){var n="Glossary filter autolinker",r="width",i="height",s="menubar",o="location",u="scrollbars",a="resizable",f="toolbar",l="status",c="directories",h="fullscreen",p="dependent",d;d=function(){d.superclass.constructor.apply(this,arguments)},e.extend(d,e.Base,{overlay:null,initializer:function(){var t=this;e.delegate("click",function(n){n.preventDefault();var r="",i=e.Node.create('<div id="glossaryfilteroverlayprogress"><img src="'+M.cfg.loadingicon+'" class="spinner" /></div>'),s=new e.Overlay({headerContent:r,bodyContent:i}),o,u;t.overlay=s,s.render(e.one(document.body)),o=this.getAttribute("href").replace("showentry.php","showentry_ajax.php"),u={method:"get",context:t,on:{success:function(e,t){this.display_callback(t.responseText)},failure:function(e,t){var n=t.statusText;M.cfg.developerdebug&&(t.statusText+=" ("+o+")"),this.display_callback("bodyContent",n)}}},e.io(o,u)},e.one(document.body),"a.glossary.autolink.concept")},display_callback:function(t){var n,r,i;try{n=e.JSON.parse(t);if(n.success){this.overlay.hide();for(r in n.entries)definition=n.entries[r].definition+n.entries[r].attachments,i=new M.core.alert({title:n.entries[r].concept,message:definition,modal:!1,yesLabel:M.util.get_string("ok","moodle")}),i.show(),e.Node.one("#id_yuialertconfirm-"+i.get("COUNT")).focus();return!0}n.error&&new M.core.ajaxException(n)}catch(s){new M.core.exception(s)}return!1}},{NAME:n,ATTRS:{url:{validator:e.Lang.isString,value:M.cfg.wwwroot+"/mod/glossary/showentry.php"},name:{validator:e.Lang.isString,value:"glossaryconcept"},options:{getter:function(){return{width:this.get(r),height:this.get(i),menubar:this.get(s),location:this.get(o),scrollbars:this.get(u),resizable:this.get(a),toolbar:this.get(f),status:this.get(l),directories:this.get(c),fullscreen:this.get(h),dependent:this.get(p)}},readOnly:!0},width:{value:600},height:{value:450},menubar:{value:!1},location:{value:!1},scrollbars:{value:!0},resizable:{value:!0},toolbar:{value:!0},status:{value:!0},directories:{value:!1},fullscreen:{value:!1},dependent:{value:!0}}}),M.filter_glossary=M.filter_glossary||{},M.filter_glossary.init_filter_autolinking=function(e){return new d(e)}},"@VERSION@",{requires:["base","node","io-base","json-parse","event-delegate","overlay","moodle-core-notification-alert"]});
YUI.add("moodle-filter_glossary-autolinker",function(e,t){var n="Glossary filter autolinker",r="width",i="height",s="menubar",o="location",u="scrollbars",a="resizable",f="toolbar",l="status",c="directories",h="fullscreen",p="dependent",d;d=function(){d.superclass.constructor.apply(this,arguments)},e.extend(d,e.Base,{overlay:null,initializer:function(){var t=this;e.delegate("click",function(n){n.preventDefault();var r="",i=e.Node.create('<div id="glossaryfilteroverlayprogress"><img src="'+M.cfg.loadingicon+'" class="spinner" /></div>'),s=new e.Overlay({headerContent:r,bodyContent:i}),o,u;t.overlay=s,s.render(e.one(document.body)),o=this.getAttribute("href").replace("showentry.php","showentry_ajax.php"),u={method:"get",context:t,on:{success:function(e,t){this.display_callback(t.responseText)},failure:function(e,t){var n=t.statusText;M.cfg.developerdebug&&(t.statusText+=" ("+o+")"),this.display_callback("bodyContent",n)}}},e.io(o,u)},e.one(document.body),"a.glossary.autolink.concept")},display_callback:function(t){var n,r,i;try{n=e.JSON.parse(t);if(n.success){this.overlay.hide();for(r in n.entries)definition=n.entries[r].definition+n.entries[r].attachments,i=new M.core.alert({title:n.entries[r].concept,message:definition,modal:!1,yesLabel:M.util.get_string("ok","moodle")}),i.show(),e.fire(M.core.event.FILTER_CONTENT_UPDATED,{nodes:new e.NodeList(i.get("boundingBox"))}),e.Node.one("#id_yuialertconfirm-"+i.get("COUNT")).focus();return!0}n.error&&new M.core.ajaxException(n)}catch(s){new M.core.exception(s)}return!1}},{NAME:n,ATTRS:{url:{validator:e.Lang.isString,value:M.cfg.wwwroot+"/mod/glossary/showentry.php"},name:{validator:e.Lang.isString,value:"glossaryconcept"},options:{getter:function(){return{width:this.get(r),height:this.get(i),menubar:this.get(s),location:this.get(o),scrollbars:this.get(u),resizable:this.get(a),toolbar:this.get(f),status:this.get(l),directories:this.get(c),fullscreen:this.get(h),dependent:this.get(p)}},readOnly:!0},width:{value:600},height:{value:450},menubar:{value:!1},location:{value:!1},scrollbars:{value:!0},resizable:{value:!0},toolbar:{value:!0},status:{value:!0},directories:{value:!1},fullscreen:{value:!1},dependent:{value:!0}}}),M.filter_glossary=M.filter_glossary||{},M.filter_glossary.init_filter_autolinking=function(e){return new d(e)}},"@VERSION@",{requires:["base","node","io-base","json-parse","event-delegate","overlay","moodle-core-event","moodle-core-notification-alert"]});

View File

@ -72,6 +72,8 @@ Y.extend(AUTOLINKER, Y.Base, {
alertpanel = new M.core.alert({title:data.entries[key].concept,
message:definition, modal:false, yesLabel: M.util.get_string('ok', 'moodle')});
alertpanel.show();
Y.fire(M.core.event.FILTER_CONTENT_UPDATED, {nodes: (new Y.NodeList(alertpanel.get('boundingBox')))});
Y.Node.one('#id_yuialertconfirm-' + alertpanel.get('COUNT')).focus();
}
@ -141,6 +143,7 @@ M.filter_glossary.init_filter_autolinking = function(config) {
"json-parse",
"event-delegate",
"overlay",
"moodle-core-event",
"moodle-core-notification-alert"
]
});

View File

@ -70,6 +70,8 @@ Y.extend(AUTOLINKER, Y.Base, {
alertpanel = new M.core.alert({title:data.entries[key].concept,
message:definition, modal:false, yesLabel: M.util.get_string('ok', 'moodle')});
alertpanel.show();
Y.fire(M.core.event.FILTER_CONTENT_UPDATED, {nodes: (new Y.NodeList(alertpanel.get('boundingBox')))});
Y.Node.one('#id_yuialertconfirm-' + alertpanel.get('COUNT')).focus();
}

View File

@ -7,6 +7,7 @@
"json-parse",
"event-delegate",
"overlay",
"moodle-core-event",
"moodle-core-notification-alert"
]
}

View File

@ -0,0 +1,171 @@
<?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/>.
/**
* This filter provides automatic support for MathJax
*
* @package filter_mathjaxloader
* @copyright 2013 Damyon Wiese (damyon@moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Mathjax filtering
*/
class filter_mathjaxloader extends moodle_text_filter {
/*
* Perform a mapping of the moodle language code to the equivalent for MathJax.
*
* @param string $moodlelangcode - The moodle language code - e.g. en_pirate
* @return string The MathJax language code.
*/
public function map_language_code($moodlelangcode) {
$mathjaxlangcodes = array('br',
'cdo',
'cs',
'da',
'de',
'en',
'eo',
'es',
'fa',
'fi',
'fr',
'gl',
'he',
'ia',
'it',
'ja',
'ko',
'lb',
'mk',
'nl',
'oc',
'pl',
'pt',
'pt-br',
'ru',
'sl',
'sv',
'tr',
'uk',
'zh-hans');
$exceptions = array('cz' => 'cs');
// First see if this is an exception.
if (isset($exceptions[$moodlelangcode])) {
$moodlelangcode = $exceptions[$moodlelangcode];
}
// Now look for an exact lang string match.
if (in_array($moodlelangcode, $mathjaxlangcodes)) {
return $moodlelangcode;
}
// Now try shortening the moodle lang string.
$moodlelangcode = preg_replace('/-.*/', '', $moodlelangcode);
// Look for a match on the shortened string.
if (in_array($moodlelangcode, $mathjaxlangcodes)) {
return $moodlelangcode;
}
// All failed - use english.
return 'en';
}
/*
* Add the javascript to enable mathjax processing on this page.
*
* @param moodle_page $page The current page.
* @param context $context The current context.
*/
public function setup($page, $context) {
global $CFG;
// This only requires execution once per request.
static $jsinitialised = false;
if (empty($jsinitialised)) {
if (strpos($CFG->httpswwwroot, 'https:') === 0) {
$url = get_config('filter_mathjaxloader', 'httpsurl');
} else {
$url = get_config('filter_mathjaxloader', 'httpurl');
}
$lang = $this->map_language_code(current_language());
$url = new moodle_url($url, array('delayStartupUntil' => 'configured'));
$moduleconfig = array(
'name' => 'mathjax',
'fullpath' => $url
);
$page->requires->js_module($moduleconfig);
$config = get_config('filter_mathjaxloader', 'mathjaxconfig');
$params = array('mathjaxconfig' => $config, 'lang' => $lang);
$page->requires->yui_module('moodle-filter_mathjaxloader-loader', 'M.filter_mathjaxloader.configure', array($params));
$jsinitialised = true;
}
}
/*
* This function wraps the filtered text in a span, that mathjaxloader is configured to process.
*
* @param string $text The text to filter.
* @param array $options The filter options.
*/
public function filter($text, array $options = array()) {
global $PAGE;
$legacy = get_config('filter_mathjaxloader', 'texfiltercompatibility');
$extradelimiters = explode(',', get_config('filter_mathjaxloader', 'additionaldelimiters'));
if ($legacy) {
// This replaces any of the tex filter maths delimiters with the default for inline maths in MathJAX "\( blah \)".
// E.g. "<tex.*> blah </tex>".
$text = preg_replace('|<(/?) *tex( [^>]*)?>|u', '[\1tex]', $text);
// E.g. "[tex.*] blah [/tex]".
$text = str_replace('[tex]', '\\(', $text);
$text = str_replace('[/tex]', '\\)', $text);
// E.g. "$$ blah $$".
$text = preg_replace('|\$\$[\S\s]\$\$|u', '\\(\1\\)', $text);
// E.g. "\[ blah \]".
$text = str_replace('\\[', '\\(', $text);
$text = str_replace('\\]', '\\)', $text);
}
$hasinline = strpos($text, '\\(') !== false && strpos($text, '\\)') !== false;
$hasdisplay = (strpos($text, '$$') !== false) ||
(strpos($text, '\\[') !== false && strpos($text, '\\]') !== false);
$hasextra = false;
foreach ($extradelimiters as $extra) {
if ($extra && strpos($text, $extra) !== false) {
$hasextra = true;
break;
}
}
if ($hasinline || $hasdisplay || $hasextra) {
$PAGE->requires->yui_module('moodle-filter_mathjaxloader-loader', 'M.filter_mathjaxloader.typeset');
return '<span class="nolink"><span class="filter_mathjaxloader_equation">' . $text . '</span></span>';
}
return $text;
}
}

View File

@ -0,0 +1,45 @@
<?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/>.
/**
* Strings for component 'filter_mathjaxloader', language 'en'.
*
* @package filter_mathjaxloader
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['filtername'] = 'MathJax';
$string['additionaldelimiters'] = 'Additional equation delimiters';
$string['additionaldelimiters_help'] = 'MathJax filter parses text for equations contained within delimiter characters.
The list of recognised delimiter characters can be added to here (e.g. AsciiMath uses `). Delimiters can contain multiple characters and multiple delimiters can be separated with commas.';
$string['httpurl'] = 'HTTP MathJax URL';
$string['httpurl_help'] = 'Full URL to MathJax library. Used when the page is loaded via http.';
$string['httpsurl'] = 'HTTPS MathJax URL';
$string['httpsurl_help'] = 'Full URL to MathJax library. Used when the page is loaded via https (secure). ';
$string['texfiltercompatibility'] = 'Tex filter compatibility';
$string['texfiltercompatibility_help'] = 'The MathJax filter can be used as a replacement for the Tex filter.
To support all the delimiters supported by the Tex filter MathJax will be configured to display all equations "inline" with the tex.';
$string['localinstall'] = 'Local MathJax installation';
$string['localinstall_help'] = 'The default MathJAX configuration uses the CDN version of MathJAX, but MathJAX can be installed locally if required.
Some reasons this might be useful are to save on bandwidth - or because of local proxy restrictions.
To use a local installation of MathJAX, first download the full MathJax library from http://www.mathjax.org/. Then install it on a web server. Finally update the MathJax filter settings httpurl and/or httpsurl to point to the local MathJax.js url.';
$string['mathjaxsettings'] = 'MathJax configuration';
$string['mathjaxsettings_desc'] = 'The default MathJAX configuration should be appropriate for most users, but MathJax is highly configurable and any of the standard MathJax configuration options can be added here.';

View File

@ -0,0 +1,15 @@
Description of MathJAX library integration in Moodle
=========================================================================================
License: Apache 2.0
Source: http://www.mathjax.org
Moodle maintainer: Damyon Wiese
=========================================================================================
This library is not shipped with Moodle, but this filter is provided, which can be used to
correctly load MathJax into a page from the CDN. Alternatively you can download the entire
library and install it locally, then use this filter to load that local version.
The only changes required to this filter to handle different MathJax versions is to update
the default CDN urls in settings.php - and update the list of language mappings - in filter.php.

View File

@ -0,0 +1,86 @@
<?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/>.
/**
* MathJAX filter settings
*
* @package filter_mathjaxloader
* @copyright 2014 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die;
if ($ADMIN->fulltree) {
$item = new admin_setting_heading('filter_mathjaxloader/localinstall',
new lang_string('localinstall', 'filter_mathjaxloader'),
new lang_string('localinstall_help', 'filter_mathjaxloader'));
$settings->add($item);
$item = new admin_setting_configtext('filter_mathjaxloader/httpurl',
new lang_string('httpurl', 'filter_mathjaxloader'),
new lang_string('httpurl_help', 'filter_mathjaxloader'),
'http://cdn.mathjax.org/mathjax/2.3-latest/MathJax.js',
PARAM_RAW);
$settings->add($item);
$item = new admin_setting_configtext('filter_mathjaxloader/httpsurl',
new lang_string('httpsurl', 'filter_mathjaxloader'),
new lang_string('httpsurl_help', 'filter_mathjaxloader'),
'https://c328740.ssl.cf1.rackcdn.com/mathjax/2.3-latest/MathJax.js',
PARAM_RAW);
$settings->add($item);
$item = new admin_setting_configcheckbox('filter_mathjaxloader/texfiltercompatibility',
new lang_string('texfiltercompatibility', 'filter_mathjaxloader'),
new lang_string('texfiltercompatibility_help', 'filter_mathjaxloader'),
0);
$settings->add($item);
$default = '
MathJax.Hub.Config({
config: ["MMLorHTML.js", "Safe.js"],
jax: ["input/TeX","input/MathML","output/HTML-CSS","output/NativeMML"],
extensions: ["tex2jax.js","mml2jax.js","MathMenu.js","MathZoom.js"],
TeX: {
extensions: ["AMSmath.js","AMSsymbols.js","noErrors.js","noUndefined.js"]
},
menuSettings: {
zoom: "Double-Click",
mpContext: true,
mpMouse: true
},
errorSettings: { message: ["!"] },
skipStartupTypeset: true,
messageStyle: "none"
});
';
$item = new admin_setting_configtextarea('filter_mathjaxloader/mathjaxconfig',
new lang_string('mathjaxsettings','filter_mathjaxloader'),
new lang_string('mathjaxsettings_desc', 'filter_mathjaxloader'),
$default);
$settings->add($item);
$item = new admin_setting_configtext('filter_mathjaxloader/additionaldelimiters',
new lang_string('additionaldelimiters', 'filter_mathjaxloader'),
new lang_string('additionaldelimiters_help', 'filter_mathjaxloader'),
'',
PARAM_RAW);
$settings->add($item);
}

View File

@ -0,0 +1,3 @@
.jsenabled #MathJax_ZoomFrame {
position: absolute;
}

View File

@ -0,0 +1,29 @@
<?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/>.
/**
* MathJax filter version information
*
* @package filter_mathjaxloader
* @copyright 2014 Damyon Wiese (damyon@moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2013110500;
$plugin->requires = 2013110500; // Requires this Moodle version
$plugin->component= 'filter_mathjaxloader';

View File

@ -0,0 +1,46 @@
YUI.add('moodle-filter_mathjax-loader', function (Y, NAME) {
// 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/>.
/**
* Mathjax JS Loader.
*
* @package filter_mathjax
* @copyright 2014 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
M.filter_mathjax = M.filter_mathjax || {
init : function(params) {
// Add an inline configuration tag to the head.
var config = Y.Node.create('<script type="text/x-mathjax-config"></script>');
config.setHTML(params.mathjaxconfig);
Y.one('head').append(config);
// Load the mathjax library.
Y.Get.js(params.mathjaxurl, function() {
// Tell mathjax to call back for the language files.
debugger;
MathJax.Localization.addTranslation(params.lang,null,{
menuTitle: params.langdescription,
directory: M.cfg.wwwroot + "/filter/mathjax/language.php?lang="
});
});
}
};
}, '@VERSION@', {"requires": ["get", "node"]});

View File

@ -0,0 +1 @@
YUI.add("moodle-filter_mathjax-loader",function(e,t){M.filter_mathjax=M.filter_mathjax||{init:function(t){var n=e.Node.create('<script type="text/x-mathjax-config"></script>');n.setHTML(t.mathjaxconfig),e.one("head").append(n),e.Get.js(t.mathjaxurl,function(){debugger;MathJax.Localization.addTranslation(t.lang,null,{menuTitle:t.langdescription,directory:M.cfg.wwwroot+"/filter/mathjax/language.php?lang="})})}}},"@VERSION@",{requires:["get","node"]});

View File

@ -0,0 +1,46 @@
YUI.add('moodle-filter_mathjax-loader', function (Y, NAME) {
// 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/>.
/**
* Mathjax JS Loader.
*
* @package filter_mathjax
* @copyright 2014 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
M.filter_mathjax = M.filter_mathjax || {
init : function(params) {
// Add an inline configuration tag to the head.
var config = Y.Node.create('<script type="text/x-mathjax-config"></script>');
config.setHTML(params.mathjaxconfig);
Y.one('head').append(config);
// Load the mathjax library.
Y.Get.js(params.mathjaxurl, function() {
// Tell mathjax to call back for the language files.
debugger;
MathJax.Localization.addTranslation(params.lang,null,{
menuTitle: params.langdescription,
directory: M.cfg.wwwroot + "/filter/mathjax/language.php?lang="
});
});
}
};
}, '@VERSION@', {"requires": ["get", "node"]});

View File

@ -0,0 +1,120 @@
YUI.add('moodle-filter_mathjaxloader-loader', function (Y, NAME) {
// 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/>.
/**
* Mathjax JS Loader.
*
* @package filter_mathjaxloader
* @copyright 2014 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
M.filter_mathjaxloader = M.filter_mathjaxloader || {
/**
* The users current language - this can't be set until MathJax is loaded - so we need to store it.
* @property _lang
* @type String
* @default ''
* @private
*/
_lang: '',
/**
* Boolean used to prevent configuring MathJax twice.
* @property _configured
* @type Boolean
* @default ''
* @private
*/
_configured: false,
/**
* Called by the filter when it is active on any page.
* This does not load MathJAX yet - it addes the configuration to the head incase it gets loaded later.
* It also subscribes to the filter-content-updated event so MathJax can respond to content loaded by Ajax.
*
* @method typeset
* @param {Object} params List of configuration params containing mathjaxconfig (text) and lang
*/
configure: function(params) {
// Add a js configuration object to the head.
// See "http://docs.mathjax.org/en/latest/dynamic.html#ajax-mathjax"
var script = document.createElement("script");
script.type = "text/x-mathjax-config";
script[(window.opera ? "innerHTML" : "text")] = params.mathjaxconfig;
document.getElementsByTagName("head")[0].appendChild(script);
// Save the lang config until MathJax is actually loaded.
this._lang = params.lang;
// Listen for events triggered when new text is added to a page that needs
// processing by a filter.
Y.on(M.core.event.FILTER_CONTENT_UPDATED, this.contentUpdated, this);
},
/**
* Set the correct language for the MathJax menus. Only do this once.
*
* @method setLocale
* @private
*/
_setLocale: function() {
if (!this._configured) {
MathJax.Localization.setLocale(this._lang);
MathJax.Hub.Configured();
this._configured = true;
}
},
/**
* Called by the filter when an equation is found while rendering the page.
*
* @method typeset
*/
typeset: function() {
if (!this._configured) {
var self = this;
Y.use('mathjax', function() {
self._setLocale();
Y.all('.filter_mathjaxloader_equation').each(function(node) {
MathJax.Hub.Queue(["Typeset", MathJax.Hub, node.getDOMNode()]);
});
});
}
},
/**
* Handle content updated events - typeset the new content.
* @method contentUpdated
* @param Y.Event - Custom event with "nodes" indicating the root of the updated nodes.
*/
contentUpdated: function(event) {
var self = this;
Y.use('mathjax', function() {
self._setLocale();
event.nodes.each(function (node) {
node.all('.filter_mathjaxloader_equation').each(function(node) {
MathJax.Hub.Queue(["Typeset", MathJax.Hub, node.getDOMNode()]);
});
});
});
}
};
}, '@VERSION@', {"requires": ["moodle-core-event"]});

View File

@ -0,0 +1 @@
YUI.add("moodle-filter_mathjaxloader-loader",function(e,t){M.filter_mathjaxloader=M.filter_mathjaxloader||{_lang:"",_configured:!1,configure:function(t){var n=document.createElement("script");n.type="text/x-mathjax-config",n[window.opera?"innerHTML":"text"]=t.mathjaxconfig,document.getElementsByTagName("head")[0].appendChild(n),this._lang=t.lang,e.on(M.core.event.FILTER_CONTENT_UPDATED,this.contentUpdated,this)},_setLocale:function(){this._configured||(MathJax.Localization.setLocale(this._lang),MathJax.Hub.Configured(),this._configured=!0)},typeset:function(){if(!this._configured){var t=this;e.use("mathjax",function(){t._setLocale(),e.all(".filter_mathjaxloader_equation").each(function(e){MathJax.Hub.Queue(["Typeset",MathJax.Hub,e.getDOMNode()])})})}},contentUpdated:function(t){var n=this;e.use("mathjax",function(){n._setLocale(),t.nodes.each(function(e){e.all(".filter_mathjaxloader_equation").each(function(e){MathJax.Hub.Queue(["Typeset",MathJax.Hub,e.getDOMNode()])})})})}}},"@VERSION@",{requires:["moodle-core-event"]});

View File

@ -0,0 +1,120 @@
YUI.add('moodle-filter_mathjaxloader-loader', function (Y, NAME) {
// 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/>.
/**
* Mathjax JS Loader.
*
* @package filter_mathjaxloader
* @copyright 2014 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
M.filter_mathjaxloader = M.filter_mathjaxloader || {
/**
* The users current language - this can't be set until MathJax is loaded - so we need to store it.
* @property _lang
* @type String
* @default ''
* @private
*/
_lang: '',
/**
* Boolean used to prevent configuring MathJax twice.
* @property _configured
* @type Boolean
* @default ''
* @private
*/
_configured: false,
/**
* Called by the filter when it is active on any page.
* This does not load MathJAX yet - it addes the configuration to the head incase it gets loaded later.
* It also subscribes to the filter-content-updated event so MathJax can respond to content loaded by Ajax.
*
* @method typeset
* @param {Object} params List of configuration params containing mathjaxconfig (text) and lang
*/
configure: function(params) {
// Add a js configuration object to the head.
// See "http://docs.mathjax.org/en/latest/dynamic.html#ajax-mathjax"
var script = document.createElement("script");
script.type = "text/x-mathjax-config";
script[(window.opera ? "innerHTML" : "text")] = params.mathjaxconfig;
document.getElementsByTagName("head")[0].appendChild(script);
// Save the lang config until MathJax is actually loaded.
this._lang = params.lang;
// Listen for events triggered when new text is added to a page that needs
// processing by a filter.
Y.on(M.core.event.FILTER_CONTENT_UPDATED, this.contentUpdated, this);
},
/**
* Set the correct language for the MathJax menus. Only do this once.
*
* @method setLocale
* @private
*/
_setLocale: function() {
if (!this._configured) {
MathJax.Localization.setLocale(this._lang);
MathJax.Hub.Configured();
this._configured = true;
}
},
/**
* Called by the filter when an equation is found while rendering the page.
*
* @method typeset
*/
typeset: function() {
if (!this._configured) {
var self = this;
Y.use('mathjax', function() {
self._setLocale();
Y.all('.filter_mathjaxloader_equation').each(function(node) {
MathJax.Hub.Queue(["Typeset", MathJax.Hub, node.getDOMNode()]);
});
});
}
},
/**
* Handle content updated events - typeset the new content.
* @method contentUpdated
* @param Y.Event - Custom event with "nodes" indicating the root of the updated nodes.
*/
contentUpdated: function(event) {
var self = this;
Y.use('mathjax', function() {
self._setLocale();
event.nodes.each(function (node) {
node.all('.filter_mathjaxloader_equation').each(function(node) {
MathJax.Hub.Queue(["Typeset", MathJax.Hub, node.getDOMNode()]);
});
});
});
}
};
}, '@VERSION@', {"requires": ["moodle-core-event"]});

View File

@ -0,0 +1,10 @@
{
"name": "moodle-filter_mathjaxloader-loader",
"builds": {
"moodle-filter_mathjaxloader-loader": {
"jsfiles": [
"loader.js"
]
}
}
}

View File

@ -0,0 +1,115 @@
// 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/>.
/**
* Mathjax JS Loader.
*
* @package filter_mathjaxloader
* @copyright 2014 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
M.filter_mathjaxloader = M.filter_mathjaxloader || {
/**
* The users current language - this can't be set until MathJax is loaded - so we need to store it.
* @property _lang
* @type String
* @default ''
* @private
*/
_lang: '',
/**
* Boolean used to prevent configuring MathJax twice.
* @property _configured
* @type Boolean
* @default ''
* @private
*/
_configured: false,
/**
* Called by the filter when it is active on any page.
* This does not load MathJAX yet - it addes the configuration to the head incase it gets loaded later.
* It also subscribes to the filter-content-updated event so MathJax can respond to content loaded by Ajax.
*
* @method typeset
* @param {Object} params List of configuration params containing mathjaxconfig (text) and lang
*/
configure: function(params) {
// Add a js configuration object to the head.
// See "http://docs.mathjax.org/en/latest/dynamic.html#ajax-mathjax"
var script = document.createElement("script");
script.type = "text/x-mathjax-config";
script[(window.opera ? "innerHTML" : "text")] = params.mathjaxconfig;
document.getElementsByTagName("head")[0].appendChild(script);
// Save the lang config until MathJax is actually loaded.
this._lang = params.lang;
// Listen for events triggered when new text is added to a page that needs
// processing by a filter.
Y.on(M.core.event.FILTER_CONTENT_UPDATED, this.contentUpdated, this);
},
/**
* Set the correct language for the MathJax menus. Only do this once.
*
* @method setLocale
* @private
*/
_setLocale: function() {
if (!this._configured) {
MathJax.Localization.setLocale(this._lang);
MathJax.Hub.Configured();
this._configured = true;
}
},
/**
* Called by the filter when an equation is found while rendering the page.
*
* @method typeset
*/
typeset: function() {
if (!this._configured) {
var self = this;
Y.use('mathjax', function() {
self._setLocale();
Y.all('.filter_mathjaxloader_equation').each(function(node) {
MathJax.Hub.Queue(["Typeset", MathJax.Hub, node.getDOMNode()]);
});
});
}
},
/**
* Handle content updated events - typeset the new content.
* @method contentUpdated
* @param Y.Event - Custom event with "nodes" indicating the root of the updated nodes.
*/
contentUpdated: function(event) {
var self = this;
Y.use('mathjax', function() {
self._setLocale();
event.nodes.each(function (node) {
node.all('.filter_mathjaxloader_equation').each(function(node) {
MathJax.Hub.Queue(["Typeset", MathJax.Hub, node.getDOMNode()]);
});
});
});
}
};

View File

@ -0,0 +1,7 @@
{
"moodle-filter_mathjaxloader-loader": {
"requires": [
"moodle-core-event"
]
}
}

View File

@ -1012,7 +1012,7 @@ class core_plugin_manager {
'filter' => array(
'activitynames', 'algebra', 'censor', 'emailprotect',
'emoticon', 'mediaplugin', 'multilang', 'tex', 'tidy',
'emoticon', 'mathjaxloader', 'mediaplugin', 'multilang', 'tex', 'tidy',
'urltolink', 'data', 'glossary'
),

View File

@ -81,6 +81,8 @@ function atto_equation_params_for_js($elementid, $options, $fpoptions) {
'elements' => get_config('atto_equation', 'librarygroup4'),
));
return array('texfilteractive' => $texfilteractive, 'contextid' => $context->id, 'library' => $library,
'texdocsurl' => get_docs_url('Using_TeX_Notation'));
return array('texfilteractive' => $texfilteractive,
'contextid' => $context->id,
'library' => $library,
'texdocsurl' => get_docs_url('Using_TeX_Notation'));
}

View File

@ -71,9 +71,9 @@ if ($ADMIN->fulltree) {
\neq
';
$setting = new admin_setting_configtextarea('atto_equation/librarygroup1',
$name,
$desc,
$default);
$name,
$desc,
$default);
$settings->add($setting);
// Group 2
@ -96,9 +96,9 @@ if ($ADMIN->fulltree) {
\Leftrightarrow
';
$setting = new admin_setting_configtextarea('atto_equation/librarygroup2',
$name,
$desc,
$default);
$name,
$desc,
$default);
$settings->add($setting);
// Group 3
@ -141,9 +141,9 @@ if ($ADMIN->fulltree) {
\Omega
';
$setting = new admin_setting_configtextarea('atto_equation/librarygroup3',
$name,
$desc,
$default);
$name,
$desc,
$default);
$settings->add($setting);
// Group 4
@ -161,8 +161,9 @@ if ($ADMIN->fulltree) {
\left| \begin{matrix} a_1 & a_2 \\ a_3 & a_4 \end{matrix} \right|
';
$setting = new admin_setting_configtextarea('atto_equation/librarygroup4',
$name,
$desc,
$default);
$name,
$desc,
$default);
$settings->add($setting);
}

View File

@ -48,6 +48,10 @@ var COMPONENTNAME = 'atto_equation',
SUBMIT: '.' + CSS.SUBMIT,
LIBRARY_BUTTON: '.' + CSS.LIBRARY + ' button'
},
DELIMITERS = {
START: '\\(',
END: '\\)'
},
TEMPLATES = {
FORM: '' +
'<form class="atto_form">' +
@ -72,7 +76,7 @@ var COMPONENTNAME = 'atto_equation',
'{{#each library}}' +
'<div id="{{elementid}}_{{../CSS.LIBRARY_GROUP_PREFIX}}{{@key}}">' +
'{{#split "\n" elements}}' +
'<button data-tex="{{this}}" title="{{this}}">$${{this}}$$</button>' +
'<button data-tex="{{this}}" title="{{this}}">{{../../DELIMITERS.START}}{{this}}{{../../DELIMITERS.END}}</button>' +
'{{/split}}' +
'</div>' +
'{{/each}}' +
@ -111,7 +115,17 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
*/
_content: null,
/**
* The source equation we are editing in the text.
*
* @property _sourceEquation
* @type String
* @private
*/
_sourceEquation: '',
initializer: function() {
// If there is a tex filter active - enable this button.
if (this.get('texfilteractive')) {
// Add the button to the toolbar.
this.addButton({
@ -127,7 +141,14 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
this.unHighlightButtons();
}
}, this);
// We need to convert these to a non dom node based format.
this.editor.all('tex').each(function (texNode) {
var replacement = Y.Node.create('<span>' + DELIMITERS.START + ' ' + texNode.get('text') + ' ' + DELIMITERS.END + '</span>');
texNode.replace(replacement);
});
}
},
/**
@ -160,6 +181,8 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
tabview.render();
dialogue.show();
// Trigger any JS filters to reprocess the new nodes.
Y.fire(M.core.event.FILTER_CONTENT_UPDATED, {nodes: (new Y.NodeList(dialogue.get('boundingBox')))});
var equation = this._resolveEquation();
if (equation) {
@ -181,7 +204,8 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
// Find the equation in the surrounding text.
var selectedNode = this.get('host').getSelectionParentNode(),
text,
equation;
equation,
patterns = [], i;
// Note this is a document fragment and YUI doesn't like them.
if (!selectedNode) {
@ -190,14 +214,27 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
text = Y.one(selectedNode).get('text');
// We use space or not space because . does not match new lines.
pattern = /\$\$[\S\s]*\$\$/;
equation = pattern.exec(text);
if (equation && equation.length) {
equation = equation.pop();
// Replace the equation.
equation = equation.substring(2, equation.length - 2);
return equation;
// $$ blah $$.
patterns.push(/\$\$([\S\s]*)\$\$/);
// E.g. "\( blah \)".
patterns.push(/\\\(([\S\s]*)\\\)/);
// E.g. "\[ blah \]".
patterns.push(/\\\[([\S\s]*)\\\]/);
// E.g. "[tex] blah [/tex]".
patterns.push(/\[tex\]([\S\s]*)\[\/tex\]/);
for (i = 0; i < patterns.length; i++) {
pattern = patterns[i];
equation = pattern.exec(text);
if (equation && equation.length) {
// Remember the inner match so we can replace it later.
this.sourceEquation = equation = equation[1];
return equation;
}
}
this.sourceEquation = '';
return false;
},
@ -212,11 +249,10 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
var input,
selectedNode,
text,
pattern,
equation,
value;
value,
host;
var host = this.get('host');
host = this.get('host');
e.preventDefault();
this.getDialogue({
@ -229,18 +265,16 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
if (value !== '') {
host.setSelection(this._currentSelection);
value = '$$ ' + value.trim() + ' $$';
selectedNode = Y.one(host.getSelectionParentNode());
text = selectedNode.get('text');
pattern = /\$\$[\S\s]*\$\$/;
equation = pattern.exec(text);
if (equation && equation.length) {
if (this.sourceEquation.length) {
// Replace the equation.
equation = equation.pop();
text = text.replace(equation, '$$' + value + '$$');
selectedNode = Y.one(host.getSelectionParentNode());
text = selectedNode.get('text');
text = text.replace(this.sourceEquation, value);
selectedNode.set('text', text);
} else {
// Insert the new equation.
value = DELIMITERS.START + ' ' + value + ' ' + DELIMITERS.END;
host.insertContentAtFocusPoint(value);
}
@ -249,6 +283,29 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
}
},
/**
* Smart throttle, only call a function every delay milli seconds,
* and always run the last call. Y.throttle does not work here,
* because it calls the function immediately, the first time, and then
* ignores repeated calls within X seconds. This does not guarantee
* that the last call will be executed (which is required here).
*
* @param {function} fn
* @param {Number} delay Delay in milliseconds
* @method _throttle
* @private
*/
_throttle: function(fn, delay) {
var timer = null;
return function () {
var context = this, args = arguments;
clearTimeout(timer);
timer = setTimeout(function () {
fn.apply(context, args);
}, delay);
};
},
/**
* Update the preview div to match the current equation.
*
@ -264,8 +321,8 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
currentPos = textarea.get('selectionStart'),
prefix = '',
cursorLatex = '\\square ',
isChar;
isChar,
params;
if (e) {
e.preventDefault();
@ -279,25 +336,33 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
while (equation.charAt(currentPos) === '\\' && currentPos > 0) {
currentPos -= 1;
}
isChar = /[\w\{\}]/;
isChar = /[a-zA-Z\{\}]/;
while (isChar.test(equation.charAt(currentPos)) && currentPos < equation.length) {
currentPos += 1;
}
// Save the cursor position - for insertion from the library.
this._lastCursorPos = currentPos;
equation = prefix + equation.substring(0, currentPos) + cursorLatex + equation.substring(currentPos);
var previewNode = this._content.one(SELECTORS.EQUATION_PREVIEW);
equation = DELIMITERS.START + ' ' + equation + ' ' + DELIMITERS.END;
// Make an ajax request to the filter.
url = M.cfg.wwwroot + '/lib/editor/atto/plugins/equation/ajax.php';
params = {
sesskey: M.cfg.sesskey,
contextid: this.get('contextid'),
action: 'filtertext',
text: '$$ ' + equation + ' $$'
text: equation
};
preview = Y.io(url, { sync: true,
data: params });
preview = Y.io(url, {
sync: true,
data: params
});
if (preview.status === 200) {
this._content.one(SELECTORS.EQUATION_PREVIEW).setHTML(preview.responseText);
previewNode.setHTML(preview.responseText);
Y.fire(M.core.event.FILTER_CONTENT_UPDATED, {nodes: (new Y.NodeList(previewNode))});
}
},
@ -322,9 +387,9 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
}));
this._content.one(SELECTORS.SUBMIT).on('click', this._setEquation, this);
this._content.one(SELECTORS.EQUATION_TEXT).on('valuechange', this._updatePreview, this);
this._content.one(SELECTORS.EQUATION_TEXT).on('mouseup', this._updatePreview, this);
this._content.one(SELECTORS.EQUATION_TEXT).on('keyup', this._updatePreview, this);
this._content.one(SELECTORS.EQUATION_TEXT).on('valuechange', this._throttle(this._updatePreview, 500), this);
this._content.one(SELECTORS.EQUATION_TEXT).on('mouseup', this._throttle(this._updatePreview, 500), this);
this._content.one(SELECTORS.EQUATION_TEXT).on('keyup', this._throttle(this._updatePreview, 500), this);
this._content.delegate('click', this._selectLibraryItem, SELECTORS.LIBRARY_BUTTON, this);
return this._content;
@ -392,7 +457,7 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
out = '';
parts = str.trim().split(delimiter);
while (parts.length > 0) {
current = parts.shift();
current = parts.shift().trim();
out += options.fn(current);
}
@ -402,7 +467,8 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
elementid: this.get('host').get('elementid'),
component: COMPONENTNAME,
library: library,
CSS: CSS
CSS: CSS,
DELIMITERS: DELIMITERS
});
var url = M.cfg.wwwroot + '/lib/editor/atto/plugins/equation/ajax.php';
@ -435,6 +501,7 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
texfilteractive: {
value: false
},
/**
* The contextid to use when generating this preview.
*
@ -464,8 +531,9 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
texdocsurl: {
value: null
}
}
});
}, '@VERSION@', {"requires": ["moodle-editor_atto-plugin", "io", "event-valuechange", "tabview"]});
}, '@VERSION@', {"requires": ["moodle-editor_atto-plugin", "moodle-core-event", "io", "event-valuechange", "tabview"]});

View File

@ -48,6 +48,10 @@ var COMPONENTNAME = 'atto_equation',
SUBMIT: '.' + CSS.SUBMIT,
LIBRARY_BUTTON: '.' + CSS.LIBRARY + ' button'
},
DELIMITERS = {
START: '\\(',
END: '\\)'
},
TEMPLATES = {
FORM: '' +
'<form class="atto_form">' +
@ -72,7 +76,7 @@ var COMPONENTNAME = 'atto_equation',
'{{#each library}}' +
'<div id="{{elementid}}_{{../CSS.LIBRARY_GROUP_PREFIX}}{{@key}}">' +
'{{#split "\n" elements}}' +
'<button data-tex="{{this}}" title="{{this}}">$${{this}}$$</button>' +
'<button data-tex="{{this}}" title="{{this}}">{{../../DELIMITERS.START}}{{this}}{{../../DELIMITERS.END}}</button>' +
'{{/split}}' +
'</div>' +
'{{/each}}' +
@ -111,7 +115,17 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
*/
_content: null,
/**
* The source equation we are editing in the text.
*
* @property _sourceEquation
* @type String
* @private
*/
_sourceEquation: '',
initializer: function() {
// If there is a tex filter active - enable this button.
if (this.get('texfilteractive')) {
// Add the button to the toolbar.
this.addButton({
@ -127,7 +141,14 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
this.unHighlightButtons();
}
}, this);
// We need to convert these to a non dom node based format.
this.editor.all('tex').each(function (texNode) {
var replacement = Y.Node.create('<span>' + DELIMITERS.START + ' ' + texNode.get('text') + ' ' + DELIMITERS.END + '</span>');
texNode.replace(replacement);
});
}
},
/**
@ -160,6 +181,8 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
tabview.render();
dialogue.show();
// Trigger any JS filters to reprocess the new nodes.
Y.fire(M.core.event.FILTER_CONTENT_UPDATED, {nodes: (new Y.NodeList(dialogue.get('boundingBox')))});
var equation = this._resolveEquation();
if (equation) {
@ -181,7 +204,8 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
// Find the equation in the surrounding text.
var selectedNode = this.get('host').getSelectionParentNode(),
text,
equation;
equation,
patterns = [], i;
// Note this is a document fragment and YUI doesn't like them.
if (!selectedNode) {
@ -190,14 +214,27 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
text = Y.one(selectedNode).get('text');
// We use space or not space because . does not match new lines.
pattern = /\$\$[\S\s]*\$\$/;
equation = pattern.exec(text);
if (equation && equation.length) {
equation = equation.pop();
// Replace the equation.
equation = equation.substring(2, equation.length - 2);
return equation;
// $$ blah $$.
patterns.push(/\$\$([\S\s]*)\$\$/);
// E.g. "\( blah \)".
patterns.push(/\\\(([\S\s]*)\\\)/);
// E.g. "\[ blah \]".
patterns.push(/\\\[([\S\s]*)\\\]/);
// E.g. "[tex] blah [/tex]".
patterns.push(/\[tex\]([\S\s]*)\[\/tex\]/);
for (i = 0; i < patterns.length; i++) {
pattern = patterns[i];
equation = pattern.exec(text);
if (equation && equation.length) {
// Remember the inner match so we can replace it later.
this.sourceEquation = equation = equation[1];
return equation;
}
}
this.sourceEquation = '';
return false;
},
@ -212,11 +249,10 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
var input,
selectedNode,
text,
pattern,
equation,
value;
value,
host;
var host = this.get('host');
host = this.get('host');
e.preventDefault();
this.getDialogue({
@ -229,18 +265,16 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
if (value !== '') {
host.setSelection(this._currentSelection);
value = '$$ ' + value.trim() + ' $$';
selectedNode = Y.one(host.getSelectionParentNode());
text = selectedNode.get('text');
pattern = /\$\$[\S\s]*\$\$/;
equation = pattern.exec(text);
if (equation && equation.length) {
if (this.sourceEquation.length) {
// Replace the equation.
equation = equation.pop();
text = text.replace(equation, '$$' + value + '$$');
selectedNode = Y.one(host.getSelectionParentNode());
text = selectedNode.get('text');
text = text.replace(this.sourceEquation, value);
selectedNode.set('text', text);
} else {
// Insert the new equation.
value = DELIMITERS.START + ' ' + value + ' ' + DELIMITERS.END;
host.insertContentAtFocusPoint(value);
}
@ -249,6 +283,29 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
}
},
/**
* Smart throttle, only call a function every delay milli seconds,
* and always run the last call. Y.throttle does not work here,
* because it calls the function immediately, the first time, and then
* ignores repeated calls within X seconds. This does not guarantee
* that the last call will be executed (which is required here).
*
* @param {function} fn
* @param {Number} delay Delay in milliseconds
* @method _throttle
* @private
*/
_throttle: function(fn, delay) {
var timer = null;
return function () {
var context = this, args = arguments;
clearTimeout(timer);
timer = setTimeout(function () {
fn.apply(context, args);
}, delay);
};
},
/**
* Update the preview div to match the current equation.
*
@ -264,8 +321,8 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
currentPos = textarea.get('selectionStart'),
prefix = '',
cursorLatex = '\\square ',
isChar;
isChar,
params;
if (e) {
e.preventDefault();
@ -279,25 +336,33 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
while (equation.charAt(currentPos) === '\\' && currentPos > 0) {
currentPos -= 1;
}
isChar = /[\w\{\}]/;
isChar = /[a-zA-Z\{\}]/;
while (isChar.test(equation.charAt(currentPos)) && currentPos < equation.length) {
currentPos += 1;
}
// Save the cursor position - for insertion from the library.
this._lastCursorPos = currentPos;
equation = prefix + equation.substring(0, currentPos) + cursorLatex + equation.substring(currentPos);
var previewNode = this._content.one(SELECTORS.EQUATION_PREVIEW);
equation = DELIMITERS.START + ' ' + equation + ' ' + DELIMITERS.END;
// Make an ajax request to the filter.
url = M.cfg.wwwroot + '/lib/editor/atto/plugins/equation/ajax.php';
params = {
sesskey: M.cfg.sesskey,
contextid: this.get('contextid'),
action: 'filtertext',
text: '$$ ' + equation + ' $$'
text: equation
};
preview = Y.io(url, { sync: true,
data: params });
preview = Y.io(url, {
sync: true,
data: params
});
if (preview.status === 200) {
this._content.one(SELECTORS.EQUATION_PREVIEW).setHTML(preview.responseText);
previewNode.setHTML(preview.responseText);
Y.fire(M.core.event.FILTER_CONTENT_UPDATED, {nodes: (new Y.NodeList(previewNode))});
}
},
@ -322,9 +387,9 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
}));
this._content.one(SELECTORS.SUBMIT).on('click', this._setEquation, this);
this._content.one(SELECTORS.EQUATION_TEXT).on('valuechange', this._updatePreview, this);
this._content.one(SELECTORS.EQUATION_TEXT).on('mouseup', this._updatePreview, this);
this._content.one(SELECTORS.EQUATION_TEXT).on('keyup', this._updatePreview, this);
this._content.one(SELECTORS.EQUATION_TEXT).on('valuechange', this._throttle(this._updatePreview, 500), this);
this._content.one(SELECTORS.EQUATION_TEXT).on('mouseup', this._throttle(this._updatePreview, 500), this);
this._content.one(SELECTORS.EQUATION_TEXT).on('keyup', this._throttle(this._updatePreview, 500), this);
this._content.delegate('click', this._selectLibraryItem, SELECTORS.LIBRARY_BUTTON, this);
return this._content;
@ -391,7 +456,7 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
out = '';
parts = str.trim().split(delimiter);
while (parts.length > 0) {
current = parts.shift();
current = parts.shift().trim();
out += options.fn(current);
}
@ -401,7 +466,8 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
elementid: this.get('host').get('elementid'),
component: COMPONENTNAME,
library: library,
CSS: CSS
CSS: CSS,
DELIMITERS: DELIMITERS
});
var url = M.cfg.wwwroot + '/lib/editor/atto/plugins/equation/ajax.php';
@ -434,6 +500,7 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
texfilteractive: {
value: false
},
/**
* The contextid to use when generating this preview.
*
@ -463,8 +530,9 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
texdocsurl: {
value: null
}
}
});
}, '@VERSION@', {"requires": ["moodle-editor_atto-plugin", "io", "event-valuechange", "tabview"]});
}, '@VERSION@', {"requires": ["moodle-editor_atto-plugin", "moodle-core-event", "io", "event-valuechange", "tabview"]});

View File

@ -46,6 +46,10 @@ var COMPONENTNAME = 'atto_equation',
SUBMIT: '.' + CSS.SUBMIT,
LIBRARY_BUTTON: '.' + CSS.LIBRARY + ' button'
},
DELIMITERS = {
START: '\\(',
END: '\\)'
},
TEMPLATES = {
FORM: '' +
'<form class="atto_form">' +
@ -70,7 +74,7 @@ var COMPONENTNAME = 'atto_equation',
'{{#each library}}' +
'<div id="{{elementid}}_{{../CSS.LIBRARY_GROUP_PREFIX}}{{@key}}">' +
'{{#split "\n" elements}}' +
'<button data-tex="{{this}}" title="{{this}}">$${{this}}$$</button>' +
'<button data-tex="{{this}}" title="{{this}}">{{../../DELIMITERS.START}}{{this}}{{../../DELIMITERS.END}}</button>' +
'{{/split}}' +
'</div>' +
'{{/each}}' +
@ -109,7 +113,17 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
*/
_content: null,
/**
* The source equation we are editing in the text.
*
* @property _sourceEquation
* @type String
* @private
*/
_sourceEquation: '',
initializer: function() {
// If there is a tex filter active - enable this button.
if (this.get('texfilteractive')) {
// Add the button to the toolbar.
this.addButton({
@ -125,7 +139,14 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
this.unHighlightButtons();
}
}, this);
// We need to convert these to a non dom node based format.
this.editor.all('tex').each(function (texNode) {
var replacement = Y.Node.create('<span>' + DELIMITERS.START + ' ' + texNode.get('text') + ' ' + DELIMITERS.END + '</span>');
texNode.replace(replacement);
});
}
},
/**
@ -158,6 +179,8 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
tabview.render();
dialogue.show();
// Trigger any JS filters to reprocess the new nodes.
Y.fire(M.core.event.FILTER_CONTENT_UPDATED, {nodes: (new Y.NodeList(dialogue.get('boundingBox')))});
var equation = this._resolveEquation();
if (equation) {
@ -179,7 +202,8 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
// Find the equation in the surrounding text.
var selectedNode = this.get('host').getSelectionParentNode(),
text,
equation;
equation,
patterns = [], i;
// Note this is a document fragment and YUI doesn't like them.
if (!selectedNode) {
@ -188,14 +212,27 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
text = Y.one(selectedNode).get('text');
// We use space or not space because . does not match new lines.
pattern = /\$\$[\S\s]*\$\$/;
equation = pattern.exec(text);
if (equation && equation.length) {
equation = equation.pop();
// Replace the equation.
equation = equation.substring(2, equation.length - 2);
return equation;
// $$ blah $$.
patterns.push(/\$\$([\S\s]*)\$\$/);
// E.g. "\( blah \)".
patterns.push(/\\\(([\S\s]*)\\\)/);
// E.g. "\[ blah \]".
patterns.push(/\\\[([\S\s]*)\\\]/);
// E.g. "[tex] blah [/tex]".
patterns.push(/\[tex\]([\S\s]*)\[\/tex\]/);
for (i = 0; i < patterns.length; i++) {
pattern = patterns[i];
equation = pattern.exec(text);
if (equation && equation.length) {
// Remember the inner match so we can replace it later.
this.sourceEquation = equation = equation[1];
return equation;
}
}
this.sourceEquation = '';
return false;
},
@ -210,11 +247,10 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
var input,
selectedNode,
text,
pattern,
equation,
value;
value,
host;
var host = this.get('host');
host = this.get('host');
e.preventDefault();
this.getDialogue({
@ -227,18 +263,16 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
if (value !== '') {
host.setSelection(this._currentSelection);
value = '$$ ' + value.trim() + ' $$';
selectedNode = Y.one(host.getSelectionParentNode());
text = selectedNode.get('text');
pattern = /\$\$[\S\s]*\$\$/;
equation = pattern.exec(text);
if (equation && equation.length) {
if (this.sourceEquation.length) {
// Replace the equation.
equation = equation.pop();
text = text.replace(equation, '$$' + value + '$$');
selectedNode = Y.one(host.getSelectionParentNode());
text = selectedNode.get('text');
text = text.replace(this.sourceEquation, value);
selectedNode.set('text', text);
} else {
// Insert the new equation.
value = DELIMITERS.START + ' ' + value + ' ' + DELIMITERS.END;
host.insertContentAtFocusPoint(value);
}
@ -247,6 +281,29 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
}
},
/**
* Smart throttle, only call a function every delay milli seconds,
* and always run the last call. Y.throttle does not work here,
* because it calls the function immediately, the first time, and then
* ignores repeated calls within X seconds. This does not guarantee
* that the last call will be executed (which is required here).
*
* @param {function} fn
* @param {Number} delay Delay in milliseconds
* @method _throttle
* @private
*/
_throttle: function(fn, delay) {
var timer = null;
return function () {
var context = this, args = arguments;
clearTimeout(timer);
timer = setTimeout(function () {
fn.apply(context, args);
}, delay);
};
},
/**
* Update the preview div to match the current equation.
*
@ -262,8 +319,8 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
currentPos = textarea.get('selectionStart'),
prefix = '',
cursorLatex = '\\square ',
isChar;
isChar,
params;
if (e) {
e.preventDefault();
@ -277,25 +334,33 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
while (equation.charAt(currentPos) === '\\' && currentPos > 0) {
currentPos -= 1;
}
isChar = /[\w\{\}]/;
isChar = /[a-zA-Z\{\}]/;
while (isChar.test(equation.charAt(currentPos)) && currentPos < equation.length) {
currentPos += 1;
}
// Save the cursor position - for insertion from the library.
this._lastCursorPos = currentPos;
equation = prefix + equation.substring(0, currentPos) + cursorLatex + equation.substring(currentPos);
var previewNode = this._content.one(SELECTORS.EQUATION_PREVIEW);
equation = DELIMITERS.START + ' ' + equation + ' ' + DELIMITERS.END;
// Make an ajax request to the filter.
url = M.cfg.wwwroot + '/lib/editor/atto/plugins/equation/ajax.php';
params = {
sesskey: M.cfg.sesskey,
contextid: this.get('contextid'),
action: 'filtertext',
text: '$$ ' + equation + ' $$'
text: equation
};
preview = Y.io(url, { sync: true,
data: params });
preview = Y.io(url, {
sync: true,
data: params
});
if (preview.status === 200) {
this._content.one(SELECTORS.EQUATION_PREVIEW).setHTML(preview.responseText);
previewNode.setHTML(preview.responseText);
Y.fire(M.core.event.FILTER_CONTENT_UPDATED, {nodes: (new Y.NodeList(previewNode))});
}
},
@ -320,9 +385,9 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
}));
this._content.one(SELECTORS.SUBMIT).on('click', this._setEquation, this);
this._content.one(SELECTORS.EQUATION_TEXT).on('valuechange', this._updatePreview, this);
this._content.one(SELECTORS.EQUATION_TEXT).on('mouseup', this._updatePreview, this);
this._content.one(SELECTORS.EQUATION_TEXT).on('keyup', this._updatePreview, this);
this._content.one(SELECTORS.EQUATION_TEXT).on('valuechange', this._throttle(this._updatePreview, 500), this);
this._content.one(SELECTORS.EQUATION_TEXT).on('mouseup', this._throttle(this._updatePreview, 500), this);
this._content.one(SELECTORS.EQUATION_TEXT).on('keyup', this._throttle(this._updatePreview, 500), this);
this._content.delegate('click', this._selectLibraryItem, SELECTORS.LIBRARY_BUTTON, this);
return this._content;
@ -390,7 +455,7 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
out = '';
parts = str.trim().split(delimiter);
while (parts.length > 0) {
current = parts.shift();
current = parts.shift().trim();
out += options.fn(current);
}
@ -400,7 +465,8 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
elementid: this.get('host').get('elementid'),
component: COMPONENTNAME,
library: library,
CSS: CSS
CSS: CSS,
DELIMITERS: DELIMITERS
});
var url = M.cfg.wwwroot + '/lib/editor/atto/plugins/equation/ajax.php';
@ -433,6 +499,7 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
texfilteractive: {
value: false
},
/**
* The contextid to use when generating this preview.
*
@ -462,5 +529,6 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto.
texdocsurl: {
value: null
}
}
});

View File

@ -2,6 +2,7 @@
"moodle-atto_equation-button": {
"requires": [
"moodle-editor_atto-plugin",
"moodle-core-event",
"io",
"event-valuechange",
"tabview"

View File

@ -0,0 +1,72 @@
YUI.add('moodle-core-event', function (Y, NAME) {
// 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/>.
/**
* @module moodle-core-event
*/
var LOGNAME = 'moodle-core-event';
/**
* List of published global JS events in Moodle. This is a collection
* of global events that can be subscribed to, or fired from any plugin.
*
* @namespace M.core
* @class event
*/
M.core = M.core || {};
M.core.event = {
/**
* This event is triggered when a page has added dynamic nodes to a page
* that should be processed by the filter system. An example is loading
* user text that could have equations in it. MathJax can typeset the equations
* but only if it is notified that there are new nodes in the page that need processing.
* To trigger this event use M.core.Event.fire(M.core.Event.FILTER_CONTENT_UPDATED, {nodes: list});
*
* @event "filter-content-updated"
* @param nodes {Y.NodeList} List of nodes added to the DOM.
*/
FILTER_CONTENT_UPDATED: "filter-content-updated"
};
var eventDefaultConfig = {
emitFacade: true,
defaultFn: function(e) {
Y.log('Event fired: ' + e.type, 'debug', LOGNAME);
},
preventedFn: function(e) {
Y.log('Event prevented: ' + e.type, 'debug', LOGNAME);
},
stoppedFn: function(e) {
Y.log('Event stopped: ' + e.type, 'debug', LOGNAME);
}
};
// Publish all the events with a standard config.
var key;
for (key in M.core.event) {
if (M.core.event.hasOwnProperty(key)) {
Y.publish(M.core.event[key], eventDefaultConfig);
}
}
// Publish events with a custom config here.
}, '@VERSION@', {"requires": ["event-custom"]});

View File

@ -0,0 +1 @@
YUI.add("moodle-core-event",function(e,t){var n="moodle-core-event";M.core=M.core||{},M.core.event={FILTER_CONTENT_UPDATED:"filter-content-updated"};var r={emitFacade:!0,defaultFn:function(e){},preventedFn:function(e){},stoppedFn:function(e){}},i;for(i in M.core.event)M.core.event.hasOwnProperty(i)&&e.publish(M.core.event[i],r)},"@VERSION@",{requires:["event-custom"]});

View File

@ -0,0 +1,69 @@
YUI.add('moodle-core-event', function (Y, NAME) {
// 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/>.
/**
* @module moodle-core-event
*/
var LOGNAME = 'moodle-core-event';
/**
* List of published global JS events in Moodle. This is a collection
* of global events that can be subscribed to, or fired from any plugin.
*
* @namespace M.core
* @class event
*/
M.core = M.core || {};
M.core.event = {
/**
* This event is triggered when a page has added dynamic nodes to a page
* that should be processed by the filter system. An example is loading
* user text that could have equations in it. MathJax can typeset the equations
* but only if it is notified that there are new nodes in the page that need processing.
* To trigger this event use M.core.Event.fire(M.core.Event.FILTER_CONTENT_UPDATED, {nodes: list});
*
* @event "filter-content-updated"
* @param nodes {Y.NodeList} List of nodes added to the DOM.
*/
FILTER_CONTENT_UPDATED: "filter-content-updated"
};
var eventDefaultConfig = {
emitFacade: true,
defaultFn: function(e) {
},
preventedFn: function(e) {
},
stoppedFn: function(e) {
}
};
// Publish all the events with a standard config.
var key;
for (key in M.core.event) {
if (M.core.event.hasOwnProperty(key)) {
Y.publish(M.core.event[key], eventDefaultConfig);
}
}
// Publish events with a custom config here.
}, '@VERSION@', {"requires": ["event-custom"]});

View File

@ -0,0 +1,10 @@
{
"name": "moodle-core-event",
"builds": {
"moodle-core-event": {
"jsfiles": [
"event.js"
]
}
}
}

67
lib/yui/src/event/js/event.js vendored Normal file
View File

@ -0,0 +1,67 @@
// 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/>.
/**
* @module moodle-core-event
*/
var LOGNAME = 'moodle-core-event';
/**
* List of published global JS events in Moodle. This is a collection
* of global events that can be subscribed to, or fired from any plugin.
*
* @namespace M.core
* @class event
*/
M.core = M.core || {};
M.core.event = {
/**
* This event is triggered when a page has added dynamic nodes to a page
* that should be processed by the filter system. An example is loading
* user text that could have equations in it. MathJax can typeset the equations
* but only if it is notified that there are new nodes in the page that need processing.
* To trigger this event use M.core.Event.fire(M.core.Event.FILTER_CONTENT_UPDATED, {nodes: list});
*
* @event "filter-content-updated"
* @param nodes {Y.NodeList} List of nodes added to the DOM.
*/
FILTER_CONTENT_UPDATED: "filter-content-updated"
};
var eventDefaultConfig = {
emitFacade: true,
defaultFn: function(e) {
Y.log('Event fired: ' + e.type, 'debug', LOGNAME);
},
preventedFn: function(e) {
Y.log('Event prevented: ' + e.type, 'debug', LOGNAME);
},
stoppedFn: function(e) {
Y.log('Event stopped: ' + e.type, 'debug', LOGNAME);
}
};
// Publish all the events with a standard config.
var key;
for (key in M.core.event) {
if (M.core.event.hasOwnProperty(key)) {
Y.publish(M.core.event[key], eventDefaultConfig);
}
}
// Publish events with a custom config here.

View File

@ -0,0 +1,7 @@
{
"moodle-core-event": {
"requires": [
"event-custom"
]
}
}