aboutsummaryrefslogtreecommitdiff
path: root/js/mathjax/extensions/a11y/auto-collapse.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/mathjax/extensions/a11y/auto-collapse.js')
-rw-r--r--js/mathjax/extensions/a11y/auto-collapse.js500
1 files changed, 500 insertions, 0 deletions
diff --git a/js/mathjax/extensions/a11y/auto-collapse.js b/js/mathjax/extensions/a11y/auto-collapse.js
new file mode 100644
index 0000000..d18eca9
--- /dev/null
+++ b/js/mathjax/extensions/a11y/auto-collapse.js
@@ -0,0 +1,500 @@
+// @license magnet:?xt=urn:btih:8e4f440f4c65981c5bf93c76d35135ba5064d8b7dn=apache-2.0.txt Apache-2.0
+/*************************************************************
+ *
+ * [Contrib]/a11y/auto-collapse.js
+ *
+ * Implements the ability to have long expressions collapse
+ * automatically on screen size changes.
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (c) 2016-2017 The MathJax Consortium
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+(function (HUB) {
+ var SETTINGS = HUB.config.menuSettings;
+ var COOKIE = {}; // replaced when menu is available
+
+ //
+ // Set up the a11y path,if it isn't already in place
+ //
+ var PATH = MathJax.Ajax.config.path;
+ if (!PATH.a11y) PATH.a11y = HUB.config.root + "/extensions/a11y";
+
+ var Collapse = MathJax.Extension["auto-collapse"] = {
+ version: "1.6.0",
+ config: HUB.CombineConfig("auto-collapse",{
+ disabled: false
+ }),
+ dependents: [], // the extensions that depend on this one
+
+ /*****************************************************************/
+
+ Enable: function (update,menu) {
+ SETTINGS.autocollapse = true;
+ if (menu) COOKIE.autocollapse = true
+ this.config.disabled = false;
+ MathJax.Extension.collapsible.Enable(false,menu);
+ if (update) {
+ HUB.Queue(
+ ["Reprocess",HUB],
+ ["CollapseWideMath",this]
+ );
+ }
+ },
+ Disable: function (update,menu) {
+ SETTINGS.autocollapse = false;
+ if (menu) COOKIE.autocollapse = false;
+ this.config.disabled = true;
+ for (var i = this.dependents.length-1; i >= 0; i--) {
+ var dependent = this.dependents[i];
+ if (dependent.Disable) dependent.Disable(false,menu);
+ }
+ if (update) HUB.Queue(["Rerender",HUB]);
+ },
+
+ //
+ // Register a dependent
+ //
+ Dependent: function (extension) {
+ this.dependents.push(extension);
+ },
+
+ Startup: function () {
+ //
+ // Inform collapsible extension that we are a dependent
+ //
+ var Collapsible = MathJax.Extension.collapsible;
+ if (Collapsible) Collapsible.Dependent(this);
+ //
+ // Add the filter into the post-input hooks (priority 150, so other
+ // hooks run first, in particular, the enrichment and complexity hooks).
+ //
+ HUB.postInputHooks.Add(["Filter",Collapse],150);
+ //
+ // Add the auto-collapsing
+ //
+ HUB.Queue(function () {return Collapse.CollapseWideMath()});
+ //
+ // Add a resize handler to check for math that needs
+ // to be collapsed or expanded.
+ //
+ if (window.addEventListener) window.addEventListener("resize",Collapse.resizeHandler,false);
+ else if (window.attachEvent) window.attachEvent("onresize",Collapse.resizeHandler);
+ else window.onresize = Collapse.resizeHandler;
+ },
+
+ //
+ // If the math is block-level (or in an element "by itself"), then
+ // add the SRE actions for this element.
+ //
+ Filter: function (jax,id,script) {
+ if (!jax.enriched || this.config.disabled) return;
+ if (jax.root.Get("display") === "block" ||
+ script.parentNode.childNodes.length <= 3) {
+ jax.root.SRE = {action: this.Actions(jax.root)};
+ }
+ },
+ //
+ // Produce an array of collapsible actions
+ // sorted by depth and complexity
+ //
+ Actions: function (node) {
+ var actions = [];
+ this.getActions(node,0,actions);
+ return this.sortActions(actions);
+ },
+ getActions: function (node,depth,actions) {
+ if (node.isToken || !node.data) return;
+ depth++;
+ for (var i = 0, m = node.data.length; i < m; i++) {
+ if (node.data[i]) {
+ var child = node.data[i];
+ if (child.collapsible) {
+ if (!actions[depth]) actions[depth] = [];
+ actions[depth].push(child);
+ this.getActions(child.data[1],depth,actions);
+ } else {
+ this.getActions(child,depth,actions);
+ }
+ }
+ }
+ },
+ sortActions: function (actions) {
+ var ACTIONS = [];
+ for (var i = 0, m = actions.length; i < m; i++) {
+ if (actions[i]) ACTIONS = ACTIONS.concat(actions[i].sort(this.sortActionsBy));
+ }
+ return ACTIONS;
+ },
+ sortActionsBy: function (a,b) {
+ a = a.data[1].complexity; b = b.data[1].complexity;
+ return (a < b ? -1 : a > b ? 1 : 0);
+ },
+
+ /*****************************************************************/
+ /*
+ * These routines implement the automatic collapsing of equations
+ * based on container widths.
+ */
+
+ //
+ // Find math that is too wide and collapse it.
+ //
+ CollapseWideMath: function (element) {
+ if (this.config.disabled) return;
+ this.GetContainerWidths(element);
+ var jax = HUB.getAllJax(element);
+ var state = {collapse: [], jax: jax, m: jax.length, i: 0, changed:false};
+ return this.collapseState(state);
+ },
+ collapseState: function (state) {
+ var collapse = state.collapse;
+ while (state.i < state.m) {
+ var jax = state.jax[state.i];
+ var SRE = jax.root.SRE; state.changed = false;
+ if (SRE && SRE.action.length) {
+ if (SRE.cwidth < SRE.m || SRE.cwidth > SRE.M) {
+ var restart = this.getActionWidths(jax,state); if (restart) return restart;
+ this.collapseActions(SRE,state);
+ if (state.changed) collapse.push(jax.SourceElement());
+ }
+ }
+ state.i++;
+ }
+ if (collapse.length === 0) return;
+ if (collapse.length === 1) collapse = collapse[0];
+ return HUB.Rerender(collapse);
+ },
+
+ //
+ // Find the actions that need to be collapsed to acheive
+ // the correct width, and retain the sizes that would cause
+ // the equation to be expanded or collapsed further.
+ //
+ collapseActions: function (SRE,state) {
+ var w = SRE.width, m = w, M = 1000000;
+ for (var j = SRE.action.length-1; j >= 0; j--) {
+ var action = SRE.action[j], selection = action.selection;
+ if (w > SRE.cwidth) {
+ action.selection = 1;
+ m = action.SREwidth; M = w;
+ } else {
+ action.selection = 2;
+ }
+ w = action.SREwidth;
+ if (SRE.DOMupdate) {
+ document.getElementById(action.id).setAttribute("selection",action.selection);
+ } else if (action.selection !== selection) {
+ state.changed = true;
+ }
+ }
+ SRE.m = m; SRE.M = M;
+ },
+
+ //
+ // Get the widths of the different collapsings,
+ // trapping any restarts, and restarting the process
+ // when the event has occurred.
+ //
+ getActionWidths: function (jax,state) {
+ if (!jax.root.SRE.actionWidths) {
+ MathJax.OutputJax[jax.outputJax].getMetrics(jax);
+ try {this.computeActionWidths(jax)} catch (err) {
+ if (!err.restart) throw err;
+ return MathJax.Callback.After(["collapseState",this,state],err.restart);
+ }
+ state.changed = true;
+ }
+ return null;
+ },
+ //
+ // Compute the action widths by collapsing each maction,
+ // and recording the width of the complete equation.
+ //
+ computeActionWidths: function (jax) {
+ var SRE = jax.root.SRE, actions = SRE.action, j, state = {};
+ SRE.width = jax.sreGetRootWidth(state);
+ for (j = actions.length-1; j >= 0; j--) actions[j].selection = 2;
+ for (j = actions.length-1; j >= 0; j--) {
+ var action = actions[j];
+ if (action.SREwidth == null) {
+ action.selection = 1;
+ action.SREwidth = jax.sreGetActionWidth(state,action);
+ }
+ }
+ SRE.actionWidths = true;
+ },
+
+ //
+ // Get the widths of the containers of tall the math elements
+ // that can be collapsed (so we can tell which ones NEED to be
+ // collapsed). Do this in a way that only causes two reflows.
+ //
+ GetContainerWidths: function (element) {
+ var JAX = HUB.getAllJax(element);
+ var i, m, script, span = MathJax.HTML.Element("span",{style:{display:"block"}});
+ var math = [], jax, root;
+ for (i = 0, m = JAX.length; i < m; i++) {
+ jax = JAX[i], root = jax.root, SRE = root.SRE;
+ if (SRE && SRE.action.length) {
+ if (SRE.width == null) {
+ jax.sreGetMetrics();
+ SRE.m = SRE.width; SRE.M = 1000000;
+ }
+ script = jax.SourceElement();
+ script.previousSibling.style.display = "none";
+ script.parentNode.insertBefore(span.cloneNode(false),script);
+ math.push([jax,script]);
+ }
+ }
+ for (i = 0, m = math.length; i < m; i++) {
+ jax = math[i][0], script = math[i][1];
+ if (script.previousSibling.offsetWidth)
+ jax.root.SRE.cwidth = script.previousSibling.offsetWidth * jax.root.SRE.em;
+ }
+ for (i = 0, m = math.length; i < m; i++) {
+ jax = math[i][0], script = math[i][1];
+ script.parentNode.removeChild(script.previousSibling);
+ script.previousSibling.style.display = "";
+ }
+ },
+
+ /*****************************************************************/
+
+ //
+ // A resize handler that can be tied to the window resize event
+ // to collapse math automatically on resize.
+ //
+
+ timer: null,
+ running: false,
+ retry: false,
+ saved_delay: 0,
+
+ resizeHandler: function (event) {
+ if (Collapse.config.disabled) return;
+ if (Collapse.running) {Collapse.retry = true; return}
+ if (Collapse.timer) clearTimeout(Collapse.timer);
+ Collapse.timer = setTimeout(Collapse.resizeAction, 100);
+ },
+ resizeAction: function () {
+ Collapse.timer = null;
+ Collapse.running = true;
+ HUB.Queue(
+ function () {
+ //
+ // Prevent flicker between input and output phases.
+ //
+ Collapse.saved_delay = HUB.processSectionDelay;
+ HUB.processSectionDelay = 0;
+ },
+ ["CollapseWideMath",Collapse],
+ ["resizeCheck",Collapse]
+ );
+ },
+ resizeCheck: function () {
+ Collapse.running = false;
+ HUB.processSectionDelay = Collapse.saved_delay;
+ if (Collapse.retry) {
+ Collapse.retry = false;
+ setTimeout(Collapse.resizeHandler,0);
+ }
+ }
+
+ };
+
+ HUB.Register.StartupHook("End Extensions", function () {
+ if (SETTINGS.autocollapse == null) {
+ SETTINGS.autocollapse = !Collapse.config.disabled;
+ } else {
+ Collapse.config.disabled = !SETTINGS.autocollapse;
+ }
+ HUB.Register.StartupHook("MathMenu Ready", function () {
+ COOKIE = MathJax.Menu.cookie;
+ var Switch = function(menu) {
+ Collapse[SETTINGS.autocollapse ? "Enable" : "Disable"](true,true);
+ MathJax.Menu.saveCookie();
+ };
+ var ITEM = MathJax.Menu.ITEM,
+ MENU = MathJax.Menu.menu;
+ var menu = ITEM.CHECKBOX(
+ ['AutoCollapse','Auto Collapse'], 'autocollapse', {action: Switch}
+ );
+ var submenu = (MENU.FindId('Accessibility')||{}).submenu, index;
+ if (submenu) {
+ index = submenu.IndexOfId('AutoCollapse');
+ if (index !== null) {
+ submenu.items[index] = menu;
+ } else {
+ index = submenu.IndexOfId('CollapsibleMath');
+ submenu.items.splice(index+1,0,menu);
+ }
+ } else {
+ index = MENU.IndexOfId('CollapsibleMath');
+ MENU.items.splice(index+1,0,menu);
+ }
+ var init = function () {Collapse[SETTINGS.autocollapse ? "Enable" : "Disable"]()};
+ if (MathJax.Extension.collapse) init();
+ else MathJax.Hub.Register.StartupHook("Auto Collapse Ready", init);
+ },25); // after Assistive-Explore
+ },25);
+
+})(MathJax.Hub);
+
+
+/*****************************************************************/
+/*
+ * Add methods to the ElementJax and OutputJax to get the
+ * widths of the collapsed elements.
+ */
+
+//
+// Add SRE methods to ElementJax.
+//
+MathJax.ElementJax.Augment({
+ sreGetMetrics: function () {
+ MathJax.OutputJax[this.outputJax].sreGetMetrics(this,this.root.SRE);
+ },
+ sreGetRootWidth: function (state) {
+ return MathJax.OutputJax[this.outputJax].sreGetRootWidth(this,state);
+ },
+ sreGetActionWidth: function (state,action) {
+ return MathJax.OutputJax[this.outputJax].sreGetActionWidth(this,state,action);
+ }
+});
+
+//
+// Add default methods to base OutputJax class.
+//
+MathJax.OutputJax.Augment({
+ getMetrics: function () {}, // make sure it is defined
+ sreGetMetrics: function (jax,SRE) {SRE.cwidth = 1000000; SRE.width = 0; SRE.em = 12},
+ sreGetRootWidth: function (jax,state) {return 0},
+ sreGetActionWidth: function (jax,state,action) {return 0}
+});
+
+//
+// Specific implementations for HTML-CSS output.
+//
+MathJax.Hub.Register.StartupHook("HTML-CSS Jax Ready",function () {
+ MathJax.OutputJax["HTML-CSS"].Augment({
+ sreGetMetrics: function (jax,SRE) {
+ SRE.width = jax.root.data[0].HTMLspanElement().parentNode.bbox.w;
+ SRE.em = 1 / jax.HTMLCSS.em / jax.HTMLCSS.scale;
+ },
+ sreGetRootWidth: function (jax,state) {
+ var html = jax.root.data[0].HTMLspanElement();
+ state.box = html.parentNode;
+ return state.box.bbox.w;
+ },
+ sreGetActionWidth: function (jax,state,action) {
+ return jax.root.data[0].toHTML(state.box).bbox.w;
+ }
+ });
+});
+
+//
+// Specific implementations for SVG output.
+//
+MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () {
+ MathJax.OutputJax.SVG.Augment({
+ getMetrics: function (jax) {
+ this.em = MathJax.ElementJax.mml.mbase.prototype.em = jax.SVG.em; this.ex = jax.SVG.ex;
+ this.linebreakWidth = jax.SVG.lineWidth; this.cwidth = jax.SVG.cwidth;
+ },
+ sreGetMetrics: function (jax,SRE) {
+ SRE.width = jax.root.SVGdata.w/1000;
+ SRE.em = 1/jax.SVG.em;
+ },
+ sreGetRootWidth: function (jax,state) {
+ state.span = document.getElementById(jax.inputID+"-Frame");
+ return jax.root.SVGdata.w/1000;
+ },
+ sreGetActionWidth: function (jax,state,action) {
+ this.mathDiv = state.span;
+ state.span.appendChild(this.textSVG);
+ try {var svg = jax.root.data[0].toSVG()} catch(err) {var error = err}
+ state.span.removeChild(this.textSVG);
+ if (error) throw error; // can happen when a restart is needed
+ return jax.root.data[0].SVGdata.w/1000;
+ }
+ });
+});
+
+//
+// Specific implementations for CommonHTML output.
+//
+MathJax.Hub.Register.StartupHook("CommonHTML Jax Ready",function () {
+ MathJax.OutputJax.CommonHTML.Augment({
+ sreGetMetrics: function (jax,SRE) {
+ SRE.width = jax.root.CHTML.w;
+ SRE.em = 1 / jax.CHTML.em / jax.CHTML.scale;
+ },
+ sreGetRootWidth: function (jax,state) {
+ state.span = document.getElementById(jax.inputID+"-Frame").firstChild;
+ state.tmp = document.createElement("span");
+ state.tmp.className = state.span.className;
+ return jax.root.CHTML.w / jax.CHTML.scale;
+ },
+ sreGetActionWidth: function (jax,state,action) {
+ state.span.parentNode.replaceChild(state.tmp,state.span);
+ MathJax.OutputJax.CommonHTML.CHTMLnode = state.tmp;
+ try {jax.root.data[0].toCommonHTML(state.tmp)} catch (err) {var error = err}
+ state.tmp.parentNode.replaceChild(state.span,state.tmp);
+ if (error) throw error; // can happen when a restart is needed
+ return jax.root.data[0].CHTML.w / jax.CHTML.scale;
+ }
+ });
+});
+
+//
+// Specific implementations for NativeMML output.
+//
+MathJax.Hub.Register.StartupHook("NativeMML Jax Ready",function () {
+ MathJax.OutputJax.NativeMML.Augment({
+ sreGetMetrics: function (jax,SRE) {
+ var span = document.getElementById(jax.inputID+"-Frame");
+ SRE.width = span.offsetWidth;
+ SRE.em = 1; SRE.DOMupdate = true;
+ },
+ sreGetRootWidth: function (jax,state) {
+ state.span = document.getElementById(jax.inputID+"-Frame").firstChild;
+ return state.span.offsetWidth;
+ },
+ sreGetActionWidth: function (jax,state,action) {
+ var maction = document.getElementById(action.id);
+ maction.setAttribute("selection",1);
+ var w = state.span.offsetWidth;
+ return w;
+ }
+ });
+});
+
+
+/*****************************************************************/
+
+//
+// Load the collapsible extension and
+// signal the start up when that has loaded.
+//
+MathJax.Ajax.Require("[a11y]/collapsible.js");
+MathJax.Hub.Register.StartupHook("Collapsible Ready", function () {
+ MathJax.Extension["auto-collapse"].Startup(); // Initialize the collapsing process
+ MathJax.Hub.Startup.signal.Post("Auto Collapse Ready");
+ MathJax.Ajax.loadComplete("[a11y]/auto-collapse.js");
+});
+
+// @license-end