Latest build deployed.

This commit is contained in:
andyeisenberg
2025-12-28 17:55:29 +00:00
commit 7e09fad64e
617 changed files with 18659 additions and 0 deletions
+195
View File
@@ -0,0 +1,195 @@
(function(b, u, v) {
function x(b, f, e) {
b = (b + "").match(/^(-?[0-9]+)(%)?$/);
if (!b) return !1;
var c = parseInt(b[1], 10);
b[2] && (c *= f / 100);
return 0 > c ? f + c + (e || 0) : c
}
function y(k, f) {
function e() {
function b() {
g = +new Date;
f.apply(e, t);
c && (c = clearTimeout(c))
}
var e = this,
q = +new Date - g,
t = arguments;
c && (c = clearTimeout(c));
q > k ? b() : c = setTimeout(b, k - q)
}
var c, g = 0;
b.guid && (e.guid = f.guid = f.guid || b.guid++);
return e
}
b.Espy = function(k, f, e) {
function c(a, d) {
b.isPlainObject(a) && (d = a, a = null);
b.extend(t.prototype, d);
null !== a && (w = a)
}
function g(a) {
if (a =
q(a)) {
var d = a.$el.offset()[a.settings.horizontal ? "left" : "top"] - p.offset[a.settings.horizontal ? "left" : "top"],
h = a.$el[a.settings.horizontal ? "outerWidth" : "outerHeight"]();
b.extend(a, {
start: d,
elSize: h,
end: d + h
})
}
}
function r(a) {
// console.log("r(a) with a = ", a);
if (a === v) b.each(m, r);
else if (a = q(a)) {
var d = p[a.settings.horizontal ? "width" : "height"],
h = x(a.settings.size, d),
d = p[a.settings.horizontal ? "left" : "top"] + x(a.settings.offset, d, -h),
c = d + h,
h = a.settings.contain ? d <= a.start && c >= a.end ? "inside" : d + h / 2 > a.start + a.elSize / 2 ? a.settings.horizontal ? "left" :
"up" : a.settings.horizontal ? "right" : "down" : d > a.start && d < a.end || c > a.start && c < a.end || d <= a.start && c >= a.start || d <= a.end && c >= a.end ? "inside" : d > a.end ? a.settings.horizontal ? "left" : "up" : a.settings.horizontal ? "right" : "down";
a.state !== h && (a.state = h, "function" === typeof w && w.call(a.el, "inside" === h, h), "function" === typeof a.callback && a.callback.call(a.el, "inside" === h, h))
}
}
function s(a) {
if (m.hasOwnProperty(a)) return a;
if (b.isPlainObject(a) && m.hasOwnProperty(a.id)) return a.id;
a = b(a)[0];
var d = !1;
b.each(m, function(b,
c) {
c.el === a && (d = b)
});
return d
}
// console.log("nothing yet");
function q(a) {
return (a = s(a)) ? m[a] : !1
}
"function" !== typeof f && (e = f, f = 0);
// console.log("k was", k);
var t = function(a) {
b.extend(this, a)
},
u = function(a, d, c, e) {
this.id = a;
this.el = d;
this.$el = b(d);
this.callback = c;
this.settings = new t(e);
this.configure = function(a, d) {
b.isPlainObject(a) && (d = a, a = null);
b.extend(this.settings, d);
null !== a && (this.callback = a)
}
},
n = this,
l = b(k);
// console.log("l", l, "l==window", l[0]==window);
k = b.fn.espy.defaults;
// console.log("k is", k);
var w, m = {},
z = 0;
// because window.offset() is not defined in jQuery 3 (why?!?!?!),
// we have to treat that as a special case (DF 1/28/19)
if (l[0]==window) { offSET = { top: 0, left: 0 } }
else { offSET = l.offset() }
var p = {
top: l.scrollTop(),
left: l.scrollLeft(),
width: l.innerWidth(),
height: l.innerHeight(),
offset: offSET
/*
offset: l.offset() || {
top: 0,
left: 0
}
*/
};
// console.log("p", p);
c(f, b.extend({}, k, e));
n.add = function(a, d, c) {
b.isPlainObject(d) && (c = d, d = 0);
b(a).each(function(a, b) {
var e = s(b) || "s" + z++;
m[e] = new u(e, b, d, c);
g(e);
r(e)
})
};
n.configure = function(a, d, e) {
"function" === typeof a ? (d = a, a = null, b.isPlainObject(d) && (e = d, d = null)) : b.isPlainObject(a) ? (e = a, d = a = null) : b.isPlainObject(d) && (e = d, d = null);
null === a ? (c(d, e), b.each(m, function(a, b) {
g(b)
})) : b(a).each(function(a, b) {
var c = q(b);
c && (c.configure(d, e), g(b))
})
};
n.reload = function(a) {
a === v ? b.each(m, function() {
g(this.id)
}) :
b(a).each(function(a, b) {
var c = s(b);
c && (g(c), r(c))
})
};
n.remove = function(a) {
b(a).each(function(a, b) {
var c = s(b);
c && delete m[c]
})
};
n.destroy = function() {
l.off(".espy");
m = {};
n = v
};
n.resize = function() {
b.each(m, function() {
this.reloadOnResize && g(this)
});
p.width = l.innerWidth();
p.height = l.innerHeight();
r()
};
l.on("scroll.espy", y(k.delay, function() {
p.top = l.scrollTop();
p.left = l.scrollLeft();
r()
}));
l.on("resize.espy", y(k.delay, function() {
n.resize()
}))
};
b.fn.espy = function(k, f) {
var e, c;
e = f && f.context || u;
var g = b.data(e,
"espy") || b.data(e, "espy", new b.Espy(e));
"string" !== typeof k ? g.add(this, k, f) : (e = k, c = Array.prototype.slice.call(arguments), c[0] = this, "function" === typeof g[e] && g[e].apply(g, c));
return this
};
b.fn.espy.defaults = {
delay: 100,
context: window,
horizontal: 0,
offset: 0,
size: "100%",
contain: 0,
reloadOnResize: !0
}
})(jQuery, window);
File diff suppressed because one or more lines are too long
+161
View File
@@ -0,0 +1,161 @@
// Sticky Plugin v1.0.0 for jQuery
// =============
// Author: Anthony Garand
// Improvements by German M. Bravo (Kronuz) and Ruud Kamphuis (ruudk)
// Improvements by Leonardo C. Daronco (daronco)
// Created: 2/14/2011
// Date: 2/12/2012
// Website: http://labs.anthonygarand.com/sticky
// Description: Makes an element on the page stick on the screen as you scroll
// It will only set the 'top' and 'position' of your element, you
// might need to adjust the width in some cases.
(function($) {
var defaults = {
topSpacing: 0,
bottomSpacing: 0,
className: 'is-sticky',
wrapperClassName: 'sticky-wrapper',
center: false,
getWidthFrom: ''
},
$window = $(window),
$document = $(document),
sticked = [],
windowHeight = $window.height(),
scroller = function() {
var scrollTop = $window.scrollTop(),
documentHeight = $document.height(),
dwh = documentHeight - windowHeight,
extra = (scrollTop > dwh) ? dwh - scrollTop : 0;
for (var i = 0; i < sticked.length; i++) {
var s = sticked[i],
elementTop = s.stickyWrapper.offset().top,
etse = elementTop - s.topSpacing - extra;
if (scrollTop <= etse) {
if (s.currentTop !== null) {
s.stickyElement
.css('position', '')
.css('top', '');
s.stickyElement.parent().removeClass(s.className);
s.currentTop = null;
}
}
else {
var newTop = documentHeight - s.stickyElement.outerHeight()
- s.topSpacing - s.bottomSpacing - scrollTop - extra;
if (newTop < 0) {
newTop = newTop + s.topSpacing;
} else {
newTop = s.topSpacing;
}
if (s.currentTop != newTop) {
s.stickyElement
.css('position', 'fixed')
.css('top', newTop);
if (typeof s.getWidthFrom !== 'undefined') {
s.stickyElement.css('width', $(s.getWidthFrom).width());
}
s.stickyElement.parent().addClass(s.className);
s.currentTop = newTop;
}
}
}
},
resizer = function() {
windowHeight = $window.height();
},
methods = {
init: function(options) {
var o = $.extend({}, defaults, options);
return this.each(function() {
var stickyElement = $(this);
var stickyId = stickyElement.attr('id');
var wrapperId = stickyId ? stickyId + '-' + defaults.wrapperClassName : defaults.wrapperClassName
var wrapper = $('<div></div>')
.attr('id', stickyId + '-sticky-wrapper')
.addClass(o.wrapperClassName);
stickyElement.wrapAll(wrapper);
if (o.center) {
stickyElement.parent().css({width:stickyElement.outerWidth(),marginLeft:"auto",marginRight:"auto"});
}
if (stickyElement.css("float") == "right") {
stickyElement.css({"float":"none"}).parent().css({"float":"right"});
}
var stickyWrapper = stickyElement.parent();
stickyWrapper.css('height', stickyElement.outerHeight());
sticked.push({
topSpacing: o.topSpacing,
bottomSpacing: o.bottomSpacing,
stickyElement: stickyElement,
currentTop: null,
stickyWrapper: stickyWrapper,
className: o.className,
getWidthFrom: o.getWidthFrom
});
});
},
update: scroller,
unstick: function(options) {
return this.each(function() {
var unstickyElement = $(this);
var removeIdx = -1;
for (var i = 0; i < sticked.length; i++)
{
if (sticked[i].stickyElement.get(0) == unstickyElement.get(0))
{
removeIdx = i;
}
}
if(removeIdx != -1)
{
sticked.splice(removeIdx,1);
unstickyElement.unwrap();
unstickyElement.removeAttr('style');
}
});
}
};
// should be more efficient than using $window.scroll(scroller) and $window.resize(resizer):
if (window.addEventListener) {
window.addEventListener('scroll', scroller, false);
window.addEventListener('resize', resizer, false);
} else if (window.attachEvent) {
window.attachEvent('onscroll', scroller);
window.attachEvent('onresize', resizer);
}
$.fn.sticky = function(method) {
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof method === 'object' || !method ) {
return methods.init.apply( this, arguments );
} else {
$.error('Method ' + method + ' does not exist on jQuery.sticky');
}
};
$.fn.unstick = function(method) {
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof method === 'object' || !method ) {
return methods.unstick.apply( this, arguments );
} else {
$.error('Method ' + method + ' does not exist on jQuery.sticky');
}
};
$(function() {
setTimeout(scroller, 0);
});
})(jQuery);
+338
View File
@@ -0,0 +1,338 @@
// Code controlling behavior of xref knowls and born hidden knowls
// Assumes this file is loaded as part of initial page
window.addEventListener("load", (event) => {
addKnowls(document);
});
function addKnowls(target) {
const xrefs = target.querySelectorAll("[data-knowl]");
for (const xref of xrefs) {
LinkKnowl.initializeXrefKnowl(xref);
}
const bornHiddens = target.querySelectorAll(".born-hidden-knowl");
for (const bhk of bornHiddens) {
const summary = bhk.querySelector(":scope > summary");
const contents = bhk.querySelector(":scope > summary + *");
new SlideRevealer(summary, contents, bhk);
}
}
// Used to animate both types of knowls
class SlideRevealer {
static STATE = Object.freeze({
INACTIVE: 0,
CLOSING: 1,
EXPANDING: 2
});
// triggerElement is the element clicked to open/close
// contentElement is the element that will hide/reveal
// animatedElement is the element that will grow/shrink as contentElement is modified
// may be the same as contentElement or a parent of it
constructor(triggerElement, contentElement, animatedElement) {
this.triggerElement = triggerElement;
this.contentElement = contentElement;
this.animatedElement = animatedElement;
// mid animation state tracking
this.animation = null;
this.animationState = SlideRevealer.STATE.INACTIVE;
this.triggerElement.addEventListener('click', (e) => this.onClick(e));
}
onClick(e) {
// Stop default behavior from the browser
if (e) e.preventDefault();
// Add an overflow on the <details> to avoid content overflowing
this.animatedElement.style.overflow = 'hidden';
// Check if the element is being closed or is already closed
if (this.animationState === SlideRevealer.STATE.CLOSING || !this.animatedElement.hasAttribute("open")) {
// Force the [open] attributes - allow for similar targetting of xref and born-hidden knowls
this.animatedElement.setAttribute("open","");
this.triggerElement.setAttribute("open","");
this.contentElement.style.display = '';
// Trigger the animation to expand or collapse the knowl.
// Delay the MathJax typesetting until the knowl is visible to ensure proper measurements
// are taken, but before the unrolling begins. This helps avoid layout shifts and ensures
// smooth animation with correctly sized content.
MathJax.typesetPromise().then(() => window.requestAnimationFrame(() => this.toggle(true)));
} else if (this.animationState === SlideRevealer.STATE.EXPANDING || this.animatedElement.hasAttribute("open")) {
this.toggle(false);
}
}
toggle(expanding) {
let closedHeight = 0;
if (this.animatedElement.contains(this.triggerElement))
closedHeight = this.triggerElement.offsetHeight;
const fullHeight = closedHeight + this.contentElement.offsetHeight;
const startHeight = `${expanding ? closedHeight : fullHeight}px`;
const endHeight = `${expanding ? fullHeight : closedHeight}px`;
// Need to animate padding to avoid extra height for xref knowls
const padding = this.animatedElement.offsetHeight - this.animatedElement.clientHeight;
const startPad = `${expanding ? 0 : padding}px`;
const endPad = `${expanding ? padding : 0}px`;
// Cancel any existing animation
if (this.animation) {
this.animation.cancel();
}
// Animate ~400 pixels per second with max of 0.75 second and min of 0.25
const animDuration = Math.max( Math.min( (Math.abs(closedHeight - fullHeight) / 400 * 1000), 750), 250);
// Start animation
this.animationState = expanding ? SlideRevealer.STATE.EXPANDING : SlideRevealer.STATE.CLOSING;
this.animation = this.animatedElement.animate({
height: [startHeight, endHeight],
paddingTop: [startPad, endPad],
paddingBottom: [startPad, endPad]
}, {
duration: animDuration,
easing: 'ease'
});
this.animation.onfinish = () => { this.onAnimationFinish(expanding); };
this.animation.oncancel = () => { this.animationState = SlideRevealer.STATE.INACTIVE; };
}
onAnimationFinish(isOpen) {
// Clear animation state
this.animation = null;
this.animationState = SlideRevealer.STATE.INACTIVE;
// Make sure animated element has open (needed for details)
if(!isOpen) {
this.animatedElement.removeAttribute("open");
this.triggerElement.removeAttribute("open");
}
// Clear styles used in animation
this.animatedElement.style.overflow = '';
if (!isOpen)
this.contentElement.style.display = 'none';
if (isOpen) {
let hasCallback = this.contentElement.querySelectorAll("[data-knowl-callback]");
hasCallback.forEach((el) => {
window[el.getAttribute("data-knowl-callback")](el, open);
});
}
}
}
// A LinkKnowl manages a single link based knowl
class LinkKnowl {
// Used to uniquely identify XrefKnowls
static xrefCount = 0;
// Factory to create an XrefKnowl from a knowl link
// Will avoid duplicate initialization
// This should be used by outside code to create XrefKnowls
static initializeXrefKnowl(knowlLinkElement) {
if (knowlLinkElement.getAttribute("data-knowl-uid") === null) {
return new LinkKnowl(knowlLinkElement);
}
}
// "Private" constructor - should only be called by initializeXrefKnowl
constructor(knowlLinkElement) {
this.linkElement = knowlLinkElement;
this.outputElement = null;
this.uid = LinkKnowl.xrefCount++;
knowlLinkElement.setAttribute("data-knowl-uid", this.uid);
// Xref's behavior is that of a button
knowlLinkElement.setAttribute("role", "button");
// Stash a copy of the original title for use in aria-label
// If no title, use textContent
knowlLinkElement.setAttribute("data-base-title", knowlLinkElement.getAttribute("title") || this.linkElement.textContent);
knowlLinkElement.classList.add("knowl__link");
this.updateLabels(false);
// Bind required to force "this" of event handler to be this object
knowlLinkElement.addEventListener("click", this.handleLinkClick.bind(this));
}
// Set aria-label and title based on visibility of knowl
updateLabels(isVisible) {
const verb = isVisible
? this.linkElement.getAttribute("data-close-label") || "Close"
: this.linkElement.getAttribute("data-reveal-label") || "Reveal";
const targetDescript = this.linkElement.getAttribute("data-base-title");
const helpText = verb + " " + targetDescript;
this.linkElement.setAttribute("aria-label", helpText);
this.linkElement.setAttribute("title", helpText);
}
// Toggle the state of the knowl link and output elements
// Assumes output is already created
toggle() {
this.linkElement.classList.toggle("active");
const isActive = this.linkElement.classList.contains("active");
this.updateLabels(isActive);
// Scroll to reveal if needed
if (isActive) {
const h = this.outputElement.getBoundingClientRect().height;
if (h > window.innerHeight) {
// knowl is taller than window, scroll to top of knowl
this.outputElement.scrollIntoView(true);
} else {
// reveal full knowl
if (this.outputElement.getBoundingClientRect().bottom > window.innerHeight)
this.outputElement.scrollIntoView(false);
}
}
}
// Returns element the knowl output should be inserted after
findOutputLocation() {
const invalidParents = "table, mjx-container, div.tabular-box, .runestone > .parsons";
// Start with the link's parent, move up as long as there are invalid parents
let el = this.linkElement.parentElement;
let problemAncestor = el.closest(invalidParents);
while (problemAncestor && problemAncestor !== el) {
el = problemAncestor;
problemAncestor = el.closest(invalidParents);
}
return el;
}
// Create the knowl output element
createOutputElement() {
const outputId = "knowl-uid-" + this.uid;
const outputContentsId = "knowl-output-" + this.uid;
const linkTarget = this.linkElement.getAttribute("data-knowl");
const placeholderText = `<div class='knowl__content' style='display:none;' id='${outputId}' aria-live='polite' id='${outputContentsId}'>`
+ `Loading '${linkTarget}'`
+ `</div>`;
const temp = document.createElement("template");
temp.innerHTML = placeholderText;
this.outputElement = temp.content.children[0];
const insertLoc = this.findOutputLocation(this.linkElement);
insertLoc.after(this.outputElement);
}
// Get content for knowl as dom element. Returns promise that resolves to knowl content
async getContent() {
const contentURL = this.linkElement.getAttribute("data-knowl");
const knowlContent = await fetch(contentURL)
.then((response) => response.text())
.then((data) => {
// knowls are full HTML pages, need to just extract body
let knowlDoc = (new DOMParser()).parseFromString(data, "text/html");
let tempContainer = knowlDoc.body;
// grab any scripts from head of knowl doc and add them to the output
let scripts = knowlDoc.querySelectorAll("head script");
tempContainer.append(...scripts);
return tempContainer;
})
.catch((error) => {
const destination = this.linkElement.getAttribute("href");
const text = this.linkElement.textContent;
const err_message = `<div class='knowl-output__error'>`
+ `<div class='para'>Error fetching content. (<em>${error}</em>)</div>`
+ `<div class='para'><a href='${destination}'>Navigate to ${text}</a> instead.</div>`
+ `<div class='para'>If you are viewing this book from your local filesystem, this is expected behavior. To view the book with all features, you must serve the book from a web server. See the <a href="https://pretextbook.org/doc/guide/html/author-faq.html#how-do-i-view-my-book-locally">PreTeXt FAQ</a> for more information.</div>`
+ `</div>`;
return err_message;
});
return knowlContent;
}
// Handle a click on the knowl link
handleLinkClick(event) {
// prevent navigation
event.preventDefault();
if (this.outputElement !== null) {
// output already created, toggle visibility
this.toggle();
} else {
this.createOutputElement();
const slideHandler = new SlideRevealer(this.linkElement, this.outputElement, this.outputElement);
//slideHandler is now responsible for handling clicks to this element
this.linkElement.addEventListener('click', slideHandler);
// Wait up to a half second in hopes of avoiding double content change
// then render to show loading message
let loadingTimeout = setTimeout(() => {
loadingTimeout = null;
slideHandler.onClick(); //fake initial click
this.toggle();
}, 500);
const content = this.getContent();
// Content is a promise at this point, insert when resolved
content
.then((tempContainer) => {
// if timeout still active, cancel it and render
if (loadingTimeout !== null) {
clearTimeout(loadingTimeout);
}
// Now give code that follows .1 seconds to render before making visible
setTimeout(() => {
slideHandler.onClick(); //fake initial click
this.toggle();
}, 100);
// check embedded runestone interactives by loading content into a temp container
// we want to not render any that already are on page. Dupe IDs probably bad
const runestoneElements = tempContainer.querySelectorAll(".ptx-runestone-container");
[...runestoneElements].forEach((e) => {
const rsId = e.querySelector("[data-component]")?.id;
const onPage = document.getElementById(rsId);
if (onPage) {
e.innerHTML = `<div class="para">The interactive that belongs here is already on the page and cannot appear multiple times. <a href="#${rsId}">Scroll to interactive.</a>`;
} else {
// let runestone start rendering it
window.runestoneComponents.renderOneComponent(e);
}
});
// now move all contents to the real output element
const children = [...tempContainer.children];
this.outputElement.innerHTML = "";
this.outputElement.append(...children);
// render any knowls and mathjax in the knowl
addKnowls(this.outputElement);
// try prism highlighting
Prism.highlightAllUnder(this.outputElement);
// force any scripts (e.g. sagecell) to execute by evaling them
[...this.outputElement.getElementsByTagName("script")].forEach((s) => {
if (
s.getAttribute("type") === null ||
s.getAttribute("type") === "text/javascript"
) {
eval(s.innerHTML);
}
});
})
.catch((data) => {
console.log("Error fetching knowl content: " + data);
});
}
}
}
+109
View File
@@ -0,0 +1,109 @@
/****************************************************
*
* mathjaxknowl.js
*
* Implements \knowl{url}{math} macro for MathJax. Knowls are
* described at
*
* http://www.aimath.org/knowlepedia/
*
* Be sure to change the loadComplete() address to the URL
* of the location of this file on your server.
*
* You can load it via the config=file parameter on the script
* tag that loads MathJax.js, or by including it in the extensions
* array in your configuration.
*
* Based on an approach developed by Tom Leathrum. See
*
* http://groups.google.com/group/mathjax-users/browse_thread/thread/d8a8d081b8e63242
*
* for details.
*/
MathJax.Extension.Knowl = {
version: "1.0",
//
// Reveal or hide a MathJax knowl
//
Show: function (url,id) {
var oid = "MathJax-knowl-output-"+id,
uid = "MathJax-kuid-"+id;
if ($("#"+oid).length) {
$("#"+uid).slideToggle("fast");
} else {
var the_content = "<div class='knowl-output' id='"+uid+"'>" +
"<div class='knowl'>" +
"<div class='knowl-content' id='"+oid+"'>" +
"loading '"+url+"'" +
"</div>" +
"</div>" +
"</div>";
var the_parent = $("#MathJax-knowl-"+id).closest("p, article, .displaymath");
if(the_parent.length == 0) {
the_parent = $("#MathJax-knowl-"+id).closest(".MJXc-display").next();
if(the_parent.length == 0) {
the_parent = $("#MathJax-knowl-"+id).parent().parent().parent().parent();
}
}
the_parent.after(the_content);
$("#"+oid).load(url,function () {
MathJax.Hub.Queue(["Typeset",MathJax.Hub,uid]);
});
$("#"+uid).slideDown("slow");
}
},
//
// Get a unique ID for the knowl
//
id: 0,
GetID: function () {return this.id++}
};
MathJax.Callback.Queue(
MathJax.Hub.Register.StartupHook("TeX Jax Ready",function () {
var TEX = MathJax.InputJax.TeX,
TEXDEF = TEX.Definitions,
KNOWL = MathJax.Extension.Knowl;
TEXDEF.macros.knowl = "Knowl";
TEX.Parse.Augment({
//
// Implements \knowl{url}{math}
//
Knowl: function (name) {
var url = this.GetArgument(name), math = this.ParseArg(name);
if (math.inferred && math.data.length == 1)
{math = math.data[0]} else {delete math.inferred}
var id = KNOWL.GetID();
this.Push(math.With({
"class": "MathJax_knowl",
href: "javascript:MathJax.Extension.Knowl.Show('"+url+"','"+id+"')",
id: "MathJax-knowl-"+id,
// border and padding will only work properly if given explicitly on the element
style: "color:blue; border-bottom: 1px dotted #00A; padding-bottom: 1px"
}));
}
});
}),
MathJax.Hub.Register.StartupHook("onLoad",function () {
MathJax.Ajax.Styles({
".MathJax_knowl:hover": {"background-color": "#DDF"}
});
}),
["Post",MathJax.Hub.Startup.signal,"TeX knowl ready"]
);
MathJax.Ajax.loadComplete("http://pretextbook.org/js/lib/mathjaxknowl.js");
MathJax.Ajax.loadComplete("https://pretextbook.org/js/lib/mathjaxknowl.js");
+48
View File
@@ -0,0 +1,48 @@
const { Configuration } = MathJax._.input.tex.Configuration;
const { CommandMap } = MathJax._.input.tex.SymbolMap;
const NodeUtil = MathJax._.input.tex.NodeUtil.default;
var GetArgumentMML = function (parser, name) {
var arg = parser.ParseArg(name);
if (!NodeUtil.isInferred(arg)) {
return arg;
}
var children = NodeUtil.getChildren(arg);
if (children.length === 1) {
return children[0];
}
var mrow = parser.create("node", "mrow");
NodeUtil.copyChildren(arg, mrow);
NodeUtil.copyAttributes(arg, mrow);
return mrow;
};
let mathjaxKnowl = {};
/**
* Implements \knowl{url}{math}
* @param {TexParser} parser The calling parser.
* @param {string} name The TeX string
*/
mathjaxKnowl.Knowl = function (parser, name) {
var url = parser.GetArgument(name);
var arg = GetArgumentMML(parser, name);
var mrow = parser.create("node", "mrow", [arg], {
tabindex: '0',
"data-knowl": url
});
parser.Push(mrow);
};
new CommandMap(
"knowl",
{
knowl: ["Knowl"]
},
mathjaxKnowl
);
const configuration = Configuration.create("knowl", {
handler: {
macro: ["knowl"]
}
});