From 7f6753151e16dd7a76f046bede311ef82c6c75ff Mon Sep 17 00:00:00 2001 From: Damyon Wiese Date: Wed, 20 Nov 2013 16:47:16 +0800 Subject: [PATCH 1/8] MDL-43856 MathJax: Add a mathjax loader filter. --- filter/mathjaxloader/filter.php | 146 ++++++++++++++++++ .../lang/en/filter_mathjaxloader.php | 38 +++++ filter/mathjaxloader/readme_moodle.txt | 15 ++ filter/mathjaxloader/settings.php | 86 +++++++++++ filter/mathjaxloader/styles.css | 3 + filter/mathjaxloader/version.php | 29 ++++ .../moodle-filter_mathjax-loader-debug.js | 46 ++++++ .../moodle-filter_mathjax-loader-min.js | 1 + .../moodle-filter_mathjax-loader.js | 46 ++++++ ...oodle-filter_mathjaxloader-loader-debug.js | 45 ++++++ .../moodle-filter_mathjaxloader-loader-min.js | 1 + .../moodle-filter_mathjaxloader-loader.js | 45 ++++++ .../mathjaxloader/yui/src/loader/build.json | 10 ++ .../mathjaxloader/yui/src/loader/js/loader.js | 40 +++++ .../yui/src/loader/meta/loader.json | 7 + 15 files changed, 558 insertions(+) create mode 100644 filter/mathjaxloader/filter.php create mode 100644 filter/mathjaxloader/lang/en/filter_mathjaxloader.php create mode 100644 filter/mathjaxloader/readme_moodle.txt create mode 100644 filter/mathjaxloader/settings.php create mode 100644 filter/mathjaxloader/styles.css create mode 100644 filter/mathjaxloader/version.php create mode 100644 filter/mathjaxloader/yui/build/moodle-filter_mathjax-loader/moodle-filter_mathjax-loader-debug.js create mode 100644 filter/mathjaxloader/yui/build/moodle-filter_mathjax-loader/moodle-filter_mathjax-loader-min.js create mode 100644 filter/mathjaxloader/yui/build/moodle-filter_mathjax-loader/moodle-filter_mathjax-loader.js create mode 100644 filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader-debug.js create mode 100644 filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader-min.js create mode 100644 filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader.js create mode 100644 filter/mathjaxloader/yui/src/loader/build.json create mode 100644 filter/mathjaxloader/yui/src/loader/js/loader.js create mode 100644 filter/mathjaxloader/yui/src/loader/meta/loader.json diff --git a/filter/mathjaxloader/filter.php b/filter/mathjaxloader/filter.php new file mode 100644 index 00000000000..c0248fe1925 --- /dev/null +++ b/filter/mathjaxloader/filter.php @@ -0,0 +1,146 @@ +. + +/** + * 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 lazy_init() { + global $CFG, $PAGE; + // 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.init', 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()) { + // This replaces blah syntax with [tex] blah [/tex] syntax + // because MathJax cannot handle html tags as delimiters. + + $text = preg_replace('|<(/?) *tex( [^>]*)?>|u', '[\1tex]', $text); + if (strpos($text, '$$') !== false || strpos($text, '\\[') !== false || strpos($text, '[tex]') !== false) { + // Only call init if there is at least one equation on the page. + $this->lazy_init(); + return '' . $text . ''; + } + return $text; + } +} diff --git a/filter/mathjaxloader/lang/en/filter_mathjaxloader.php b/filter/mathjaxloader/lang/en/filter_mathjaxloader.php new file mode 100644 index 00000000000..3327778b507 --- /dev/null +++ b/filter/mathjaxloader/lang/en/filter_mathjaxloader.php @@ -0,0 +1,38 @@ +. + +/** + * Strings for component 'filter_mathjax', language 'en'. + * + * Note: use filter/mathjax/cli/update_lang_files.php script to import strings from upstream JS lang files. + * + * @package filter_mathjax + * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +//== Custom Moodle strings that are not part of upstream MathJax == +$string['filtername'] = 'MathJax'; +$string['httpurl'] = 'HTTP MathJax URL'; +$string['httpurl_help'] = 'Full URL to MathJax library. Used when the page is loaded via http.'; +$string['httpsurl'] = 'HTTP MathJax URL'; +$string['httpsurl_help'] = 'Full URL to MathJax library. Used when the page is loaded via https (secure). '; +$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 advanced users may want to provide a different configuration. More information on MathJax configuration options is available here: http://docs.mathjax.org/en/latest/options/index.html'; + diff --git a/filter/mathjaxloader/readme_moodle.txt b/filter/mathjaxloader/readme_moodle.txt new file mode 100644 index 00000000000..76355a381b1 --- /dev/null +++ b/filter/mathjaxloader/readme_moodle.txt @@ -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 CDN urls in filter.php - and update the list of language mappings - also in filter.php. diff --git a/filter/mathjaxloader/settings.php b/filter/mathjaxloader/settings.php new file mode 100644 index 00000000000..fa9be8da383 --- /dev/null +++ b/filter/mathjaxloader/settings.php @@ -0,0 +1,86 @@ +. + +/** + * 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); + + $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: ["!"] }, + tex2jax: { + skipTags: ["script","noscript","style","textarea"], + inlineMath: [ + ["\\\\(","\\\\)"], + ["$$","$$"], + ["\\\\[","\\\\]"] + ], + displayMath: [ + ["[tex]","[/tex]"], + ], + balanceBraces: true, + preview: "TeX" + }, + 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); + +} diff --git a/filter/mathjaxloader/styles.css b/filter/mathjaxloader/styles.css new file mode 100644 index 00000000000..f09529267e9 --- /dev/null +++ b/filter/mathjaxloader/styles.css @@ -0,0 +1,3 @@ +.jsenabled #MathJax_ZoomFrame { + position: absolute; +} diff --git a/filter/mathjaxloader/version.php b/filter/mathjaxloader/version.php new file mode 100644 index 00000000000..666c4b8572c --- /dev/null +++ b/filter/mathjaxloader/version.php @@ -0,0 +1,29 @@ +. + +/** + * 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'; diff --git a/filter/mathjaxloader/yui/build/moodle-filter_mathjax-loader/moodle-filter_mathjax-loader-debug.js b/filter/mathjaxloader/yui/build/moodle-filter_mathjax-loader/moodle-filter_mathjax-loader-debug.js new file mode 100644 index 00000000000..a2df1208ebf --- /dev/null +++ b/filter/mathjaxloader/yui/build/moodle-filter_mathjax-loader/moodle-filter_mathjax-loader-debug.js @@ -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 . + +/** + * Mathjax JS Loader. + * + * @package filter_mathjax + * @copyright 2014 Damyon Wiese + * @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(''); + 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"]}); diff --git a/filter/mathjaxloader/yui/build/moodle-filter_mathjax-loader/moodle-filter_mathjax-loader-min.js b/filter/mathjaxloader/yui/build/moodle-filter_mathjax-loader/moodle-filter_mathjax-loader-min.js new file mode 100644 index 00000000000..6970a0f8d52 --- /dev/null +++ b/filter/mathjaxloader/yui/build/moodle-filter_mathjax-loader/moodle-filter_mathjax-loader-min.js @@ -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('');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"]}); diff --git a/filter/mathjaxloader/yui/build/moodle-filter_mathjax-loader/moodle-filter_mathjax-loader.js b/filter/mathjaxloader/yui/build/moodle-filter_mathjax-loader/moodle-filter_mathjax-loader.js new file mode 100644 index 00000000000..a2df1208ebf --- /dev/null +++ b/filter/mathjaxloader/yui/build/moodle-filter_mathjax-loader/moodle-filter_mathjax-loader.js @@ -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 . + +/** + * Mathjax JS Loader. + * + * @package filter_mathjax + * @copyright 2014 Damyon Wiese + * @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(''); + 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"]}); diff --git a/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader-debug.js b/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader-debug.js new file mode 100644 index 00000000000..8cea37eca0c --- /dev/null +++ b/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader-debug.js @@ -0,0 +1,45 @@ +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 . + +/** + * Mathjax JS Loader. + * + * @package filter_mathjaxloader + * @copyright 2014 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +M.filter_mathjaxloader = M.filter_mathjaxloader || { + init: 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); + + MathJax.Localization.setLocale(params.lang); + + Y.all('.filter_mathjaxloader_equation').each(function(node) { + MathJax.Hub.Queue(["Typeset",MathJax.Hub,node.getDOMNode()]); + }); + MathJax.Hub.Configured(); + } +}; + + +}, '@VERSION@', {"requires": ["mathjax"]}); diff --git a/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader-min.js b/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader-min.js new file mode 100644 index 00000000000..1dffab905e5 --- /dev/null +++ b/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader-min.js @@ -0,0 +1 @@ +YUI.add("moodle-filter_mathjaxloader-loader",function(e,t){M.filter_mathjaxloader=M.filter_mathjaxloader||{init: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),MathJax.Localization.setLocale(t.lang),e.all(".filter_mathjaxloader_equation").each(function(e){MathJax.Hub.Queue(["Typeset",MathJax.Hub,e.getDOMNode()])}),MathJax.Hub.Configured()}}},"@VERSION@",{requires:["mathjax"]}); diff --git a/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader.js b/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader.js new file mode 100644 index 00000000000..8cea37eca0c --- /dev/null +++ b/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader.js @@ -0,0 +1,45 @@ +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 . + +/** + * Mathjax JS Loader. + * + * @package filter_mathjaxloader + * @copyright 2014 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +M.filter_mathjaxloader = M.filter_mathjaxloader || { + init: 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); + + MathJax.Localization.setLocale(params.lang); + + Y.all('.filter_mathjaxloader_equation').each(function(node) { + MathJax.Hub.Queue(["Typeset",MathJax.Hub,node.getDOMNode()]); + }); + MathJax.Hub.Configured(); + } +}; + + +}, '@VERSION@', {"requires": ["mathjax"]}); diff --git a/filter/mathjaxloader/yui/src/loader/build.json b/filter/mathjaxloader/yui/src/loader/build.json new file mode 100644 index 00000000000..b262702bd16 --- /dev/null +++ b/filter/mathjaxloader/yui/src/loader/build.json @@ -0,0 +1,10 @@ +{ + "name": "moodle-filter_mathjaxloader-loader", + "builds": { + "moodle-filter_mathjaxloader-loader": { + "jsfiles": [ + "loader.js" + ] + } + } +} diff --git a/filter/mathjaxloader/yui/src/loader/js/loader.js b/filter/mathjaxloader/yui/src/loader/js/loader.js new file mode 100644 index 00000000000..92adb06b0b1 --- /dev/null +++ b/filter/mathjaxloader/yui/src/loader/js/loader.js @@ -0,0 +1,40 @@ +// 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 . + +/** + * Mathjax JS Loader. + * + * @package filter_mathjaxloader + * @copyright 2014 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +M.filter_mathjaxloader = M.filter_mathjaxloader || { + init: 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); + + MathJax.Localization.setLocale(params.lang); + + Y.all('.filter_mathjaxloader_equation').each(function(node) { + MathJax.Hub.Queue(["Typeset",MathJax.Hub,node.getDOMNode()]); + }); + MathJax.Hub.Configured(); + } +}; diff --git a/filter/mathjaxloader/yui/src/loader/meta/loader.json b/filter/mathjaxloader/yui/src/loader/meta/loader.json new file mode 100644 index 00000000000..a8ce1960842 --- /dev/null +++ b/filter/mathjaxloader/yui/src/loader/meta/loader.json @@ -0,0 +1,7 @@ +{ + "moodle-filter_mathjaxloader-loader": { + "requires": [ + "mathjax" + ] + } +} From 8939ebac77406f18d358f1d32a6be0960277cf44 Mon Sep 17 00:00:00 2001 From: Damyon Wiese Date: Thu, 20 Mar 2014 14:25:44 +0800 Subject: [PATCH 2/8] MDL-43856 MathJax: alter the equation editor to make it use MathJax if it is available. If MathJax is loaded by any means, the editor should use it instead of the filter. This prevents slow ajax requests and gives much better rendering. MathJax could be loaded by the filter, or it could just be done the old way by sticking the include in the page header. --- .../moodle-atto_equation-button-debug.js | 70 ++++++++++++------- .../moodle-atto_equation-button-min.js | 2 +- .../moodle-atto_equation-button.js | 70 ++++++++++++------- .../equation/yui/src/button/js/button.js | 70 ++++++++++++------- 4 files changed, 133 insertions(+), 79 deletions(-) diff --git a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-debug.js b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-debug.js index 00abb9c9ce2..c4612f22483 100644 --- a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-debug.js +++ b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-debug.js @@ -112,7 +112,9 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. _content: null, initializer: function() { - if (this.get('texfilteractive')) { + // If there is a tex filter active - or if MathJax is loaded in + // the page - enable this button. + if (this.get('texfilteractive') || (typeof MathJax !== "undefined")) { // Add the button to the toolbar. this.addButton({ icon: 'e/math', @@ -160,6 +162,10 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. tabview.render(); dialogue.show(); + // If MathJax is available, get it to render all the library buttons at once. + if (typeof MathJax !== "undefined") { + MathJax.Hub.Queue(["Typeset", MathJax.Hub, library.getDOMNode()]); + } var equation = this._resolveEquation(); if (equation) { @@ -286,18 +292,27 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. // Save the cursor position - for insertion from the library. this._lastCursorPos = currentPos; equation = prefix + equation.substring(0, currentPos) + cursorLatex + equation.substring(currentPos); - url = M.cfg.wwwroot + '/lib/editor/atto/plugins/equation/ajax.php'; - params = { - sesskey: M.cfg.sesskey, - contextid: this.get('contextid'), - action: 'filtertext', - text: '$$ ' + equation + ' $$' - }; - preview = Y.io(url, { sync: true, - data: params }); - if (preview.status === 200) { - this._content.one(SELECTORS.EQUATION_PREVIEW).setHTML(preview.responseText); + var previewNode = this._content.one(SELECTORS.EQUATION_PREVIEW); + if (typeof MathJax !== "undefined") { + // If MathJax is available, get it to update the preview without an ajax request. + previewNode.setHTML('$$' + equation + '$$'); + MathJax.Hub.Queue(["Typeset", MathJax.Hub, previewNode.getDOMNode()]); + } else { + // Fall back on 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 + ' $$' + }; + + preview = Y.io(url, { sync: true, + data: params }); + if (preview.status === 200) { + previewNode.setHTML(preview.responseText); + } } }, @@ -405,22 +420,25 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. CSS: CSS }); - var url = M.cfg.wwwroot + '/lib/editor/atto/plugins/equation/ajax.php'; - var params = { - sesskey: M.cfg.sesskey, - contextid: this.get('contextid'), - action: 'filtertext', - text: content - }; + // Only render the library via ajax is MathJax is not available. + if (typeof MathJax === "undefined") { + var url = M.cfg.wwwroot + '/lib/editor/atto/plugins/equation/ajax.php'; + var params = { + sesskey: M.cfg.sesskey, + contextid: this.get('contextid'), + action: 'filtertext', + text: content + }; - preview = Y.io(url, { - sync: true, - data: params, - method: 'POST' - }); + preview = Y.io(url, { + sync: true, + data: params, + method: 'POST' + }); - if (preview.status === 200) { - content = preview.responseText; + if (preview.status === 200) { + content = preview.responseText; + } } return content; } diff --git a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-min.js b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-min.js index c043250ead3..ff118538888 100644 --- a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-min.js +++ b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-min.js @@ -1 +1 @@ -YUI.add("moodle-atto_equation-button",function(e,t){var n="atto_equation",r={EQUATION_TEXT:"atto_equation_equation",EQUATION_PREVIEW:"atto_equation_preview",SUBMIT:"atto_equation_submit",LIBRARY:"atto_equation_library",LIBRARY_GROUP_PREFIX:"atto_equation_library"},i={LIBRARY_GROUP_PREFIX:"."+r.LIBRARY_GROUP_PREFIX,EQUATION_TEXT:"."+r.EQUATION_TEXT,EQUATION_PREVIEW:"."+r.EQUATION_PREVIEW,SUBMIT:"."+r.SUBMIT,LIBRARY_BUTTON:"."+r.LIBRARY+" button"},s={FORM:'
{{{library}}}

',LIBRARY:'
{{#each library}}
{{#split "\n" elements}}{{/split}}
{{/each}}
'};e.namespace("M.atto_equation").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{_currentSelection:null,_lastCursorPos:0,_content:null,initializer:function(){this.get("texfilteractive")&&(this.addButton({icon:"e/math",callback:this._displayDialogue}),this.get("host").on("atto:selectionchanged",function(){this._resolveEquation()?this.highlightButtons():this.unHighlightButtons()},this))},_displayDialogue:function(){this._currentSelection=this.get("host").getSelection();if(this._currentSelection===!1)return;var t=this.getDialogue({headerContent:M.util.get_string("pluginname",n),focusAfterHide:!0,width:600}),r=this._getDialogueContent();t.set("bodyContent",r);var s=r.one(i.LIBRARY_GROUP_PREFIX),o=new e.TabView({srcNode:s});o.render(),t.show();var u=this._resolveEquation();u&&r.one(i.EQUATION_TEXT).set("text",u),this._updatePreview(!1)},_resolveEquation:function(){var t=this.get("host").getSelectionParentNode(),n,r;return t?(n=e.one(t).get("text"),pattern=/\$\$[\S\s]*\$\$/,r=pattern.exec(n),r&&r.length?(r=r.pop(),r=r.substring(2,r.length-2),r):!1):!1},_setEquation:function(t){var n,r,i,s,o,u,a=this.get("host");t.preventDefault(),this.getDialogue({focusAfterHide:null}).hide(),n=t.currentTarget.ancestor(".atto_form").one("textarea"),u=n.get("value"),u!==""&&(a.setSelection(this._currentSelection),u="$$ "+u.trim()+" $$",r=e.one(a.getSelectionParentNode()),i=r.get("text"),s=/\$\$[\S\s]*\$\$/,o=s.exec(i),o&&o.length?(o=o.pop(),i=i.replace(o,"$$"+u+"$$"),r.set("text",i)):a.insertContentAtFocusPoint(u),this.markUpdated())},_updatePreview:function(t){var n=this._content.one(i.EQUATION_TEXT),r=n.get("value"),s,o,u=n.get("selectionStart"),a="",f="\\square ",l;t&&t.preventDefault(),u||(u=0);while(r.charAt(u)==="\\"&&u>0)u-=1;l=/[\w\{\}]/;while(l.test(r.charAt(u))&&u0)i=r.shift(),s+=n.fn(i);return s}),o=t({elementid:this.get("host").get("elementid"),component:n,library:i,CSS:r});var u=M.cfg.wwwroot+"/lib/editor/atto/plugins/equation/ajax.php",a={sesskey:M.cfg.sesskey,contextid:this.get("contextid"),action:"filtertext",text:o};return preview=e.io(u,{sync:!0,data:a,method:"POST"}),preview.status===200&&(o=preview.responseText),o}},{ATTRS:{texfilteractive:{value:!1},contextid:{value:null},library:{value:{}},texdocsurl:{value:null}}})},"@VERSION@",{requires:["moodle-editor_atto-plugin","io","event-valuechange","tabview"]}); +YUI.add("moodle-atto_equation-button",function(e,t){var n="atto_equation",r={EQUATION_TEXT:"atto_equation_equation",EQUATION_PREVIEW:"atto_equation_preview",SUBMIT:"atto_equation_submit",LIBRARY:"atto_equation_library",LIBRARY_GROUP_PREFIX:"atto_equation_library"},i={LIBRARY_GROUP_PREFIX:"."+r.LIBRARY_GROUP_PREFIX,EQUATION_TEXT:"."+r.EQUATION_TEXT,EQUATION_PREVIEW:"."+r.EQUATION_PREVIEW,SUBMIT:"."+r.SUBMIT,LIBRARY_BUTTON:"."+r.LIBRARY+" button"},s={FORM:'
{{{library}}}

',LIBRARY:'
{{#each library}}
{{#split "\n" elements}}{{/split}}
{{/each}}
'};e.namespace("M.atto_equation").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{_currentSelection:null,_lastCursorPos:0,_content:null,initializer:function(){if(this.get("texfilteractive")||typeof MathJax!="undefined")this.addButton({icon:"e/math",callback:this._displayDialogue}),this.get("host").on("atto:selectionchanged",function(){this._resolveEquation()?this.highlightButtons():this.unHighlightButtons()},this)},_displayDialogue:function(){this._currentSelection=this.get("host").getSelection();if(this._currentSelection===!1)return;var t=this.getDialogue({headerContent:M.util.get_string("pluginname",n),focusAfterHide:!0,width:600}),r=this._getDialogueContent();t.set("bodyContent",r);var s=r.one(i.LIBRARY_GROUP_PREFIX),o=new e.TabView({srcNode:s});o.render(),t.show(),typeof MathJax!="undefined"&&MathJax.Hub.Queue(["Typeset",MathJax.Hub,s.getDOMNode()]);var u=this._resolveEquation();u&&r.one(i.EQUATION_TEXT).set("text",u),this._updatePreview(!1)},_resolveEquation:function(){var t=this.get("host").getSelectionParentNode(),n,r;return t?(n=e.one(t).get("text"),pattern=/\$\$[\S\s]*\$\$/,r=pattern.exec(n),r&&r.length?(r=r.pop(),r=r.substring(2,r.length-2),r):!1):!1},_setEquation:function(t){var n,r,i,s,o,u,a=this.get("host");t.preventDefault(),this.getDialogue({focusAfterHide:null}).hide(),n=t.currentTarget.ancestor(".atto_form").one("textarea"),u=n.get("value"),u!==""&&(a.setSelection(this._currentSelection),u="$$ "+u.trim()+" $$",r=e.one(a.getSelectionParentNode()),i=r.get("text"),s=/\$\$[\S\s]*\$\$/,o=s.exec(i),o&&o.length?(o=o.pop(),i=i.replace(o,"$$"+u+"$$"),r.set("text",i)):a.insertContentAtFocusPoint(u),this.markUpdated())},_updatePreview:function(t){var n=this._content.one(i.EQUATION_TEXT),r=n.get("value"),s,o,u=n.get("selectionStart"),a="",f="\\square ",l;t&&t.preventDefault(),u||(u=0);while(r.charAt(u)==="\\"&&u>0)u-=1;l=/[\w\{\}]/;while(l.test(r.charAt(u))&&u0)i=r.shift(),s+=n.fn(i);return s}),o=t({elementid:this.get("host").get("elementid"),component:n,library:i,CSS:r});if(typeof MathJax=="undefined"){var u=M.cfg.wwwroot+"/lib/editor/atto/plugins/equation/ajax.php",a={sesskey:M.cfg.sesskey,contextid:this.get("contextid"),action:"filtertext",text:o};preview=e.io(u,{sync:!0,data:a,method:"POST"}),preview.status===200&&(o=preview.responseText)}return o}},{ATTRS:{texfilteractive:{value:!1},contextid:{value:null},library:{value:{}},texdocsurl:{value:null}}})},"@VERSION@",{requires:["moodle-editor_atto-plugin","io","event-valuechange","tabview"]}); diff --git a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js index 76df89c2ec8..d909984cfd4 100644 --- a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js +++ b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js @@ -112,7 +112,9 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. _content: null, initializer: function() { - if (this.get('texfilteractive')) { + // If there is a tex filter active - or if MathJax is loaded in + // the page - enable this button. + if (this.get('texfilteractive') || (typeof MathJax !== "undefined")) { // Add the button to the toolbar. this.addButton({ icon: 'e/math', @@ -160,6 +162,10 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. tabview.render(); dialogue.show(); + // If MathJax is available, get it to render all the library buttons at once. + if (typeof MathJax !== "undefined") { + MathJax.Hub.Queue(["Typeset", MathJax.Hub, library.getDOMNode()]); + } var equation = this._resolveEquation(); if (equation) { @@ -286,18 +292,27 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. // Save the cursor position - for insertion from the library. this._lastCursorPos = currentPos; equation = prefix + equation.substring(0, currentPos) + cursorLatex + equation.substring(currentPos); - url = M.cfg.wwwroot + '/lib/editor/atto/plugins/equation/ajax.php'; - params = { - sesskey: M.cfg.sesskey, - contextid: this.get('contextid'), - action: 'filtertext', - text: '$$ ' + equation + ' $$' - }; - preview = Y.io(url, { sync: true, - data: params }); - if (preview.status === 200) { - this._content.one(SELECTORS.EQUATION_PREVIEW).setHTML(preview.responseText); + var previewNode = this._content.one(SELECTORS.EQUATION_PREVIEW); + if (typeof MathJax !== "undefined") { + // If MathJax is available, get it to update the preview without an ajax request. + previewNode.setHTML('$$' + equation + '$$'); + MathJax.Hub.Queue(["Typeset", MathJax.Hub, previewNode.getDOMNode()]); + } else { + // Fall back on 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 + ' $$' + }; + + preview = Y.io(url, { sync: true, + data: params }); + if (preview.status === 200) { + previewNode.setHTML(preview.responseText); + } } }, @@ -404,22 +419,25 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. CSS: CSS }); - var url = M.cfg.wwwroot + '/lib/editor/atto/plugins/equation/ajax.php'; - var params = { - sesskey: M.cfg.sesskey, - contextid: this.get('contextid'), - action: 'filtertext', - text: content - }; + // Only render the library via ajax is MathJax is not available. + if (typeof MathJax === "undefined") { + var url = M.cfg.wwwroot + '/lib/editor/atto/plugins/equation/ajax.php'; + var params = { + sesskey: M.cfg.sesskey, + contextid: this.get('contextid'), + action: 'filtertext', + text: content + }; - preview = Y.io(url, { - sync: true, - data: params, - method: 'POST' - }); + preview = Y.io(url, { + sync: true, + data: params, + method: 'POST' + }); - if (preview.status === 200) { - content = preview.responseText; + if (preview.status === 200) { + content = preview.responseText; + } } return content; } diff --git a/lib/editor/atto/plugins/equation/yui/src/button/js/button.js b/lib/editor/atto/plugins/equation/yui/src/button/js/button.js index a5af93df7ef..2e30f82b765 100644 --- a/lib/editor/atto/plugins/equation/yui/src/button/js/button.js +++ b/lib/editor/atto/plugins/equation/yui/src/button/js/button.js @@ -110,7 +110,9 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. _content: null, initializer: function() { - if (this.get('texfilteractive')) { + // If there is a tex filter active - or if MathJax is loaded in + // the page - enable this button. + if (this.get('texfilteractive') || (typeof MathJax !== "undefined")) { // Add the button to the toolbar. this.addButton({ icon: 'e/math', @@ -158,6 +160,10 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. tabview.render(); dialogue.show(); + // If MathJax is available, get it to render all the library buttons at once. + if (typeof MathJax !== "undefined") { + MathJax.Hub.Queue(["Typeset", MathJax.Hub, library.getDOMNode()]); + } var equation = this._resolveEquation(); if (equation) { @@ -284,18 +290,27 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. // Save the cursor position - for insertion from the library. this._lastCursorPos = currentPos; equation = prefix + equation.substring(0, currentPos) + cursorLatex + equation.substring(currentPos); - url = M.cfg.wwwroot + '/lib/editor/atto/plugins/equation/ajax.php'; - params = { - sesskey: M.cfg.sesskey, - contextid: this.get('contextid'), - action: 'filtertext', - text: '$$ ' + equation + ' $$' - }; - preview = Y.io(url, { sync: true, - data: params }); - if (preview.status === 200) { - this._content.one(SELECTORS.EQUATION_PREVIEW).setHTML(preview.responseText); + var previewNode = this._content.one(SELECTORS.EQUATION_PREVIEW); + if (typeof MathJax !== "undefined") { + // If MathJax is available, get it to update the preview without an ajax request. + previewNode.setHTML('$$' + equation + '$$'); + MathJax.Hub.Queue(["Typeset", MathJax.Hub, previewNode.getDOMNode()]); + } else { + // Fall back on 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 + ' $$' + }; + + preview = Y.io(url, { sync: true, + data: params }); + if (preview.status === 200) { + previewNode.setHTML(preview.responseText); + } } }, @@ -403,22 +418,25 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. CSS: CSS }); - var url = M.cfg.wwwroot + '/lib/editor/atto/plugins/equation/ajax.php'; - var params = { - sesskey: M.cfg.sesskey, - contextid: this.get('contextid'), - action: 'filtertext', - text: content - }; + // Only render the library via ajax is MathJax is not available. + if (typeof MathJax === "undefined") { + var url = M.cfg.wwwroot + '/lib/editor/atto/plugins/equation/ajax.php'; + var params = { + sesskey: M.cfg.sesskey, + contextid: this.get('contextid'), + action: 'filtertext', + text: content + }; - preview = Y.io(url, { - sync: true, - data: params, - method: 'POST' - }); + preview = Y.io(url, { + sync: true, + data: params, + method: 'POST' + }); - if (preview.status === 200) { - content = preview.responseText; + if (preview.status === 200) { + content = preview.responseText; + } } return content; } From 289ed254a7196e0f67ab80eda34464c8b35a0594 Mon Sep 17 00:00:00 2001 From: Damyon Wiese Date: Tue, 25 Mar 2014 16:42:07 +0800 Subject: [PATCH 3/8] MDL-43856 MathJax: Add to standard plugins list --- lib/classes/plugin_manager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/classes/plugin_manager.php b/lib/classes/plugin_manager.php index d06464b6435..580265a2c9b 100644 --- a/lib/classes/plugin_manager.php +++ b/lib/classes/plugin_manager.php @@ -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' ), From cc90cedc856153358cdc8852f46bc32d83897d04 Mon Sep 17 00:00:00 2001 From: Damyon Wiese Date: Tue, 1 Apr 2014 15:20:59 +0800 Subject: [PATCH 4/8] MDL-43856 MathJax: Improvements to the MathJax filter * Remove all traces of mathjax from the atto equation editor - it is filter agnostic * Add a M.core.event module for core events that can be published/subscribed to by plugins (loose coupling) * Add a FILTER_CONTENT_UPDATED event so plugins can tell the filter system when nodes are added to the page * Implement a listener for this event in the MathJax filter * Fire this event from the equation editor and from the glossary filter * Add a nolink tag to the mathjax filter, so equations will not be processed by the other filters * Add a smarter throttle function to the equation editor preview updates (Y.throttle is dumb) --- ...moodle-filter_glossary-autolinker-debug.js | 3 + .../moodle-filter_glossary-autolinker-min.js | 2 +- .../moodle-filter_glossary-autolinker.js | 3 + .../yui/src/autolinker/js/autolinker.js | 2 + .../yui/src/autolinker/meta/autolinker.json | 1 + filter/mathjaxloader/filter.php | 25 ++- .../lang/en/filter_mathjaxloader.php | 18 +- filter/mathjaxloader/settings.php | 19 +- ...oodle-filter_mathjaxloader-loader-debug.js | 20 +- .../moodle-filter_mathjaxloader-loader-min.js | 2 +- .../moodle-filter_mathjaxloader-loader.js | 20 +- .../mathjaxloader/yui/src/loader/js/loader.js | 18 ++ .../yui/src/loader/meta/loader.json | 1 + lib/editor/atto/plugins/equation/lib.php | 6 +- lib/editor/atto/plugins/equation/settings.php | 1 + .../moodle-atto_equation-button-debug.js | 186 +++++++++++------- .../moodle-atto_equation-button-min.js | 3 +- .../moodle-atto_equation-button.js | 186 +++++++++++------- .../equation/yui/src/button/js/button.js | 184 ++++++++++------- .../equation/yui/src/button/meta/button.json | 1 + .../moodle-core-event-debug.js | 63 ++++++ .../moodle-core-event-min.js | 1 + .../moodle-core-event/moodle-core-event.js | 60 ++++++ lib/yui/src/event/build.json | 10 + lib/yui/src/event/js/event.js | 58 ++++++ lib/yui/src/event/meta/event.json | 7 + 26 files changed, 658 insertions(+), 242 deletions(-) create mode 100644 lib/yui/build/moodle-core-event/moodle-core-event-debug.js create mode 100644 lib/yui/build/moodle-core-event/moodle-core-event-min.js create mode 100644 lib/yui/build/moodle-core-event/moodle-core-event.js create mode 100644 lib/yui/src/event/build.json create mode 100644 lib/yui/src/event/js/event.js create mode 100644 lib/yui/src/event/meta/event.json diff --git a/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-debug.js b/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-debug.js index 0a1867900fa..92b7fe50f2c 100644 --- a/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-debug.js +++ b/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-debug.js @@ -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" ] }); diff --git a/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-min.js b/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-min.js index 5d6f83b4b80..a895bd0664e 100644 --- a/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-min.js +++ b/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-min.js @@ -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('
'),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('
'),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"]}); diff --git a/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker.js b/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker.js index 0a1867900fa..92b7fe50f2c 100644 --- a/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker.js +++ b/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker.js @@ -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" ] }); diff --git a/filter/glossary/yui/src/autolinker/js/autolinker.js b/filter/glossary/yui/src/autolinker/js/autolinker.js index 3217f4a2a10..8879603b88c 100644 --- a/filter/glossary/yui/src/autolinker/js/autolinker.js +++ b/filter/glossary/yui/src/autolinker/js/autolinker.js @@ -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(); } diff --git a/filter/glossary/yui/src/autolinker/meta/autolinker.json b/filter/glossary/yui/src/autolinker/meta/autolinker.json index ac4042ed378..065cb2eea78 100644 --- a/filter/glossary/yui/src/autolinker/meta/autolinker.json +++ b/filter/glossary/yui/src/autolinker/meta/autolinker.json @@ -7,6 +7,7 @@ "json-parse", "event-delegate", "overlay", + "moodle-core-event", "moodle-core-notification-alert" ] } diff --git a/filter/mathjaxloader/filter.php b/filter/mathjaxloader/filter.php index c0248fe1925..4876429b277 100644 --- a/filter/mathjaxloader/filter.php +++ b/filter/mathjaxloader/filter.php @@ -132,14 +132,29 @@ class filter_mathjaxloader extends moodle_text_filter { * @param array $options The filter options. */ public function filter($text, array $options = array()) { - // This replaces blah syntax with [tex] blah [/tex] syntax - // because MathJax cannot handle html tags as delimiters. + $legacy = get_config('filter_mathjaxloader', 'texfiltercompatibility'); + if ($legacy) { + // This replaces any of the tex filter maths delimiters with the default for inline maths in MathJAX "\( blah \)". + // blah + $text = preg_replace('|<(/?) *tex( [^>]*)?>|u', '[\1tex]', $text); + // [tex.*] blah [/tex] + $text = str_replace('[tex]', '\\(', $text); + $text = str_replace('[/tex]', '\\)', $text); + // $$ blah $$ + $text = preg_replace('|\$\$[\S\s]\$\$|u', '\\(\1\\)', $text); + // \[ blah \] + $text = str_replace('\\[', '\\(', $text); + $text = str_replace('\\]', '\\)', $text); + } - $text = preg_replace('|<(/?) *tex( [^>]*)?>|u', '[\1tex]', $text); - if (strpos($text, '$$') !== false || strpos($text, '\\[') !== false || strpos($text, '[tex]') !== false) { + $hasinline = strpos($text, '\\(') !== false && strpos($text, '\\)') !== false; + $hasdisplay = (strpos($text, '$$') !== false) || + (strpos($text, '\\[') !== false && strpos($text, '\\]') !== false); + + if ($hasinline || $hasdisplay) { // Only call init if there is at least one equation on the page. $this->lazy_init(); - return '' . $text . ''; + return '' . $text . ''; } return $text; } diff --git a/filter/mathjaxloader/lang/en/filter_mathjaxloader.php b/filter/mathjaxloader/lang/en/filter_mathjaxloader.php index 3327778b507..f244356bba6 100644 --- a/filter/mathjaxloader/lang/en/filter_mathjaxloader.php +++ b/filter/mathjaxloader/lang/en/filter_mathjaxloader.php @@ -29,10 +29,18 @@ $string['filtername'] = 'MathJax'; $string['httpurl'] = 'HTTP MathJax URL'; $string['httpurl_help'] = 'Full URL to MathJax library. Used when the page is loaded via http.'; -$string['httpsurl'] = 'HTTP MathJax URL'; +$string['httpsurl'] = 'HTTPS MathJax URL'; $string['httpsurl_help'] = 'Full URL to MathJax library. Used when the page is loaded via https (secure). '; -$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 advanced users may want to provide a different configuration. More information on MathJax configuration options is available here: http://docs.mathjax.org/en/latest/options/index.html'; +$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.'; diff --git a/filter/mathjaxloader/settings.php b/filter/mathjaxloader/settings.php index fa9be8da383..2da905a3879 100644 --- a/filter/mathjaxloader/settings.php +++ b/filter/mathjaxloader/settings.php @@ -44,6 +44,12 @@ if ($ADMIN->fulltree) { 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"], @@ -58,19 +64,6 @@ MathJax.Hub.Config({ mpMouse: true }, errorSettings: { message: ["!"] }, - tex2jax: { - skipTags: ["script","noscript","style","textarea"], - inlineMath: [ - ["\\\\(","\\\\)"], - ["$$","$$"], - ["\\\\[","\\\\]"] - ], - displayMath: [ - ["[tex]","[/tex]"], - ], - balanceBraces: true, - preview: "TeX" - }, skipStartupTypeset: true, messageStyle: "none" }); diff --git a/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader-debug.js b/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader-debug.js index 8cea37eca0c..ea088053a12 100644 --- a/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader-debug.js +++ b/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader-debug.js @@ -38,8 +38,26 @@ M.filter_mathjaxloader = M.filter_mathjaxloader || { MathJax.Hub.Queue(["Typeset",MathJax.Hub,node.getDOMNode()]); }); MathJax.Hub.Configured(); + + // 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); + }, + + /** + * 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) { + event.nodes.each(function (node) { + node.all('.filter_mathjaxloader_equation').each(function(node) { + MathJax.Hub.Queue(["Typeset",MathJax.Hub,node.getDOMNode()]); + }); + }); } }; -}, '@VERSION@', {"requires": ["mathjax"]}); +}, '@VERSION@', {"requires": ["moodle-core-event", "mathjax"]}); diff --git a/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader-min.js b/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader-min.js index 1dffab905e5..1cb456a36c3 100644 --- a/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader-min.js +++ b/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader-min.js @@ -1 +1 @@ -YUI.add("moodle-filter_mathjaxloader-loader",function(e,t){M.filter_mathjaxloader=M.filter_mathjaxloader||{init: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),MathJax.Localization.setLocale(t.lang),e.all(".filter_mathjaxloader_equation").each(function(e){MathJax.Hub.Queue(["Typeset",MathJax.Hub,e.getDOMNode()])}),MathJax.Hub.Configured()}}},"@VERSION@",{requires:["mathjax"]}); +YUI.add("moodle-filter_mathjaxloader-loader",function(e,t){M.filter_mathjaxloader=M.filter_mathjaxloader||{init: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),MathJax.Localization.setLocale(t.lang),e.all(".filter_mathjaxloader_equation").each(function(e){MathJax.Hub.Queue(["Typeset",MathJax.Hub,e.getDOMNode()])}),MathJax.Hub.Configured(),e.on(M.core.event.FILTER_CONTENT_UPDATED,this.contentUpdated,this)},contentUpdated:function(e){e.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","mathjax"]}); diff --git a/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader.js b/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader.js index 8cea37eca0c..ea088053a12 100644 --- a/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader.js +++ b/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader.js @@ -38,8 +38,26 @@ M.filter_mathjaxloader = M.filter_mathjaxloader || { MathJax.Hub.Queue(["Typeset",MathJax.Hub,node.getDOMNode()]); }); MathJax.Hub.Configured(); + + // 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); + }, + + /** + * 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) { + event.nodes.each(function (node) { + node.all('.filter_mathjaxloader_equation').each(function(node) { + MathJax.Hub.Queue(["Typeset",MathJax.Hub,node.getDOMNode()]); + }); + }); } }; -}, '@VERSION@', {"requires": ["mathjax"]}); +}, '@VERSION@', {"requires": ["moodle-core-event", "mathjax"]}); diff --git a/filter/mathjaxloader/yui/src/loader/js/loader.js b/filter/mathjaxloader/yui/src/loader/js/loader.js index 92adb06b0b1..6aaf892cae6 100644 --- a/filter/mathjaxloader/yui/src/loader/js/loader.js +++ b/filter/mathjaxloader/yui/src/loader/js/loader.js @@ -36,5 +36,23 @@ M.filter_mathjaxloader = M.filter_mathjaxloader || { MathJax.Hub.Queue(["Typeset",MathJax.Hub,node.getDOMNode()]); }); MathJax.Hub.Configured(); + + // 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); + }, + + /** + * 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) { + event.nodes.each(function (node) { + node.all('.filter_mathjaxloader_equation').each(function(node) { + MathJax.Hub.Queue(["Typeset",MathJax.Hub,node.getDOMNode()]); + }); + }); } }; diff --git a/filter/mathjaxloader/yui/src/loader/meta/loader.json b/filter/mathjaxloader/yui/src/loader/meta/loader.json index a8ce1960842..20e4640a20f 100644 --- a/filter/mathjaxloader/yui/src/loader/meta/loader.json +++ b/filter/mathjaxloader/yui/src/loader/meta/loader.json @@ -1,6 +1,7 @@ { "moodle-filter_mathjaxloader-loader": { "requires": [ + "moodle-core-event", "mathjax" ] } diff --git a/lib/editor/atto/plugins/equation/lib.php b/lib/editor/atto/plugins/equation/lib.php index 3e2816ed13f..87865f27b99 100644 --- a/lib/editor/atto/plugins/equation/lib.php +++ b/lib/editor/atto/plugins/equation/lib.php @@ -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')); } diff --git a/lib/editor/atto/plugins/equation/settings.php b/lib/editor/atto/plugins/equation/settings.php index 9aec16a2f7c..a0af0245bcd 100644 --- a/lib/editor/atto/plugins/equation/settings.php +++ b/lib/editor/atto/plugins/equation/settings.php @@ -165,4 +165,5 @@ if ($ADMIN->fulltree) { $desc, $default); $settings->add($setting); + } diff --git a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-debug.js b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-debug.js index c4612f22483..687af4baf95 100644 --- a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-debug.js +++ b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-debug.js @@ -48,6 +48,10 @@ var COMPONENTNAME = 'atto_equation', SUBMIT: '.' + CSS.SUBMIT, LIBRARY_BUTTON: '.' + CSS.LIBRARY + ' button' }, + DELIMITERS = { + START: '\\(', + END: '\\)' + }, TEMPLATES = { FORM: '' + '
' + @@ -72,7 +76,7 @@ var COMPONENTNAME = 'atto_equation', '{{#each library}}' + '
' + '{{#split "\n" elements}}' + - '' + + '' + '{{/split}}' + '
' + '{{/each}}' + @@ -111,10 +115,18 @@ 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 - or if MathJax is loaded in - // the page - enable this button. - if (this.get('texfilteractive') || (typeof MathJax !== "undefined")) { + // If there is a tex filter active - enable this button. + if (this.get('texfilteractive')) { // Add the button to the toolbar. this.addButton({ icon: 'e/math', @@ -129,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('' + DELIMITERS.START + ' ' + texNode.get('text') + ' ' + DELIMITERS.END + ''); + texNode.replace(replacement); + }); } + }, /** @@ -162,10 +181,8 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. tabview.render(); dialogue.show(); - // If MathJax is available, get it to render all the library buttons at once. - if (typeof MathJax !== "undefined") { - MathJax.Hub.Queue(["Typeset", MathJax.Hub, library.getDOMNode()]); - } + // 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) { @@ -187,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) { @@ -196,14 +214,28 @@ 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]*)\$\$/); + // \( blah \) + patterns.push(/\\\(([\S\s]*)\\\)/); + // \[ blah \] + patterns.push(/\\\[([\S\s]*)\\\]/); + // [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) { + equation.shift(); + // Remember the inner match so we can replace it later. + this.sourceEquation = equation = equation.shift(); + + return equation; + } } + + this.sourceEquation = ''; return false; }, @@ -218,11 +250,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({ @@ -235,18 +266,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); } @@ -255,6 +284,26 @@ 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. + * + * @param {function} fn + * @param {Integer} 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. * @@ -272,7 +321,6 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. cursorLatex = '\\square ', isChar; - if (e) { e.preventDefault(); } @@ -285,7 +333,7 @@ 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; } @@ -294,25 +342,21 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. equation = prefix + equation.substring(0, currentPos) + cursorLatex + equation.substring(currentPos); var previewNode = this._content.one(SELECTORS.EQUATION_PREVIEW); - if (typeof MathJax !== "undefined") { - // If MathJax is available, get it to update the preview without an ajax request. - previewNode.setHTML('$$' + equation + '$$'); - MathJax.Hub.Queue(["Typeset", MathJax.Hub, previewNode.getDOMNode()]); - } else { - // Fall back on 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 + ' $$' - }; + 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 + }; - preview = Y.io(url, { sync: true, - data: params }); - if (preview.status === 200) { - previewNode.setHTML(preview.responseText); - } + preview = Y.io(url, { sync: true, + data: params }); + if (preview.status === 200) { + previewNode.setHTML(preview.responseText); + Y.fire(M.core.event.FILTER_CONTENT_UPDATED, {nodes: (new Y.NodeList(previewNode))}); } }, @@ -337,9 +381,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; @@ -407,7 +451,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); } @@ -417,28 +461,26 @@ 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 }); - // Only render the library via ajax is MathJax is not available. - if (typeof MathJax === "undefined") { - var url = M.cfg.wwwroot + '/lib/editor/atto/plugins/equation/ajax.php'; - var params = { - sesskey: M.cfg.sesskey, - contextid: this.get('contextid'), - action: 'filtertext', - text: content - }; + var url = M.cfg.wwwroot + '/lib/editor/atto/plugins/equation/ajax.php'; + var params = { + sesskey: M.cfg.sesskey, + contextid: this.get('contextid'), + action: 'filtertext', + text: content + }; - preview = Y.io(url, { - sync: true, - data: params, - method: 'POST' - }); + preview = Y.io(url, { + sync: true, + data: params, + method: 'POST' + }); - if (preview.status === 200) { - content = preview.responseText; - } + if (preview.status === 200) { + content = preview.responseText; } return content; } @@ -453,6 +495,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. * @@ -482,8 +525,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"]}); diff --git a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-min.js b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-min.js index ff118538888..6e87451bb1f 100644 --- a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-min.js +++ b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-min.js @@ -1 +1,2 @@ -YUI.add("moodle-atto_equation-button",function(e,t){var n="atto_equation",r={EQUATION_TEXT:"atto_equation_equation",EQUATION_PREVIEW:"atto_equation_preview",SUBMIT:"atto_equation_submit",LIBRARY:"atto_equation_library",LIBRARY_GROUP_PREFIX:"atto_equation_library"},i={LIBRARY_GROUP_PREFIX:"."+r.LIBRARY_GROUP_PREFIX,EQUATION_TEXT:"."+r.EQUATION_TEXT,EQUATION_PREVIEW:"."+r.EQUATION_PREVIEW,SUBMIT:"."+r.SUBMIT,LIBRARY_BUTTON:"."+r.LIBRARY+" button"},s={FORM:'{{{library}}}

',LIBRARY:'
{{#each library}}
{{#split "\n" elements}}{{/split}}
{{/each}}
'};e.namespace("M.atto_equation").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{_currentSelection:null,_lastCursorPos:0,_content:null,initializer:function(){if(this.get("texfilteractive")||typeof MathJax!="undefined")this.addButton({icon:"e/math",callback:this._displayDialogue}),this.get("host").on("atto:selectionchanged",function(){this._resolveEquation()?this.highlightButtons():this.unHighlightButtons()},this)},_displayDialogue:function(){this._currentSelection=this.get("host").getSelection();if(this._currentSelection===!1)return;var t=this.getDialogue({headerContent:M.util.get_string("pluginname",n),focusAfterHide:!0,width:600}),r=this._getDialogueContent();t.set("bodyContent",r);var s=r.one(i.LIBRARY_GROUP_PREFIX),o=new e.TabView({srcNode:s});o.render(),t.show(),typeof MathJax!="undefined"&&MathJax.Hub.Queue(["Typeset",MathJax.Hub,s.getDOMNode()]);var u=this._resolveEquation();u&&r.one(i.EQUATION_TEXT).set("text",u),this._updatePreview(!1)},_resolveEquation:function(){var t=this.get("host").getSelectionParentNode(),n,r;return t?(n=e.one(t).get("text"),pattern=/\$\$[\S\s]*\$\$/,r=pattern.exec(n),r&&r.length?(r=r.pop(),r=r.substring(2,r.length-2),r):!1):!1},_setEquation:function(t){var n,r,i,s,o,u,a=this.get("host");t.preventDefault(),this.getDialogue({focusAfterHide:null}).hide(),n=t.currentTarget.ancestor(".atto_form").one("textarea"),u=n.get("value"),u!==""&&(a.setSelection(this._currentSelection),u="$$ "+u.trim()+" $$",r=e.one(a.getSelectionParentNode()),i=r.get("text"),s=/\$\$[\S\s]*\$\$/,o=s.exec(i),o&&o.length?(o=o.pop(),i=i.replace(o,"$$"+u+"$$"),r.set("text",i)):a.insertContentAtFocusPoint(u),this.markUpdated())},_updatePreview:function(t){var n=this._content.one(i.EQUATION_TEXT),r=n.get("value"),s,o,u=n.get("selectionStart"),a="",f="\\square ",l;t&&t.preventDefault(),u||(u=0);while(r.charAt(u)==="\\"&&u>0)u-=1;l=/[\w\{\}]/;while(l.test(r.charAt(u))&&u0)i=r.shift(),s+=n.fn(i);return s}),o=t({elementid:this.get("host").get("elementid"),component:n,library:i,CSS:r});if(typeof MathJax=="undefined"){var u=M.cfg.wwwroot+"/lib/editor/atto/plugins/equation/ajax.php",a={sesskey:M.cfg.sesskey,contextid:this.get("contextid"),action:"filtertext",text:o};preview=e.io(u,{sync:!0,data:a,method:"POST"}),preview.status===200&&(o=preview.responseText)}return o}},{ATTRS:{texfilteractive:{value:!1},contextid:{value:null},library:{value:{}},texdocsurl:{value:null}}})},"@VERSION@",{requires:["moodle-editor_atto-plugin","io","event-valuechange","tabview"]}); +YUI.add("moodle-atto_equation-button",function(e,t){var n="atto_equation",r={EQUATION_TEXT:"atto_equation_equation",EQUATION_PREVIEW:"atto_equation_preview",SUBMIT:"atto_equation_submit",LIBRARY:"atto_equation_library",LIBRARY_GROUP_PREFIX:"atto_equation_library"},i={LIBRARY_GROUP_PREFIX:"."+r.LIBRARY_GROUP_PREFIX,EQUATION_TEXT:"."+r.EQUATION_TEXT,EQUATION_PREVIEW:"."+r.EQUATION_PREVIEW,SUBMIT:"."+r.SUBMIT,LIBRARY_BUTTON:"."+r.LIBRARY+" button"},s={START:"\\(",END:"\\)"},o={FORM:'
{{{library}}}

',LIBRARY:'
{{#each library}}
{{#split "\n" elements}}{{/split}}
{{/each}}
'};e.namespace("M.atto_equation").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{_currentSelection:null,_lastCursorPos:0,_content:null,_sourceEquation:"",initializer:function(){this.get("texfilteractive")&&(this.addButton({icon:"e/math",callback:this._displayDialogue}),this.get("host").on("atto:selectionchanged",function(){this._resolveEquation()?this.highlightButtons():this.unHighlightButtons()},this),this.editor.all("tex").each(function(t){var n=e.Node.create(""+s.START+" "+t.get("text")+" "+s.END+"");t.replace(n)}))},_displayDialogue:function(){this._currentSelection=this.get("host").getSelection();if(this._currentSelection===!1)return;var t=this.getDialogue({headerContent:M.util.get_string("pluginname",n),focusAfterHide:!0,width:600}),r=this._getDialogueContent();t.set("bodyContent",r);var s=r.one(i.LIBRARY_GROUP_PREFIX),o=new e.TabView({srcNode:s});o.render(),t.show(),e.fire(M.core.event.FILTER_CONTENT_UPDATED,{nodes:new e.NodeList(t.get("boundingBox"))});var u=this._resolveEquation();u&&r.one(i.EQUATION_TEXT).set("text",u),this._updatePreview(!1)},_resolveEquation:function(){var t=this.get("host").getSelectionParentNode(),n,r,i=[],s;if(!t)return!1;n=e.one(t).get("text"),i.push(/\$\$([\S\s]*)\$\$/),i.push(/\\\(([\S\s]*)\\\)/),i.push(/\\\[([\S\s]*)\\\]/),i.push(/\[tex\]([\S\s]*)\[\/tex\]/);for(s=0;s0)a-=1;c=/[a-zA-Z\{\}]/;while(c.test(r.charAt(a))&&a0)i=r.shift().trim(),s+=n.fn(i);return s}),u=t({elementid:this.get("host").get("elementid"),component:n,library:i,CSS:r,DELIMITERS:s});var a=M.cfg.wwwroot+"/lib/editor/atto/plugins/equation/ajax.php",f={sesskey +:M.cfg.sesskey,contextid:this.get("contextid"),action:"filtertext",text:u};return preview=e.io(a,{sync:!0,data:f,method:"POST"}),preview.status===200&&(u=preview.responseText),u}},{ATTRS:{texfilteractive:{value:!1},contextid:{value:null},library:{value:{}},texdocsurl:{value:null}}})},"@VERSION@",{requires:["moodle-editor_atto-plugin","moodle-core-event","io","event-valuechange","tabview"]}); diff --git a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js index d909984cfd4..96744f1f3e1 100644 --- a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js +++ b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js @@ -48,6 +48,10 @@ var COMPONENTNAME = 'atto_equation', SUBMIT: '.' + CSS.SUBMIT, LIBRARY_BUTTON: '.' + CSS.LIBRARY + ' button' }, + DELIMITERS = { + START: '\\(', + END: '\\)' + }, TEMPLATES = { FORM: '' + '
' + @@ -72,7 +76,7 @@ var COMPONENTNAME = 'atto_equation', '{{#each library}}' + '
' + '{{#split "\n" elements}}' + - '' + + '' + '{{/split}}' + '
' + '{{/each}}' + @@ -111,10 +115,18 @@ 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 - or if MathJax is loaded in - // the page - enable this button. - if (this.get('texfilteractive') || (typeof MathJax !== "undefined")) { + // If there is a tex filter active - enable this button. + if (this.get('texfilteractive')) { // Add the button to the toolbar. this.addButton({ icon: 'e/math', @@ -129,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('' + DELIMITERS.START + ' ' + texNode.get('text') + ' ' + DELIMITERS.END + ''); + texNode.replace(replacement); + }); } + }, /** @@ -162,10 +181,8 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. tabview.render(); dialogue.show(); - // If MathJax is available, get it to render all the library buttons at once. - if (typeof MathJax !== "undefined") { - MathJax.Hub.Queue(["Typeset", MathJax.Hub, library.getDOMNode()]); - } + // 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) { @@ -187,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) { @@ -196,14 +214,28 @@ 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]*)\$\$/); + // \( blah \) + patterns.push(/\\\(([\S\s]*)\\\)/); + // \[ blah \] + patterns.push(/\\\[([\S\s]*)\\\]/); + // [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) { + equation.shift(); + // Remember the inner match so we can replace it later. + this.sourceEquation = equation = equation.shift(); + + return equation; + } } + + this.sourceEquation = ''; return false; }, @@ -218,11 +250,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({ @@ -235,18 +266,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); } @@ -255,6 +284,26 @@ 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. + * + * @param {function} fn + * @param {Integer} 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. * @@ -272,7 +321,6 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. cursorLatex = '\\square ', isChar; - if (e) { e.preventDefault(); } @@ -285,7 +333,7 @@ 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; } @@ -294,25 +342,21 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. equation = prefix + equation.substring(0, currentPos) + cursorLatex + equation.substring(currentPos); var previewNode = this._content.one(SELECTORS.EQUATION_PREVIEW); - if (typeof MathJax !== "undefined") { - // If MathJax is available, get it to update the preview without an ajax request. - previewNode.setHTML('$$' + equation + '$$'); - MathJax.Hub.Queue(["Typeset", MathJax.Hub, previewNode.getDOMNode()]); - } else { - // Fall back on 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 + ' $$' - }; + 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 + }; - preview = Y.io(url, { sync: true, - data: params }); - if (preview.status === 200) { - previewNode.setHTML(preview.responseText); - } + preview = Y.io(url, { sync: true, + data: params }); + if (preview.status === 200) { + previewNode.setHTML(preview.responseText); + Y.fire(M.core.event.FILTER_CONTENT_UPDATED, {nodes: (new Y.NodeList(previewNode))}); } }, @@ -337,9 +381,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; @@ -406,7 +450,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); } @@ -416,28 +460,26 @@ 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 }); - // Only render the library via ajax is MathJax is not available. - if (typeof MathJax === "undefined") { - var url = M.cfg.wwwroot + '/lib/editor/atto/plugins/equation/ajax.php'; - var params = { - sesskey: M.cfg.sesskey, - contextid: this.get('contextid'), - action: 'filtertext', - text: content - }; + var url = M.cfg.wwwroot + '/lib/editor/atto/plugins/equation/ajax.php'; + var params = { + sesskey: M.cfg.sesskey, + contextid: this.get('contextid'), + action: 'filtertext', + text: content + }; - preview = Y.io(url, { - sync: true, - data: params, - method: 'POST' - }); + preview = Y.io(url, { + sync: true, + data: params, + method: 'POST' + }); - if (preview.status === 200) { - content = preview.responseText; - } + if (preview.status === 200) { + content = preview.responseText; } return content; } @@ -452,6 +494,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. * @@ -481,8 +524,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"]}); diff --git a/lib/editor/atto/plugins/equation/yui/src/button/js/button.js b/lib/editor/atto/plugins/equation/yui/src/button/js/button.js index 2e30f82b765..92a3f209016 100644 --- a/lib/editor/atto/plugins/equation/yui/src/button/js/button.js +++ b/lib/editor/atto/plugins/equation/yui/src/button/js/button.js @@ -46,6 +46,10 @@ var COMPONENTNAME = 'atto_equation', SUBMIT: '.' + CSS.SUBMIT, LIBRARY_BUTTON: '.' + CSS.LIBRARY + ' button' }, + DELIMITERS = { + START: '\\(', + END: '\\)' + }, TEMPLATES = { FORM: '' + '' + @@ -70,7 +74,7 @@ var COMPONENTNAME = 'atto_equation', '{{#each library}}' + '
' + '{{#split "\n" elements}}' + - '' + + '' + '{{/split}}' + '
' + '{{/each}}' + @@ -109,10 +113,18 @@ 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 - or if MathJax is loaded in - // the page - enable this button. - if (this.get('texfilteractive') || (typeof MathJax !== "undefined")) { + // If there is a tex filter active - enable this button. + if (this.get('texfilteractive')) { // Add the button to the toolbar. this.addButton({ icon: 'e/math', @@ -127,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('' + DELIMITERS.START + ' ' + texNode.get('text') + ' ' + DELIMITERS.END + ''); + texNode.replace(replacement); + }); } + }, /** @@ -160,10 +179,8 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. tabview.render(); dialogue.show(); - // If MathJax is available, get it to render all the library buttons at once. - if (typeof MathJax !== "undefined") { - MathJax.Hub.Queue(["Typeset", MathJax.Hub, library.getDOMNode()]); - } + // 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) { @@ -185,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) { @@ -194,14 +212,28 @@ 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]*)\$\$/); + // \( blah \) + patterns.push(/\\\(([\S\s]*)\\\)/); + // \[ blah \] + patterns.push(/\\\[([\S\s]*)\\\]/); + // [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) { + equation.shift(); + // Remember the inner match so we can replace it later. + this.sourceEquation = equation = equation.shift(); + + return equation; + } } + + this.sourceEquation = ''; return false; }, @@ -216,11 +248,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({ @@ -233,18 +264,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); } @@ -253,6 +282,26 @@ 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. + * + * @param {function} fn + * @param {Integer} 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. * @@ -270,7 +319,6 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. cursorLatex = '\\square ', isChar; - if (e) { e.preventDefault(); } @@ -283,7 +331,7 @@ 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; } @@ -292,25 +340,21 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. equation = prefix + equation.substring(0, currentPos) + cursorLatex + equation.substring(currentPos); var previewNode = this._content.one(SELECTORS.EQUATION_PREVIEW); - if (typeof MathJax !== "undefined") { - // If MathJax is available, get it to update the preview without an ajax request. - previewNode.setHTML('$$' + equation + '$$'); - MathJax.Hub.Queue(["Typeset", MathJax.Hub, previewNode.getDOMNode()]); - } else { - // Fall back on 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 + ' $$' - }; + 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 + }; - preview = Y.io(url, { sync: true, - data: params }); - if (preview.status === 200) { - previewNode.setHTML(preview.responseText); - } + preview = Y.io(url, { sync: true, + data: params }); + if (preview.status === 200) { + previewNode.setHTML(preview.responseText); + Y.fire(M.core.event.FILTER_CONTENT_UPDATED, {nodes: (new Y.NodeList(previewNode))}); } }, @@ -335,9 +379,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; @@ -405,7 +449,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); } @@ -415,28 +459,26 @@ 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 }); - // Only render the library via ajax is MathJax is not available. - if (typeof MathJax === "undefined") { - var url = M.cfg.wwwroot + '/lib/editor/atto/plugins/equation/ajax.php'; - var params = { - sesskey: M.cfg.sesskey, - contextid: this.get('contextid'), - action: 'filtertext', - text: content - }; + var url = M.cfg.wwwroot + '/lib/editor/atto/plugins/equation/ajax.php'; + var params = { + sesskey: M.cfg.sesskey, + contextid: this.get('contextid'), + action: 'filtertext', + text: content + }; - preview = Y.io(url, { - sync: true, - data: params, - method: 'POST' - }); + preview = Y.io(url, { + sync: true, + data: params, + method: 'POST' + }); - if (preview.status === 200) { - content = preview.responseText; - } + if (preview.status === 200) { + content = preview.responseText; } return content; } @@ -451,6 +493,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. * @@ -480,5 +523,6 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. texdocsurl: { value: null } + } }); diff --git a/lib/editor/atto/plugins/equation/yui/src/button/meta/button.json b/lib/editor/atto/plugins/equation/yui/src/button/meta/button.json index c0d749fae02..6103cb338de 100644 --- a/lib/editor/atto/plugins/equation/yui/src/button/meta/button.json +++ b/lib/editor/atto/plugins/equation/yui/src/button/meta/button.json @@ -2,6 +2,7 @@ "moodle-atto_equation-button": { "requires": [ "moodle-editor_atto-plugin", + "moodle-core-event", "io", "event-valuechange", "tabview" diff --git a/lib/yui/build/moodle-core-event/moodle-core-event-debug.js b/lib/yui/build/moodle-core-event/moodle-core-event-debug.js new file mode 100644 index 00000000000..14c774f25f3 --- /dev/null +++ b/lib/yui/build/moodle-core-event/moodle-core-event-debug.js @@ -0,0 +1,63 @@ +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 . + +/** + * @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.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. + // This event must contain a property "nodes" listing the root of any new nodes in the DOM (as a NodeList). + // To trigger this event use M.core.Event.fire(M.core.Event.FILTER_CONTENT_UPDATED, {nodes: list}); + 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); + } +}; + +for (var key in M.core.event) { + Y.publish(M.core.event[key], eventDefaultConfig); +} + + + + +}, '@VERSION@', {"requires": ["event-custom"]}); diff --git a/lib/yui/build/moodle-core-event/moodle-core-event-min.js b/lib/yui/build/moodle-core-event/moodle-core-event-min.js new file mode 100644 index 00000000000..5b0a2653bc8 --- /dev/null +++ b/lib/yui/build/moodle-core-event/moodle-core-event-min.js @@ -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){}};for(var i in M.core.event)e.publish(M.core.event[i],r)},"@VERSION@",{requires:["event-custom"]}); diff --git a/lib/yui/build/moodle-core-event/moodle-core-event.js b/lib/yui/build/moodle-core-event/moodle-core-event.js new file mode 100644 index 00000000000..60a6b176925 --- /dev/null +++ b/lib/yui/build/moodle-core-event/moodle-core-event.js @@ -0,0 +1,60 @@ +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 . + +/** + * @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.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. + // This event must contain a property "nodes" listing the root of any new nodes in the DOM (as a NodeList). + // To trigger this event use M.core.Event.fire(M.core.Event.FILTER_CONTENT_UPDATED, {nodes: list}); + FILTER_CONTENT_UPDATED: "filter-content-updated" +}; + + +var eventDefaultConfig = { + emitFacade: true, + defaultFn: function(e) { + }, + preventedFn: function(e) { + }, + stoppedFn: function(e) { + } +}; + +for (var key in M.core.event) { + Y.publish(M.core.event[key], eventDefaultConfig); +} + + + + +}, '@VERSION@', {"requires": ["event-custom"]}); diff --git a/lib/yui/src/event/build.json b/lib/yui/src/event/build.json new file mode 100644 index 00000000000..62712457527 --- /dev/null +++ b/lib/yui/src/event/build.json @@ -0,0 +1,10 @@ +{ + "name": "moodle-core-event", + "builds": { + "moodle-core-event": { + "jsfiles": [ + "event.js" + ] + } + } +} diff --git a/lib/yui/src/event/js/event.js b/lib/yui/src/event/js/event.js new file mode 100644 index 00000000000..7c861869490 --- /dev/null +++ b/lib/yui/src/event/js/event.js @@ -0,0 +1,58 @@ +// 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 . + +/** + * @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.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. + // This event must contain a property "nodes" listing the root of any new nodes in the DOM (as a NodeList). + // To trigger this event use M.core.Event.fire(M.core.Event.FILTER_CONTENT_UPDATED, {nodes: list}); + 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); + } +}; + +for (var key in M.core.event) { + Y.publish(M.core.event[key], eventDefaultConfig); +} + + diff --git a/lib/yui/src/event/meta/event.json b/lib/yui/src/event/meta/event.json new file mode 100644 index 00000000000..89a7d388714 --- /dev/null +++ b/lib/yui/src/event/meta/event.json @@ -0,0 +1,7 @@ +{ + "moodle-core-event": { + "requires": [ + "event-custom" + ] + } +} From 441f94b26350941e6e13cae65ccdb7fdac169499 Mon Sep 17 00:00:00 2001 From: Damyon Wiese Date: Thu, 3 Apr 2014 14:26:56 +0800 Subject: [PATCH 5/8] MDL-43856 MathJax: Coding style cleanups. --- filter/mathjaxloader/filter.php | 8 ++--- .../lang/en/filter_mathjaxloader.php | 7 ++--- filter/mathjaxloader/settings.php | 10 +++---- ...oodle-filter_mathjaxloader-loader-debug.js | 5 ++-- .../moodle-filter_mathjaxloader-loader.js | 5 ++-- .../mathjaxloader/yui/src/loader/js/loader.js | 5 ++-- lib/editor/atto/plugins/equation/settings.php | 24 +++++++-------- .../moodle-atto_equation-button-debug.js | 28 +++++++++++------- .../moodle-atto_equation-button-min.js | 4 +-- .../moodle-atto_equation-button.js | 28 +++++++++++------- .../equation/yui/src/button/js/button.js | 28 +++++++++++------- .../moodle-core-event-debug.js | 29 ++++++++++++------- .../moodle-core-event-min.js | 2 +- .../moodle-core-event/moodle-core-event.js | 29 ++++++++++++------- lib/yui/src/event/js/event.js | 29 ++++++++++++------- 15 files changed, 141 insertions(+), 100 deletions(-) diff --git a/filter/mathjaxloader/filter.php b/filter/mathjaxloader/filter.php index 4876429b277..6c1be05ca28 100644 --- a/filter/mathjaxloader/filter.php +++ b/filter/mathjaxloader/filter.php @@ -135,14 +135,14 @@ class filter_mathjaxloader extends moodle_text_filter { $legacy = get_config('filter_mathjaxloader', 'texfiltercompatibility'); if ($legacy) { // This replaces any of the tex filter maths delimiters with the default for inline maths in MathJAX "\( blah \)". - // blah + // E.g. " blah ". $text = preg_replace('|<(/?) *tex( [^>]*)?>|u', '[\1tex]', $text); - // [tex.*] blah [/tex] + // E.g. "[tex.*] blah [/tex]". $text = str_replace('[tex]', '\\(', $text); $text = str_replace('[/tex]', '\\)', $text); - // $$ blah $$ + // E.g. "$$ blah $$". $text = preg_replace('|\$\$[\S\s]\$\$|u', '\\(\1\\)', $text); - // \[ blah \] + // E.g. "\[ blah \]". $text = str_replace('\\[', '\\(', $text); $text = str_replace('\\]', '\\)', $text); } diff --git a/filter/mathjaxloader/lang/en/filter_mathjaxloader.php b/filter/mathjaxloader/lang/en/filter_mathjaxloader.php index f244356bba6..c19a4bffd1e 100644 --- a/filter/mathjaxloader/lang/en/filter_mathjaxloader.php +++ b/filter/mathjaxloader/lang/en/filter_mathjaxloader.php @@ -32,15 +32,14 @@ $string['httpurl_help'] = 'Full URL to MathJax library. Used when the page is lo $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. +$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. +$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. +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.'; - diff --git a/filter/mathjaxloader/settings.php b/filter/mathjaxloader/settings.php index 2da905a3879..6656d9c15b3 100644 --- a/filter/mathjaxloader/settings.php +++ b/filter/mathjaxloader/settings.php @@ -26,8 +26,8 @@ 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')); + new lang_string('localinstall', 'filter_mathjaxloader'), + new lang_string('localinstall_help', 'filter_mathjaxloader')); $settings->add($item); $item = new admin_setting_configtext('filter_mathjaxloader/httpurl', @@ -45,9 +45,9 @@ if ($ADMIN->fulltree) { $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); + new lang_string('texfiltercompatibility', 'filter_mathjaxloader'), + new lang_string('texfiltercompatibility_help', 'filter_mathjaxloader'), + 0); $settings->add($item); $default = ' diff --git a/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader-debug.js b/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader-debug.js index ea088053a12..6535e4d3856 100644 --- a/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader-debug.js +++ b/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader-debug.js @@ -35,13 +35,12 @@ M.filter_mathjaxloader = M.filter_mathjaxloader || { MathJax.Localization.setLocale(params.lang); Y.all('.filter_mathjaxloader_equation').each(function(node) { - MathJax.Hub.Queue(["Typeset",MathJax.Hub,node.getDOMNode()]); + MathJax.Hub.Queue(["Typeset", MathJax.Hub, node.getDOMNode()]); }); MathJax.Hub.Configured(); // 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); }, @@ -53,7 +52,7 @@ M.filter_mathjaxloader = M.filter_mathjaxloader || { contentUpdated: function(event) { event.nodes.each(function (node) { node.all('.filter_mathjaxloader_equation').each(function(node) { - MathJax.Hub.Queue(["Typeset",MathJax.Hub,node.getDOMNode()]); + MathJax.Hub.Queue(["Typeset", MathJax.Hub, node.getDOMNode()]); }); }); } diff --git a/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader.js b/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader.js index ea088053a12..6535e4d3856 100644 --- a/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader.js +++ b/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader.js @@ -35,13 +35,12 @@ M.filter_mathjaxloader = M.filter_mathjaxloader || { MathJax.Localization.setLocale(params.lang); Y.all('.filter_mathjaxloader_equation').each(function(node) { - MathJax.Hub.Queue(["Typeset",MathJax.Hub,node.getDOMNode()]); + MathJax.Hub.Queue(["Typeset", MathJax.Hub, node.getDOMNode()]); }); MathJax.Hub.Configured(); // 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); }, @@ -53,7 +52,7 @@ M.filter_mathjaxloader = M.filter_mathjaxloader || { contentUpdated: function(event) { event.nodes.each(function (node) { node.all('.filter_mathjaxloader_equation').each(function(node) { - MathJax.Hub.Queue(["Typeset",MathJax.Hub,node.getDOMNode()]); + MathJax.Hub.Queue(["Typeset", MathJax.Hub, node.getDOMNode()]); }); }); } diff --git a/filter/mathjaxloader/yui/src/loader/js/loader.js b/filter/mathjaxloader/yui/src/loader/js/loader.js index 6aaf892cae6..00f58f8b51e 100644 --- a/filter/mathjaxloader/yui/src/loader/js/loader.js +++ b/filter/mathjaxloader/yui/src/loader/js/loader.js @@ -33,13 +33,12 @@ M.filter_mathjaxloader = M.filter_mathjaxloader || { MathJax.Localization.setLocale(params.lang); Y.all('.filter_mathjaxloader_equation').each(function(node) { - MathJax.Hub.Queue(["Typeset",MathJax.Hub,node.getDOMNode()]); + MathJax.Hub.Queue(["Typeset", MathJax.Hub, node.getDOMNode()]); }); MathJax.Hub.Configured(); // 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); }, @@ -51,7 +50,7 @@ M.filter_mathjaxloader = M.filter_mathjaxloader || { contentUpdated: function(event) { event.nodes.each(function (node) { node.all('.filter_mathjaxloader_equation').each(function(node) { - MathJax.Hub.Queue(["Typeset",MathJax.Hub,node.getDOMNode()]); + MathJax.Hub.Queue(["Typeset", MathJax.Hub, node.getDOMNode()]); }); }); } diff --git a/lib/editor/atto/plugins/equation/settings.php b/lib/editor/atto/plugins/equation/settings.php index a0af0245bcd..11d75bcf52a 100644 --- a/lib/editor/atto/plugins/equation/settings.php +++ b/lib/editor/atto/plugins/equation/settings.php @@ -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,9 +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); } diff --git a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-debug.js b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-debug.js index 687af4baf95..77ae2888560 100644 --- a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-debug.js +++ b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-debug.js @@ -214,22 +214,21 @@ 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. - // $$ blah $$ + // $$ blah $$. patterns.push(/\$\$([\S\s]*)\$\$/); - // \( blah \) + // E.g. "\( blah \)". patterns.push(/\\\(([\S\s]*)\\\)/); - // \[ blah \] + // E.g. "\[ blah \]". patterns.push(/\\\[([\S\s]*)\\\]/); - // [tex] blah [/tex] + // 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) { - equation.shift(); // Remember the inner match so we can replace it later. - this.sourceEquation = equation = equation.shift(); + this.sourceEquation = equation = equation[1]; return equation; } @@ -286,10 +285,13 @@ 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. + * 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 {Integer} delay - delay in milliseconds + * @param {Number} delay Delay in milliseconds * @method _throttle * @private */ @@ -319,7 +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(); @@ -352,8 +355,11 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. text: equation }; - preview = Y.io(url, { sync: true, - data: params }); + preview = Y.io(url, { + sync: true, + data: params + }); + if (preview.status === 200) { previewNode.setHTML(preview.responseText); Y.fire(M.core.event.FILTER_CONTENT_UPDATED, {nodes: (new Y.NodeList(previewNode))}); diff --git a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-min.js b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-min.js index 6e87451bb1f..85204f1668f 100644 --- a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-min.js +++ b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-min.js @@ -1,2 +1,2 @@ -YUI.add("moodle-atto_equation-button",function(e,t){var n="atto_equation",r={EQUATION_TEXT:"atto_equation_equation",EQUATION_PREVIEW:"atto_equation_preview",SUBMIT:"atto_equation_submit",LIBRARY:"atto_equation_library",LIBRARY_GROUP_PREFIX:"atto_equation_library"},i={LIBRARY_GROUP_PREFIX:"."+r.LIBRARY_GROUP_PREFIX,EQUATION_TEXT:"."+r.EQUATION_TEXT,EQUATION_PREVIEW:"."+r.EQUATION_PREVIEW,SUBMIT:"."+r.SUBMIT,LIBRARY_BUTTON:"."+r.LIBRARY+" button"},s={START:"\\(",END:"\\)"},o={FORM:'{{{library}}}

',LIBRARY:'
{{#each library}}
{{#split "\n" elements}}{{/split}}
{{/each}}
'};e.namespace("M.atto_equation").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{_currentSelection:null,_lastCursorPos:0,_content:null,_sourceEquation:"",initializer:function(){this.get("texfilteractive")&&(this.addButton({icon:"e/math",callback:this._displayDialogue}),this.get("host").on("atto:selectionchanged",function(){this._resolveEquation()?this.highlightButtons():this.unHighlightButtons()},this),this.editor.all("tex").each(function(t){var n=e.Node.create(""+s.START+" "+t.get("text")+" "+s.END+"");t.replace(n)}))},_displayDialogue:function(){this._currentSelection=this.get("host").getSelection();if(this._currentSelection===!1)return;var t=this.getDialogue({headerContent:M.util.get_string("pluginname",n),focusAfterHide:!0,width:600}),r=this._getDialogueContent();t.set("bodyContent",r);var s=r.one(i.LIBRARY_GROUP_PREFIX),o=new e.TabView({srcNode:s});o.render(),t.show(),e.fire(M.core.event.FILTER_CONTENT_UPDATED,{nodes:new e.NodeList(t.get("boundingBox"))});var u=this._resolveEquation();u&&r.one(i.EQUATION_TEXT).set("text",u),this._updatePreview(!1)},_resolveEquation:function(){var t=this.get("host").getSelectionParentNode(),n,r,i=[],s;if(!t)return!1;n=e.one(t).get("text"),i.push(/\$\$([\S\s]*)\$\$/),i.push(/\\\(([\S\s]*)\\\)/),i.push(/\\\[([\S\s]*)\\\]/),i.push(/\[tex\]([\S\s]*)\[\/tex\]/);for(s=0;s0)a-=1;c=/[a-zA-Z\{\}]/;while(c.test(r.charAt(a))&&a0)i=r.shift().trim(),s+=n.fn(i);return s}),u=t({elementid:this.get("host").get("elementid"),component:n,library:i,CSS:r,DELIMITERS:s});var a=M.cfg.wwwroot+"/lib/editor/atto/plugins/equation/ajax.php",f={sesskey -:M.cfg.sesskey,contextid:this.get("contextid"),action:"filtertext",text:u};return preview=e.io(a,{sync:!0,data:f,method:"POST"}),preview.status===200&&(u=preview.responseText),u}},{ATTRS:{texfilteractive:{value:!1},contextid:{value:null},library:{value:{}},texdocsurl:{value:null}}})},"@VERSION@",{requires:["moodle-editor_atto-plugin","moodle-core-event","io","event-valuechange","tabview"]}); +YUI.add("moodle-atto_equation-button",function(e,t){var n="atto_equation",r={EQUATION_TEXT:"atto_equation_equation",EQUATION_PREVIEW:"atto_equation_preview",SUBMIT:"atto_equation_submit",LIBRARY:"atto_equation_library",LIBRARY_GROUP_PREFIX:"atto_equation_library"},i={LIBRARY_GROUP_PREFIX:"."+r.LIBRARY_GROUP_PREFIX,EQUATION_TEXT:"."+r.EQUATION_TEXT,EQUATION_PREVIEW:"."+r.EQUATION_PREVIEW,SUBMIT:"."+r.SUBMIT,LIBRARY_BUTTON:"."+r.LIBRARY+" button"},s={START:"\\(",END:"\\)"},o={FORM:'
{{{library}}}

',LIBRARY:'
{{#each library}}
{{#split "\n" elements}}{{/split}}
{{/each}}
'};e.namespace("M.atto_equation").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{_currentSelection:null,_lastCursorPos:0,_content:null,_sourceEquation:"",initializer:function(){this.get("texfilteractive")&&(this.addButton({icon:"e/math",callback:this._displayDialogue}),this.get("host").on("atto:selectionchanged",function(){this._resolveEquation()?this.highlightButtons():this.unHighlightButtons()},this),this.editor.all("tex").each(function(t){var n=e.Node.create(""+s.START+" "+t.get("text")+" "+s.END+"");t.replace(n)}))},_displayDialogue:function(){this._currentSelection=this.get("host").getSelection();if(this._currentSelection===!1)return;var t=this.getDialogue({headerContent:M.util.get_string("pluginname",n),focusAfterHide:!0,width:600}),r=this._getDialogueContent();t.set("bodyContent",r);var s=r.one(i.LIBRARY_GROUP_PREFIX),o=new e.TabView({srcNode:s});o.render(),t.show(),e.fire(M.core.event.FILTER_CONTENT_UPDATED,{nodes:new e.NodeList(t.get("boundingBox"))});var u=this._resolveEquation();u&&r.one(i.EQUATION_TEXT).set("text",u),this._updatePreview(!1)},_resolveEquation:function(){var t=this.get("host").getSelectionParentNode(),n,r,i=[],s;if(!t)return!1;n=e.one(t).get("text"),i.push(/\$\$([\S\s]*)\$\$/),i.push(/\\\(([\S\s]*)\\\)/),i.push(/\\\[([\S\s]*)\\\]/),i.push(/\[tex\]([\S\s]*)\[\/tex\]/);for(s=0;s0)a-=1;c=/[a-zA-Z\{\}]/;while(c.test(r.charAt(a))&&a0)i=r.shift().trim(),s+=n.fn(i);return s}),u=t({elementid:this.get("host").get("elementid"),component:n,library:i,CSS:r,DELIMITERS:s});var a=M.cfg.wwwroot+"/lib/editor/atto/plugins/equation/ajax.php",f={sesskey:M.cfg.sesskey,contextid +:this.get("contextid"),action:"filtertext",text:u};return preview=e.io(a,{sync:!0,data:f,method:"POST"}),preview.status===200&&(u=preview.responseText),u}},{ATTRS:{texfilteractive:{value:!1},contextid:{value:null},library:{value:{}},texdocsurl:{value:null}}})},"@VERSION@",{requires:["moodle-editor_atto-plugin","moodle-core-event","io","event-valuechange","tabview"]}); diff --git a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js index 96744f1f3e1..1153695e2d1 100644 --- a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js +++ b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js @@ -214,22 +214,21 @@ 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. - // $$ blah $$ + // $$ blah $$. patterns.push(/\$\$([\S\s]*)\$\$/); - // \( blah \) + // E.g. "\( blah \)". patterns.push(/\\\(([\S\s]*)\\\)/); - // \[ blah \] + // E.g. "\[ blah \]". patterns.push(/\\\[([\S\s]*)\\\]/); - // [tex] blah [/tex] + // 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) { - equation.shift(); // Remember the inner match so we can replace it later. - this.sourceEquation = equation = equation.shift(); + this.sourceEquation = equation = equation[1]; return equation; } @@ -286,10 +285,13 @@ 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. + * 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 {Integer} delay - delay in milliseconds + * @param {Number} delay Delay in milliseconds * @method _throttle * @private */ @@ -319,7 +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(); @@ -352,8 +355,11 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. text: equation }; - preview = Y.io(url, { sync: true, - data: params }); + preview = Y.io(url, { + sync: true, + data: params + }); + if (preview.status === 200) { previewNode.setHTML(preview.responseText); Y.fire(M.core.event.FILTER_CONTENT_UPDATED, {nodes: (new Y.NodeList(previewNode))}); diff --git a/lib/editor/atto/plugins/equation/yui/src/button/js/button.js b/lib/editor/atto/plugins/equation/yui/src/button/js/button.js index 92a3f209016..9d8185a181f 100644 --- a/lib/editor/atto/plugins/equation/yui/src/button/js/button.js +++ b/lib/editor/atto/plugins/equation/yui/src/button/js/button.js @@ -212,22 +212,21 @@ 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. - // $$ blah $$ + // $$ blah $$. patterns.push(/\$\$([\S\s]*)\$\$/); - // \( blah \) + // E.g. "\( blah \)". patterns.push(/\\\(([\S\s]*)\\\)/); - // \[ blah \] + // E.g. "\[ blah \]". patterns.push(/\\\[([\S\s]*)\\\]/); - // [tex] blah [/tex] + // 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) { - equation.shift(); // Remember the inner match so we can replace it later. - this.sourceEquation = equation = equation.shift(); + this.sourceEquation = equation = equation[1]; return equation; } @@ -284,10 +283,13 @@ 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. + * 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 {Integer} delay - delay in milliseconds + * @param {Number} delay Delay in milliseconds * @method _throttle * @private */ @@ -317,7 +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(); @@ -350,8 +353,11 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. text: equation }; - preview = Y.io(url, { sync: true, - data: params }); + preview = Y.io(url, { + sync: true, + data: params + }); + if (preview.status === 200) { previewNode.setHTML(preview.responseText); Y.fire(M.core.event.FILTER_CONTENT_UPDATED, {nodes: (new Y.NodeList(previewNode))}); diff --git a/lib/yui/build/moodle-core-event/moodle-core-event-debug.js b/lib/yui/build/moodle-core-event/moodle-core-event-debug.js index 14c774f25f3..9bb34797b66 100644 --- a/lib/yui/build/moodle-core-event/moodle-core-event-debug.js +++ b/lib/yui/build/moodle-core-event/moodle-core-event-debug.js @@ -25,17 +25,22 @@ 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.event + * @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. - // This event must contain a property "nodes" listing the root of any new nodes in the DOM (as a NodeList). - // To trigger this event use M.core.Event.fire(M.core.Event.FILTER_CONTENT_UPDATED, {nodes: list}); + /** + * 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" }; @@ -53,11 +58,15 @@ var eventDefaultConfig = { } }; -for (var key in M.core.event) { - Y.publish(M.core.event[key], eventDefaultConfig); +// 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"]}); diff --git a/lib/yui/build/moodle-core-event/moodle-core-event-min.js b/lib/yui/build/moodle-core-event/moodle-core-event-min.js index 5b0a2653bc8..b0168680ce5 100644 --- a/lib/yui/build/moodle-core-event/moodle-core-event-min.js +++ b/lib/yui/build/moodle-core-event/moodle-core-event-min.js @@ -1 +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){}};for(var i in M.core.event)e.publish(M.core.event[i],r)},"@VERSION@",{requires:["event-custom"]}); +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"]}); diff --git a/lib/yui/build/moodle-core-event/moodle-core-event.js b/lib/yui/build/moodle-core-event/moodle-core-event.js index 60a6b176925..d2c481f2c0e 100644 --- a/lib/yui/build/moodle-core-event/moodle-core-event.js +++ b/lib/yui/build/moodle-core-event/moodle-core-event.js @@ -25,17 +25,22 @@ 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.event + * @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. - // This event must contain a property "nodes" listing the root of any new nodes in the DOM (as a NodeList). - // To trigger this event use M.core.Event.fire(M.core.Event.FILTER_CONTENT_UPDATED, {nodes: list}); + /** + * 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" }; @@ -50,11 +55,15 @@ var eventDefaultConfig = { } }; -for (var key in M.core.event) { - Y.publish(M.core.event[key], eventDefaultConfig); +// 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"]}); diff --git a/lib/yui/src/event/js/event.js b/lib/yui/src/event/js/event.js index 7c861869490..39fe4f6c862 100644 --- a/lib/yui/src/event/js/event.js +++ b/lib/yui/src/event/js/event.js @@ -23,17 +23,22 @@ 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.event + * @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. - // This event must contain a property "nodes" listing the root of any new nodes in the DOM (as a NodeList). - // To trigger this event use M.core.Event.fire(M.core.Event.FILTER_CONTENT_UPDATED, {nodes: list}); + /** + * 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" }; @@ -51,8 +56,12 @@ var eventDefaultConfig = { } }; -for (var key in M.core.event) { - Y.publish(M.core.event[key], eventDefaultConfig); +// 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. From 894e20395d3a91d8a015c7f631c6d27e765dd315 Mon Sep 17 00:00:00 2001 From: Damyon Wiese Date: Fri, 4 Apr 2014 11:18:54 +0800 Subject: [PATCH 6/8] MDL-43856 MathJax: Add a setting for additional delimiters to support e.g. ASCIIMATH --- filter/mathjaxloader/filter.php | 11 ++++++++++- .../mathjaxloader/lang/en/filter_mathjaxloader.php | 12 ++++++------ filter/mathjaxloader/settings.php | 7 +++++++ 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/filter/mathjaxloader/filter.php b/filter/mathjaxloader/filter.php index 6c1be05ca28..ba13211d5fe 100644 --- a/filter/mathjaxloader/filter.php +++ b/filter/mathjaxloader/filter.php @@ -133,6 +133,7 @@ class filter_mathjaxloader extends moodle_text_filter { */ public function filter($text, array $options = array()) { $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. " blah ". @@ -151,7 +152,15 @@ class filter_mathjaxloader extends moodle_text_filter { $hasdisplay = (strpos($text, '$$') !== false) || (strpos($text, '\\[') !== false && strpos($text, '\\]') !== false); - if ($hasinline || $hasdisplay) { + $hasextra = false; + + foreach ($extradelimiters as $extra) { + if ($extra && strpos($text, $extra) !== false) { + $hasextra = true; + break; + } + } + if ($hasinline || $hasdisplay || $hasextra) { // Only call init if there is at least one equation on the page. $this->lazy_init(); return '' . $text . ''; diff --git a/filter/mathjaxloader/lang/en/filter_mathjaxloader.php b/filter/mathjaxloader/lang/en/filter_mathjaxloader.php index c19a4bffd1e..8ba35837d25 100644 --- a/filter/mathjaxloader/lang/en/filter_mathjaxloader.php +++ b/filter/mathjaxloader/lang/en/filter_mathjaxloader.php @@ -15,18 +15,18 @@ // along with Moodle. If not, see . /** - * Strings for component 'filter_mathjax', language 'en'. + * Strings for component 'filter_mathjaxloader', language 'en'. * - * Note: use filter/mathjax/cli/update_lang_files.php script to import strings from upstream JS lang files. - * - * @package filter_mathjax + * @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 */ - -//== Custom Moodle strings that are not part of upstream MathJax == $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'; diff --git a/filter/mathjaxloader/settings.php b/filter/mathjaxloader/settings.php index 6656d9c15b3..deeb9751e1f 100644 --- a/filter/mathjaxloader/settings.php +++ b/filter/mathjaxloader/settings.php @@ -76,4 +76,11 @@ MathJax.Hub.Config({ $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); + } From aaf6f1e8f2d7c450d5d8f78bf36d6d9958343ddb Mon Sep 17 00:00:00 2001 From: Damyon Wiese Date: Fri, 4 Apr 2014 14:18:50 +0800 Subject: [PATCH 7/8] MDL-43856 MathJax: Dynamically load mathjax into the page (as required). This is to solve a tricky edge case, when a page has no equations, and then an AJAX action loads filtered content which contains an equation. (The glossary filter can do this). To fix this I split the loading into configuration and typesetting. The config always happens, but does not load the external mathjax libraries. The typesetting happens when an equation is found - and is what triggers the load of MathJax. This can happen in response to an AJAX event. --- filter/mathjaxloader/filter.php | 13 +-- ...oodle-filter_mathjaxloader-loader-debug.js | 80 ++++++++++++++++--- .../moodle-filter_mathjaxloader-loader-min.js | 2 +- .../moodle-filter_mathjaxloader-loader.js | 80 ++++++++++++++++--- .../mathjaxloader/yui/src/loader/js/loader.js | 78 +++++++++++++++--- .../yui/src/loader/meta/loader.json | 3 +- 6 files changed, 215 insertions(+), 41 deletions(-) diff --git a/filter/mathjaxloader/filter.php b/filter/mathjaxloader/filter.php index ba13211d5fe..a22e5bf130d 100644 --- a/filter/mathjaxloader/filter.php +++ b/filter/mathjaxloader/filter.php @@ -94,8 +94,8 @@ class filter_mathjaxloader extends moodle_text_filter { * @param moodle_page $page The current page. * @param context $context The current context. */ - public function lazy_init() { - global $CFG, $PAGE; + public function setup($page, $context) { + global $CFG; // This only requires execution once per request. static $jsinitialised = false; @@ -113,13 +113,13 @@ class filter_mathjaxloader extends moodle_text_filter { 'fullpath' => $url ); - $PAGE->requires->js_module($moduleconfig); + $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.init', array($params)); + $page->requires->yui_module('moodle-filter_mathjaxloader-loader', 'M.filter_mathjaxloader.configure', array($params)); $jsinitialised = true; } @@ -132,6 +132,8 @@ class filter_mathjaxloader extends moodle_text_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) { @@ -161,8 +163,7 @@ class filter_mathjaxloader extends moodle_text_filter { } } if ($hasinline || $hasdisplay || $hasextra) { - // Only call init if there is at least one equation on the page. - $this->lazy_init(); + $PAGE->requires->yui_module('moodle-filter_mathjaxloader-loader', 'M.filter_mathjaxloader.typeset'); return '' . $text . ''; } return $text; diff --git a/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader-debug.js b/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader-debug.js index 6535e4d3856..15f0c756eea 100644 --- a/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader-debug.js +++ b/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader-debug.js @@ -23,7 +23,34 @@ YUI.add('moodle-filter_mathjaxloader-loader', function (Y, NAME) { * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ M.filter_mathjaxloader = M.filter_mathjaxloader || { - init: function(params) { + + /** + * 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" @@ -32,31 +59,62 @@ M.filter_mathjaxloader = M.filter_mathjaxloader || { script[(window.opera ? "innerHTML" : "text")] = params.mathjaxconfig; document.getElementsByTagName("head")[0].appendChild(script); - MathJax.Localization.setLocale(params.lang); - - Y.all('.filter_mathjaxloader_equation').each(function(node) { - MathJax.Hub.Queue(["Typeset", MathJax.Hub, node.getDOMNode()]); - }); - MathJax.Hub.Configured(); + // 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) { - event.nodes.each(function (node) { - node.all('.filter_mathjaxloader_equation').each(function(node) { - MathJax.Hub.Queue(["Typeset", MathJax.Hub, node.getDOMNode()]); + 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", "mathjax"]}); +}, '@VERSION@', {"requires": ["moodle-core-event"]}); diff --git a/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader-min.js b/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader-min.js index 1cb456a36c3..a5cd7f1a411 100644 --- a/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader-min.js +++ b/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader-min.js @@ -1 +1 @@ -YUI.add("moodle-filter_mathjaxloader-loader",function(e,t){M.filter_mathjaxloader=M.filter_mathjaxloader||{init: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),MathJax.Localization.setLocale(t.lang),e.all(".filter_mathjaxloader_equation").each(function(e){MathJax.Hub.Queue(["Typeset",MathJax.Hub,e.getDOMNode()])}),MathJax.Hub.Configured(),e.on(M.core.event.FILTER_CONTENT_UPDATED,this.contentUpdated,this)},contentUpdated:function(e){e.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","mathjax"]}); +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"]}); diff --git a/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader.js b/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader.js index 6535e4d3856..15f0c756eea 100644 --- a/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader.js +++ b/filter/mathjaxloader/yui/build/moodle-filter_mathjaxloader-loader/moodle-filter_mathjaxloader-loader.js @@ -23,7 +23,34 @@ YUI.add('moodle-filter_mathjaxloader-loader', function (Y, NAME) { * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ M.filter_mathjaxloader = M.filter_mathjaxloader || { - init: function(params) { + + /** + * 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" @@ -32,31 +59,62 @@ M.filter_mathjaxloader = M.filter_mathjaxloader || { script[(window.opera ? "innerHTML" : "text")] = params.mathjaxconfig; document.getElementsByTagName("head")[0].appendChild(script); - MathJax.Localization.setLocale(params.lang); - - Y.all('.filter_mathjaxloader_equation').each(function(node) { - MathJax.Hub.Queue(["Typeset", MathJax.Hub, node.getDOMNode()]); - }); - MathJax.Hub.Configured(); + // 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) { - event.nodes.each(function (node) { - node.all('.filter_mathjaxloader_equation').each(function(node) { - MathJax.Hub.Queue(["Typeset", MathJax.Hub, node.getDOMNode()]); + 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", "mathjax"]}); +}, '@VERSION@', {"requires": ["moodle-core-event"]}); diff --git a/filter/mathjaxloader/yui/src/loader/js/loader.js b/filter/mathjaxloader/yui/src/loader/js/loader.js index 00f58f8b51e..fb7fe1be167 100644 --- a/filter/mathjaxloader/yui/src/loader/js/loader.js +++ b/filter/mathjaxloader/yui/src/loader/js/loader.js @@ -21,7 +21,34 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ M.filter_mathjaxloader = M.filter_mathjaxloader || { - init: function(params) { + + /** + * 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" @@ -30,27 +57,58 @@ M.filter_mathjaxloader = M.filter_mathjaxloader || { script[(window.opera ? "innerHTML" : "text")] = params.mathjaxconfig; document.getElementsByTagName("head")[0].appendChild(script); - MathJax.Localization.setLocale(params.lang); - - Y.all('.filter_mathjaxloader_equation').each(function(node) { - MathJax.Hub.Queue(["Typeset", MathJax.Hub, node.getDOMNode()]); - }); - MathJax.Hub.Configured(); + // 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) { - event.nodes.each(function (node) { - node.all('.filter_mathjaxloader_equation').each(function(node) { - MathJax.Hub.Queue(["Typeset", MathJax.Hub, node.getDOMNode()]); + 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()]); + }); }); }); } diff --git a/filter/mathjaxloader/yui/src/loader/meta/loader.json b/filter/mathjaxloader/yui/src/loader/meta/loader.json index 20e4640a20f..eaca4224906 100644 --- a/filter/mathjaxloader/yui/src/loader/meta/loader.json +++ b/filter/mathjaxloader/yui/src/loader/meta/loader.json @@ -1,8 +1,7 @@ { "moodle-filter_mathjaxloader-loader": { "requires": [ - "moodle-core-event", - "mathjax" + "moodle-core-event" ] } } From 1c9785d52d59805b3ca8126a80f14cbf3456c827 Mon Sep 17 00:00:00 2001 From: Damyon Wiese Date: Mon, 7 Apr 2014 15:44:06 +0800 Subject: [PATCH 8/8] MDL-43856 MathJAX: Small correction to readme_moodle.txt The CDN urls have been moved to settings.php. --- filter/mathjaxloader/readme_moodle.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filter/mathjaxloader/readme_moodle.txt b/filter/mathjaxloader/readme_moodle.txt index 76355a381b1..a46c89569c7 100644 --- a/filter/mathjaxloader/readme_moodle.txt +++ b/filter/mathjaxloader/readme_moodle.txt @@ -12,4 +12,4 @@ correctly load MathJax into a page from the CDN. Alternatively you can download 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 CDN urls in filter.php - and update the list of language mappings - also in filter.php. +the default CDN urls in settings.php - and update the list of language mappings - in filter.php.