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)
This commit is contained in:
Damyon Wiese
2014-04-03 14:36:36 +08:00
parent 289ed254a7
commit cc90cedc85
26 changed files with 656 additions and 240 deletions
@@ -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"
]
});
@@ -1 +1 @@
YUI.add("moodle-filter_glossary-autolinker",function(e,t){var n="Glossary filter autolinker",r="width",i="height",s="menubar",o="location",u="scrollbars",a="resizable",f="toolbar",l="status",c="directories",h="fullscreen",p="dependent",d;d=function(){d.superclass.constructor.apply(this,arguments)},e.extend(d,e.Base,{overlay:null,initializer:function(){var t=this;e.delegate("click",function(n){n.preventDefault();var r="",i=e.Node.create('<div id="glossaryfilteroverlayprogress"><img src="'+M.cfg.loadingicon+'" class="spinner" /></div>'),s=new e.Overlay({headerContent:r,bodyContent:i}),o,u;t.overlay=s,s.render(e.one(document.body)),o=this.getAttribute("href").replace("showentry.php","showentry_ajax.php"),u={method:"get",context:t,on:{success:function(e,t){this.display_callback(t.responseText)},failure:function(e,t){var n=t.statusText;M.cfg.developerdebug&&(t.statusText+=" ("+o+")"),this.display_callback("bodyContent",n)}}},e.io(o,u)},e.one(document.body),"a.glossary.autolink.concept")},display_callback:function(t){var n,r,i;try{n=e.JSON.parse(t);if(n.success){this.overlay.hide();for(r in n.entries)definition=n.entries[r].definition+n.entries[r].attachments,i=new M.core.alert({title:n.entries[r].concept,message:definition,modal:!1,yesLabel:M.util.get_string("ok","moodle")}),i.show(),e.Node.one("#id_yuialertconfirm-"+i.get("COUNT")).focus();return!0}n.error&&new M.core.ajaxException(n)}catch(s){new M.core.exception(s)}return!1}},{NAME:n,ATTRS:{url:{validator:e.Lang.isString,value:M.cfg.wwwroot+"/mod/glossary/showentry.php"},name:{validator:e.Lang.isString,value:"glossaryconcept"},options:{getter:function(){return{width:this.get(r),height:this.get(i),menubar:this.get(s),location:this.get(o),scrollbars:this.get(u),resizable:this.get(a),toolbar:this.get(f),status:this.get(l),directories:this.get(c),fullscreen:this.get(h),dependent:this.get(p)}},readOnly:!0},width:{value:600},height:{value:450},menubar:{value:!1},location:{value:!1},scrollbars:{value:!0},resizable:{value:!0},toolbar:{value:!0},status:{value:!0},directories:{value:!1},fullscreen:{value:!1},dependent:{value:!0}}}),M.filter_glossary=M.filter_glossary||{},M.filter_glossary.init_filter_autolinking=function(e){return new d(e)}},"@VERSION@",{requires:["base","node","io-base","json-parse","event-delegate","overlay","moodle-core-notification-alert"]});
YUI.add("moodle-filter_glossary-autolinker",function(e,t){var n="Glossary filter autolinker",r="width",i="height",s="menubar",o="location",u="scrollbars",a="resizable",f="toolbar",l="status",c="directories",h="fullscreen",p="dependent",d;d=function(){d.superclass.constructor.apply(this,arguments)},e.extend(d,e.Base,{overlay:null,initializer:function(){var t=this;e.delegate("click",function(n){n.preventDefault();var r="",i=e.Node.create('<div id="glossaryfilteroverlayprogress"><img src="'+M.cfg.loadingicon+'" class="spinner" /></div>'),s=new e.Overlay({headerContent:r,bodyContent:i}),o,u;t.overlay=s,s.render(e.one(document.body)),o=this.getAttribute("href").replace("showentry.php","showentry_ajax.php"),u={method:"get",context:t,on:{success:function(e,t){this.display_callback(t.responseText)},failure:function(e,t){var n=t.statusText;M.cfg.developerdebug&&(t.statusText+=" ("+o+")"),this.display_callback("bodyContent",n)}}},e.io(o,u)},e.one(document.body),"a.glossary.autolink.concept")},display_callback:function(t){var n,r,i;try{n=e.JSON.parse(t);if(n.success){this.overlay.hide();for(r in n.entries)definition=n.entries[r].definition+n.entries[r].attachments,i=new M.core.alert({title:n.entries[r].concept,message:definition,modal:!1,yesLabel:M.util.get_string("ok","moodle")}),i.show(),e.fire(M.core.event.FILTER_CONTENT_UPDATED,{nodes:new e.NodeList(i.get("boundingBox"))}),e.Node.one("#id_yuialertconfirm-"+i.get("COUNT")).focus();return!0}n.error&&new M.core.ajaxException(n)}catch(s){new M.core.exception(s)}return!1}},{NAME:n,ATTRS:{url:{validator:e.Lang.isString,value:M.cfg.wwwroot+"/mod/glossary/showentry.php"},name:{validator:e.Lang.isString,value:"glossaryconcept"},options:{getter:function(){return{width:this.get(r),height:this.get(i),menubar:this.get(s),location:this.get(o),scrollbars:this.get(u),resizable:this.get(a),toolbar:this.get(f),status:this.get(l),directories:this.get(c),fullscreen:this.get(h),dependent:this.get(p)}},readOnly:!0},width:{value:600},height:{value:450},menubar:{value:!1},location:{value:!1},scrollbars:{value:!0},resizable:{value:!0},toolbar:{value:!0},status:{value:!0},directories:{value:!1},fullscreen:{value:!1},dependent:{value:!0}}}),M.filter_glossary=M.filter_glossary||{},M.filter_glossary.init_filter_autolinking=function(e){return new d(e)}},"@VERSION@",{requires:["base","node","io-base","json-parse","event-delegate","overlay","moodle-core-event","moodle-core-notification-alert"]});
@@ -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"
]
});
+2
View File
@@ -70,6 +70,8 @@ Y.extend(AUTOLINKER, Y.Base, {
alertpanel = new M.core.alert({title:data.entries[key].concept,
message:definition, modal:false, yesLabel: M.util.get_string('ok', 'moodle')});
alertpanel.show();
Y.fire(M.core.event.FILTER_CONTENT_UPDATED, {nodes: (new Y.NodeList(alertpanel.get('boundingBox')))});
Y.Node.one('#id_yuialertconfirm-' + alertpanel.get('COUNT')).focus();
}
@@ -7,6 +7,7 @@
"json-parse",
"event-delegate",
"overlay",
"moodle-core-event",
"moodle-core-notification-alert"
]
}
+20 -5
View File
@@ -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 <tex> blah </tex> 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 \)".
// <tex.*> blah </tex>
$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 '<span class="filter_mathjaxloader_equation">' . $text . '</span>';
return '<span class="nolink"><span class="filter_mathjaxloader_equation">' . $text . '</span></span>';
}
return $text;
}
@@ -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['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['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['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.';
+6 -13
View File
@@ -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"
});
@@ -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"]});
@@ -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"]});
@@ -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"]});
+18
View File
@@ -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()]);
});
});
}
};
@@ -1,6 +1,7 @@
{
"moodle-filter_mathjaxloader-loader": {
"requires": [
"moodle-core-event",
"mathjax"
]
}
+4 -2
View File
@@ -81,6 +81,8 @@ function atto_equation_params_for_js($elementid, $options, $fpoptions) {
'elements' => get_config('atto_equation', 'librarygroup4'),
));
return array('texfilteractive' => $texfilteractive, 'contextid' => $context->id, 'library' => $library,
'texdocsurl' => get_docs_url('Using_TeX_Notation'));
return array('texfilteractive' => $texfilteractive,
'contextid' => $context->id,
'library' => $library,
'texdocsurl' => get_docs_url('Using_TeX_Notation'));
}
@@ -165,4 +165,5 @@ if ($ADMIN->fulltree) {
$desc,
$default);
$settings->add($setting);
}
@@ -48,6 +48,10 @@ var COMPONENTNAME = 'atto_equation',
SUBMIT: '.' + CSS.SUBMIT,
LIBRARY_BUTTON: '.' + CSS.LIBRARY + ' button'
},
DELIMITERS = {
START: '\\(',
END: '\\)'
},
TEMPLATES = {
FORM: '' +
'<form class="atto_form">' +
@@ -72,7 +76,7 @@ var COMPONENTNAME = 'atto_equation',
'{{#each library}}' +
'<div id="{{elementid}}_{{../CSS.LIBRARY_GROUP_PREFIX}}{{@key}}">' +
'{{#split "\n" elements}}' +
'<button data-tex="{{this}}" title="{{this}}">$${{this}}$$</button>' +
'<button data-tex="{{this}}" title="{{this}}">{{../../DELIMITERS.START}}{{this}}{{../../DELIMITERS.END}}</button>' +
'{{/split}}' +
'</div>' +
'{{/each}}' +
@@ -111,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('<span>' + DELIMITERS.START + ' ' + texNode.get('text') + ' ' + DELIMITERS.END + '</span>');
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"]});
File diff suppressed because one or more lines are too long
@@ -48,6 +48,10 @@ var COMPONENTNAME = 'atto_equation',
SUBMIT: '.' + CSS.SUBMIT,
LIBRARY_BUTTON: '.' + CSS.LIBRARY + ' button'
},
DELIMITERS = {
START: '\\(',
END: '\\)'
},
TEMPLATES = {
FORM: '' +
'<form class="atto_form">' +
@@ -72,7 +76,7 @@ var COMPONENTNAME = 'atto_equation',
'{{#each library}}' +
'<div id="{{elementid}}_{{../CSS.LIBRARY_GROUP_PREFIX}}{{@key}}">' +
'{{#split "\n" elements}}' +
'<button data-tex="{{this}}" title="{{this}}">$${{this}}$$</button>' +
'<button data-tex="{{this}}" title="{{this}}">{{../../DELIMITERS.START}}{{this}}{{../../DELIMITERS.END}}</button>' +
'{{/split}}' +
'</div>' +
'{{/each}}' +
@@ -111,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('<span>' + DELIMITERS.START + ' ' + texNode.get('text') + ' ' + DELIMITERS.END + '</span>');
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"]});
+114 -70
View File
@@ -46,6 +46,10 @@ var COMPONENTNAME = 'atto_equation',
SUBMIT: '.' + CSS.SUBMIT,
LIBRARY_BUTTON: '.' + CSS.LIBRARY + ' button'
},
DELIMITERS = {
START: '\\(',
END: '\\)'
},
TEMPLATES = {
FORM: '' +
'<form class="atto_form">' +
@@ -70,7 +74,7 @@ var COMPONENTNAME = 'atto_equation',
'{{#each library}}' +
'<div id="{{elementid}}_{{../CSS.LIBRARY_GROUP_PREFIX}}{{@key}}">' +
'{{#split "\n" elements}}' +
'<button data-tex="{{this}}" title="{{this}}">$${{this}}$$</button>' +
'<button data-tex="{{this}}" title="{{this}}">{{../../DELIMITERS.START}}{{this}}{{../../DELIMITERS.END}}</button>' +
'{{/split}}' +
'</div>' +
'{{/each}}' +
@@ -109,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('<span>' + DELIMITERS.START + ' ' + texNode.get('text') + ' ' + DELIMITERS.END + '</span>');
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
}
}
});
@@ -2,6 +2,7 @@
"moodle-atto_equation-button": {
"requires": [
"moodle-editor_atto-plugin",
"moodle-core-event",
"io",
"event-valuechange",
"tabview"
@@ -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 <http://www.gnu.org/licenses/>.
/**
* @module moodle-core-event
*/
var LOGNAME = 'moodle-core-event';
/**
* List of published global JS events in Moodle. This is a collection
* of global events that can be subscribed to, or fired from any plugin.
*
* @namespace M.core.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"]});
@@ -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"]});
+60
View File
@@ -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 <http://www.gnu.org/licenses/>.
/**
* @module moodle-core-event
*/
var LOGNAME = 'moodle-core-event';
/**
* List of published global JS events in Moodle. This is a collection
* of global events that can be subscribed to, or fired from any plugin.
*
* @namespace M.core.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"]});
+10
View File
@@ -0,0 +1,10 @@
{
"name": "moodle-core-event",
"builds": {
"moodle-core-event": {
"jsfiles": [
"event.js"
]
}
}
}
+58
View File
@@ -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 <http://www.gnu.org/licenses/>.
/**
* @module moodle-core-event
*/
var LOGNAME = 'moodle-core-event';
/**
* List of published global JS events in Moodle. This is a collection
* of global events that can be subscribed to, or fired from any plugin.
*
* @namespace M.core.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);
}
+7
View File
@@ -0,0 +1,7 @@
{
"moodle-core-event": {
"requires": [
"event-custom"
]
}
}