diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..daa4bd6f --- /dev/null +++ b/.travis.yml @@ -0,0 +1,7 @@ +language: node_js +node_js: +- 'iojs' +sudo: false +script: + - npm install + - npm test diff --git a/README.md b/README.md index 63a398fc..342cc3a5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# MathJax-node +# MathJax-node [![Build Status](https://travis-ci.org/mathjax/MathJax-node.svg?branch=develop)](https://travis-ci.org/mathjax/MathJax-node) This repository contains files that provide APIs to call MathJax from node.js programs. There is an API for converting individual math @@ -23,3 +23,55 @@ These API's can produce PNG images, but that requires the should be installed in the `batik` directory. See the README file in that directory for more details. +# Getting started + +MahJax-node provides two libraries, `lib/mj-single.js` and `lib/mj-page.js`. Below are two very minimal examples -- be sure to check out the examples in `./bin/` for more advanced configurations. + +* `lib/mj-single.js` is optimized for processing single equations. + + +```javascript +// a simple TeX-input example +var mjAPI = require("./lib/mj-single.js"); +mjAPI.config({ + MathJax: { + // traditional MathJax configuration + } +}); +mjAPI.start(); + +var yourMath = 'E = mc^2'; + +mjAPI.typeset({ + math: yourMath, + format: "TeX", // "inline-TeX", "MathML" + mml:true, // svg:true, +}, function (data) { + if (!data.errors) {console.log(data.mml)} +}); +``` + + +* `lib/mj-page.js` is optimized for handling full HTML pages. + + +```javascript +var mjAPI = require("./lib/mj-page.js"); +var jsdom = require("jsdom").jsdom; + +var document = jsdom("Test

Let's test mj-page

\\[f: X \\to Y\\], where \\( X = 2^{\mathbb{N}}\\)

