
Summary: Add support for math-mode accents. This involves a couple changes. First, in order to correctly position the accents, we must know the kern between every character and the "skewchar" in that font. To do this, we improve our tfm parser to run the mini-kern-language and calculate kerns. We then export these into fontMetrics.js. Then, we add normal support for accents. In particular, we do some special handling for supsubs around accents. This involves building the supsub separately without the accent, and then replacing its base with the built accent. Finally, the character in the fonts for the \vec command is a combining unicode character, so it is shifted to the left, but none of the other characters do this. We add some special handling for \vec to account for this. Fixes #7 Test Plan: - Make sure tests pass - Make sure no huxley screenshots changed, and the new one looks good Reviewers: alpert Reviewed By: alpert Differential Revision: http://phabricator.khanacademy.org/D13157
192 lines
4.7 KiB
JavaScript
192 lines
4.7 KiB
JavaScript
// These objects store the data about the DOM nodes we create, as well as some
|
|
// extra data. They can then be transformed into real DOM nodes with the toNode
|
|
// function or HTML markup using toMarkup. They are useful for both storing
|
|
// extra properties on the nodes, as well as providing a way to easily work
|
|
// with the DOM.
|
|
|
|
var utils = require("./utils");
|
|
|
|
var createClass = function(classes) {
|
|
classes = classes.slice();
|
|
for (var i = classes.length - 1; i >= 0; i--) {
|
|
if (!classes[i]) {
|
|
classes.splice(i, 1);
|
|
}
|
|
}
|
|
|
|
return classes.join(" ");
|
|
};
|
|
|
|
function span(classes, children, height, depth, maxFontSize, style) {
|
|
this.classes = classes || [];
|
|
this.children = children || [];
|
|
this.height = height || 0;
|
|
this.depth = depth || 0;
|
|
this.maxFontSize = maxFontSize || 0;
|
|
this.style = style || {};
|
|
}
|
|
|
|
span.prototype.toNode = function() {
|
|
var span = document.createElement("span");
|
|
|
|
span.className = createClass(this.classes);
|
|
|
|
for (var style in this.style) {
|
|
if (this.style.hasOwnProperty(style)) {
|
|
span.style[style] = this.style[style];
|
|
}
|
|
}
|
|
|
|
for (var i = 0; i < this.children.length; i++) {
|
|
span.appendChild(this.children[i].toNode());
|
|
}
|
|
|
|
return span;
|
|
};
|
|
|
|
span.prototype.toMarkup = function() {
|
|
var markup = "<span";
|
|
|
|
if (this.classes.length) {
|
|
markup += " class=\"";
|
|
markup += utils.escape(createClass(this.classes));
|
|
markup += "\"";
|
|
}
|
|
|
|
var styles = "";
|
|
|
|
for (var style in this.style) {
|
|
if (this.style.hasOwnProperty(style)) {
|
|
styles += utils.hyphenate(style) + ":" + this.style[style] + ";";
|
|
}
|
|
}
|
|
|
|
if (styles) {
|
|
markup += " style=\"" + utils.escape(styles) + "\"";
|
|
}
|
|
|
|
markup += ">";
|
|
|
|
for (var i = 0; i < this.children.length; i++) {
|
|
markup += this.children[i].toMarkup();
|
|
}
|
|
|
|
markup += "</span>";
|
|
|
|
return markup;
|
|
};
|
|
|
|
function documentFragment(children, height, depth, maxFontSize) {
|
|
this.children = children || [];
|
|
this.height = height || 0;
|
|
this.depth = depth || 0;
|
|
this.maxFontSize = maxFontSize || 0;
|
|
}
|
|
|
|
documentFragment.prototype.toNode = function() {
|
|
var frag = document.createDocumentFragment();
|
|
|
|
for (var i = 0; i < this.children.length; i++) {
|
|
frag.appendChild(this.children[i].toNode());
|
|
}
|
|
|
|
return frag;
|
|
};
|
|
|
|
documentFragment.prototype.toMarkup = function() {
|
|
var markup = "";
|
|
|
|
for (var i = 0; i < this.children.length; i++) {
|
|
markup += this.children[i].toMarkup();
|
|
}
|
|
|
|
return markup;
|
|
};
|
|
|
|
function symbolNode(value, height, depth, italic, skew, classes, style) {
|
|
this.value = value || "";
|
|
this.height = height || 0;
|
|
this.depth = depth || 0;
|
|
this.italic = italic || 0;
|
|
this.skew = skew || 0;
|
|
this.classes = classes || [];
|
|
this.style = style || {};
|
|
this.maxFontSize = 0;
|
|
}
|
|
|
|
symbolNode.prototype.toNode = function() {
|
|
var node = document.createTextNode(this.value);
|
|
var span = null;
|
|
|
|
if (this.italic > 0) {
|
|
span = document.createElement("span");
|
|
span.style.marginRight = this.italic + "em";
|
|
}
|
|
|
|
if (this.classes.length > 0) {
|
|
span = span || document.createElement("span");
|
|
span.className = createClass(this.classes);
|
|
}
|
|
|
|
for (var style in this.style) {
|
|
if (this.style.hasOwnProperty(style)) {
|
|
span = span || document.createElement("span");
|
|
span.style[style] = this.style[style];
|
|
}
|
|
}
|
|
|
|
if (span) {
|
|
span.appendChild(node);
|
|
return span;
|
|
} else {
|
|
return node;
|
|
}
|
|
};
|
|
|
|
symbolNode.prototype.toMarkup = function() {
|
|
// TODO(alpert): More duplication than I'd like from
|
|
// span.prototype.toMarkup and symbolNode.prototype.toNode...
|
|
var needsSpan = false;
|
|
|
|
var markup = "<span";
|
|
|
|
if (this.classes.length) {
|
|
needsSpan = true;
|
|
markup += " class=\"";
|
|
markup += utils.escape(createClass(this.classes));
|
|
markup += "\"";
|
|
}
|
|
|
|
var styles = "";
|
|
|
|
if (this.italic > 0) {
|
|
styles += "margin-right:" + this.italic + "em;";
|
|
}
|
|
for (var style in this.style) {
|
|
if (this.style.hasOwnProperty(style)) {
|
|
styles += utils.hyphenate(style) + ":" + this.style[style] + ";";
|
|
}
|
|
}
|
|
|
|
if (styles) {
|
|
needsSpan = true;
|
|
markup += " style=\"" + utils.escape(styles) + "\"";
|
|
}
|
|
|
|
var escaped = utils.escape(this.value);
|
|
if (needsSpan) {
|
|
markup += ">";
|
|
markup += escaped;
|
|
markup += "</span>";
|
|
return markup;
|
|
} else {
|
|
return escaped;
|
|
}
|
|
};
|
|
|
|
module.exports = {
|
|
span: span,
|
|
documentFragment: documentFragment,
|
|
symbolNode: symbolNode
|
|
};
|