"); + +mjAPI.start(); + +mjAPI.typeset({ + html: document.body.innerHTML, + renderer: "NativeMML", + inputs: ["TeX"], + xmlns: "mml" +}, function(result) { + "use strict"; + document.body.innerHTML = result.html; + var HTML = "\n" + document.documentElement.outerHTML.replace(/^(\n|\s)*/, ""); + console.log(HTML); +}); +``` diff --git a/lib/mj-page.js b/lib/mj-page.js index 4356d991..abca8871 100644 --- a/lib/mj-page.js +++ b/lib/mj-page.js @@ -87,7 +87,7 @@ var MathJax; // filled in once MathJax is loaded var serverState = STATE.STOPPED; // nothing loaded yet var timer; // used to reset MathJax if it runs too long -var tmpfile = os.tmpdir() + "/mj-single-svg"; // file name prefix to use for temp files +var tmpfile = os.tmpdir() + "/mj-single-svg" + process.pid; // file name prefix to use for temp files var document, window, content, html; // the DOM elements @@ -116,7 +116,7 @@ var STYLES; // filled in when SVG is loaded function GetWindow() { document = jsdom(); html = document.firstChild; - window = document.parentWindow; + window = document.defaultView; window.console = console; window.onerror = function (err,url,line) {AddError("Error: "+err)} content = document.body.appendChild(document.createElement("div")); @@ -148,7 +148,7 @@ function ConfigureMathJax() { tex2jax: {inlineMath: [['$','$'],['\\(','\\)']], preview:"none"}, mml2jax: {preview:"none"}, asciimath2jax: {preview:"none"}, - SVG: {useFontCache: true, useGlobalCache: false}, + SVG: {useFontCache: true, useGlobalCache: false, EqnChunk: 1000000, EqnDelay: 0}, // // This gets run before MathJax queues any actions @@ -210,6 +210,13 @@ function ConfigureMathJax() { AddError("TeX parse error",message[1]); }); + // + // Set the delays to 0 (we don't need to update the screen) + // + MathJax.Hub.processSectionDelay = 0; + MathJax.Hub.processUpdateTime = 10000000; // don't interrupt processing of output + MathJax.Hub.processUpdateDelay = 0; + // // Adjust the SVG output jax // @@ -283,7 +290,7 @@ function ConfigureMathJax() { jax.SVG.ex = ex = (data||defaults).ex; jax.SVG.em = em = ex / SVG.TeX.x_height * 1000; // scale ex to x_height jax.SVG.cwidth = width / em * 1000; - jax.SVG.lineWidth = (linebreak ? width / em *1000 : 1000000); + jax.SVG.lineWidth = (linebreak ? width / em *1000 : SVG.BIGDIMEN); } // // Set state variables used for displaying equations in chunks @@ -361,7 +368,25 @@ function ConfigureMathJax() { window.MathJax.extensions.push(matches[1] + '.js'); } } - if (MathJaxConfig) {Insert(window.MathJax,MathJaxConfig)} + + // + // Turn arrays into jsdom window arrays + // (so "instanceof Array" will identify them properly) + // + var adjustArrays = function (obj) { + for (var id in obj) {if (obj.hasOwnProperty(id)) { + if (obj[id] instanceof Array) { + var A = window.Array(); + obj[id] = A.concat.apply(A,obj[id]); + } else if (typeof obj[id] === "object") { + adjustArrays(obj[id]); + } + }} + } + if (MathJaxConfig) { + adjustArrays(MathJaxConfig); + Insert(window.MathJax,MathJaxConfig); + } } // @@ -501,7 +526,7 @@ function ConfigureTypeset() { // Configure SVG and TeX // SVG.defaultEx = data.ex; - SVG.defaultWidth = data.width; + SVG.defaultWidth = data.width * data.ex; SVG.config.linebreaks.automatic = data.linebreaks; SVG.config.linebreaks.width = data.width * data.ex; SVG.config.useFontCache = data.useFontCache; diff --git a/lib/mj-single.js b/lib/mj-single.js index 87293420..36cbd386 100644 --- a/lib/mj-single.js +++ b/lib/mj-single.js @@ -85,7 +85,7 @@ var MathJaxConfig; // configuration for when starting MathJax var MathJax; // filled in once MathJax is loaded var serverState = STATE.STOPPED; // nothing loaded yet var timer; // used to reset MathJax if it runs too long -var tmpfile = os.tmpdir() + "/mj-single-svg"; // file name prefix to use for temp files +var tmpfile = os.tmpdir() + "/mj-single-svg" + process.pid; // file name prefix to use for temp files var document, window, content, html; // the DOM elements @@ -117,7 +117,7 @@ var delimiters = { function GetWindow() { document = jsdom(); html = document.firstChild; - window = document.parentWindow; + window = document.defaultView; window.console = console; window.onerror = function (err,url,line) {AddError("Error: "+err)} content = document.body.appendChild(document.createElement("div")); @@ -149,7 +149,7 @@ function ConfigureMathJax() { tex2jax: {inlineMath: [['$','$'],['\\(','\\)']], preview:"none"}, mml2jax: {preview:"none"}, asciimath2jax: {preview:"none"}, - SVG: {useFontCache: true, useGlobalCache: false}, + SVG: {useFontCache: true, useGlobalCache: false, EqnChunk: 1000000, EqnDelay: 0}, // // This gets run before MathJax queues any actions @@ -211,6 +211,13 @@ function ConfigureMathJax() { AddError("TeX parse error: "+message[1]); }); + // + // Set the delays to 0 (we don't need to update the screen) + // + MathJax.Hub.processSectionDelay = 0; + MathJax.Hub.processUpdateTime = 10000000; // don't interrupt processing of output + MathJax.Hub.processUpdateDelay = 0; + // // Adjust the SVG output jax // @@ -278,7 +285,7 @@ function ConfigureMathJax() { jax.SVG.ex = ex = (data||defaults).ex; jax.SVG.em = em = ex / SVG.TeX.x_height * 1000; // scale ex to x_height jax.SVG.cwidth = width / em * 1000; - jax.SVG.lineWidth = (linebreak ? width / em * 1000 : 1000000); + jax.SVG.lineWidth = (linebreak ? width / em * 1000 : SVG.BIGDIMEN); } // // Set state variables used for displaying equations in chunks @@ -335,7 +342,25 @@ function ConfigureMathJax() { window.MathJax.extensions.push(matches[1] + '.js'); } } - if (MathJaxConfig) {Insert(window.MathJax,MathJaxConfig)} + + // + // Turn arrays into jsdom window arrays + // (so "instanceof Array" will identify them properly) + // + var adjustArrays = function (obj) { + for (var id in obj) {if (obj.hasOwnProperty(id)) { + if (obj[id] instanceof Array) { + var A = window.Array(); + obj[id] = A.concat.apply(A,obj[id]); + } else if (typeof obj[id] === "object") { + adjustArrays(obj[id]); + } + }} + } + if (MathJaxConfig) { + adjustArrays(MathJaxConfig); + Insert(window.MathJax,MathJaxConfig); + } } // @@ -530,7 +555,7 @@ function StartQueue() { HUB = MathJax.Hub; SVG.defaultEx = data.ex; - SVG.defaultWidth = data.width; + SVG.defaultWidth = data.width * data.ex; SVG.config.linebreaks.automatic = data.linebreaks; SVG.config.linebreaks.width = data.width * data.ex; SVG.config.useFontCache = data.useFontCache; diff --git a/package.json b/package.json index a24e4034..0a74b6cb 100644 --- a/package.json +++ b/package.json @@ -1,27 +1,37 @@ { - "name": "MathJax-node", - "version": "0.3.0", - "description": "API's for calling MathJax from node.js", - "keywords": ["MathJax","math","svg","MathML","TeX","AsciiMath"], - "maintainers": [ - "MathJax Consortium (http://www.mathjax.org)" - ], - "bugs": { - "url": "http://github.com/mathjax/MathJax-node/issues" - }, - "license": { - "type": "Apache", - "url": "http://github.com/mathjax/MathJax-node/blob/master/LICENSE" - }, - "repository": { - "type": "git", - "url": "git://github.com/mathjax/MathJax-node.git" - }, - "dependencies": { - "jsdom": "3.1.1", - "speech-rule-engine": "*", - "yargs": "*", - "MathJax": "https://github.com/mathjax/MathJax/tarball/mathjax-node-2.5.1" - }, - "main": "./lib/mj-page.js" + "name": "mathjax-node", + "version": "0.4.0", + "description": "API's for calling MathJax from node.js", + "keywords": [ + "MathJax", + "math", + "svg", + "MathML", + "TeX", + "AsciiMath" + ], + "maintainers": [ + "MathJax Consortium (http://www.mathjax.org)" + ], + "bugs": { + "url": "http://github.com/mathjax/MathJax-node/issues" + }, + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "git://github.com/mathjax/MathJax-node.git" + }, + "dependencies": { + "jsdom": "^6.0.0", + "speech-rule-engine": "*", + "yargs": "^3.0.0", + "MathJax": "https://github.com/mathjax/MathJax/tarball/mathjax-node-2.5.1" + }, + "scripts": { + "test": "tape test/*.js" + }, + "main": "./lib/mj-page.js", + "devDependencies": { + "tape": "^4.0.3" + } } diff --git a/test/base-mathjax.js b/test/base-mathjax.js new file mode 100644 index 00000000..12aeb250 --- /dev/null +++ b/test/base-mathjax.js @@ -0,0 +1,17 @@ +var tape = require('tape'); +var mjAPI = require("..//lib/mj-single.js"); + +tape('basic test: check MathJax core', function(t) { + t.plan(1); + + var tex = ''; + mjAPI.start(); + + mjAPI.typeset({ + math: tex, + format: "TeX", + mml: true + }, function(data) { + t.ok(data.mml, 'MathJax core seems ok'); + }); +}); diff --git a/test/base-speechruleengine.js b/test/base-speechruleengine.js new file mode 100644 index 00000000..8e7de807 --- /dev/null +++ b/test/base-speechruleengine.js @@ -0,0 +1,18 @@ +var tape = require('tape'); +var mjAPI = require("..//lib/mj-single.js"); + +tape('basic test: check speechruleengine', function(t) { + t.plan(1); + + var tex = 'MathJax'; + mjAPI.start(); + + mjAPI.typeset({ + math: tex, + format: "TeX", + mml: true, + speakText: true + }, function(data) { + t.ok(data.speakText, 'speechruleengine seems ok'); + }); +}); diff --git a/test/issue104.js b/test/issue104.js new file mode 100644 index 00000000..80772df4 --- /dev/null +++ b/test/issue104.js @@ -0,0 +1,23 @@ +var tape = require('tape'); +var mjAPI = require("..//lib/mj-single.js"); +var jsdom = require('jsdom').jsdom; + +tape('the SVG width should match the default', function(t) { + t.plan(1); + + mjAPI.start(); + var tex = 'a \\\\ b'; + var expected = '100ex'; + + mjAPI.typeset({ + math: tex, + format: "TeX", + svg: true + }, function(data) { + var document = jsdom(data.svg); + var window = document.defaultView; + var element = window.document.getElementsByTagName("svg")[0]; + var width = element.getAttribute('width'); + t.equal(width, expected); + }); +});