CoffeeScript JavaScript Shell CSS HTML
cben New certificate
Not Before: May 10 18:51:26 2018 GMT
Not After : Aug  8 18:51:26 2018 GMT
Latest commit 05577d6 May 10, 2018
Failed to load latest commit information.
.openshift/action_hooks [openshift] Write to REPO/.npmrc instead of ~/.npmrc Sep 3, 2015
CodeMirror @ f65d506 Correct CM commit to current `direction` branch instead of my fork of… Jan 14, 2015
CodeMirror-MathJax @ 2e5a9fa Revert New CM-MJ — it made all math tiny. (haven't deployed to prod.) Dec 17, 2015
MathJax @ a4b82c5 New CM 3.8: minor stex & markdown improvements. gfm strikethrough. Jan 13, 2015
deployment New certificate May 10, 2018
firebase @ 3bbc43b Upgrade firebase lib from 1.0.11 to 2.2.4. Apr 30, 2015
firebase-config Fix security test to allow 40X and not only 403. Apr 2, 2014
firepad @ 89db92b firepad 1.1.1: can work with CM 5.x May 19, 2015
fonts Use Bitstream Charter digits (0-9) over Georgia's small old-style dig… Oct 11, 2015
test [test] Bump a the newer of Safari and IE pairs to 'latest' Nov 16, 2017
.gitignore s/letsencrypt/dehydrated/ in .gitignore Aug 11, 2017
.gitmodules got renamed to dehydrated Sep 14, 2016
.nojekyll Bypass Jekyll on github pages. Sep 18, 2013
.travis.yml [Travis] Also test nodejs 6, latest on Openshift Online v3 (#173) Oct 3, 2017
LICENSE Use Bitstream Charter digits (0-9) over Georgia's small old-style dig… Oct 11, 2015 README: Travis badge: only `gh-pages` branch Oct 31, 2016
index.html Hide $\binom...$ until the MJ typesets the logo. Jan 7, 2016
mathdown.css Add <noscript> (shamed by Jan 6, 2016
mathdown.js GFM tasklists: monospace inset style, toggle on click. Dec 17, 2015
npm-shrinkwrap.json New deps Dec 21, 2017
package.json New deps Dec 21, 2017
redirects.js Send Content-Length on redirects even for HEAD requests. Oct 6, 2015 Server log: hide doc ID in redirect lines too Nov 17, 2017
wercker.yml fix file name typo. Jan 14, 2015

Collaborative markdown with math. Main features:

  • Markdown is styled in-place, no source/preview separation.
  • Edits are synced in real time.
  • Access control is simply by sharing the secret URL. No sign up needed to collaborate.
  • LaTeX-syntax formulas rendered in-place when cursor is outside formula.

Powered by CodeMirror, MathJax and Firebase's Firepad. I'm using "CM" = CodeMirror, "MJ" = MathJax abbreviations a lot in the project.

Alpha quality – will eat your math, burn your bookmarks & expose your secrets. I mean it. See for example #85 — saving would sometimes be silently broken, for half a year! I'm working to make it more robust (and tested) but for now, be careful.

Issues: mathdown HuBoard CodeMirror-MathJax issues


My code is under MIT License. Exception: font/ contains a subsetted Bitstream Charter font under a permissive license - see fonts/LICENSE.


  • CodeMirror is also MIT.

  • MathJax is under Apache License 2.0.

  • My CodeMirror-MathJax glue is also MIT.

  • The collaborative editor Firepad is MIT. It calls firebase javascipt API.

  • Firebase is a proprietary service; their client-side javascipt API firebase.js is also proprietary, though apparently fine to distribute in practice — (#4). [firbease.js has been accidentally MIT-licensed for a time but I've upgraded to newer versions so this doesn't apply.]

    I'm not including firebase.js directly but using it as a git submodule.

Document hosting and privacy(?) on Firebase

All user data is stored in Firebase, now owned by Google. Their privacy policy. Documents access (read AND edit) is by secret document id which is part of the url. This is grossly unsecure unless using HTTPS.

The downside is users can't really control their data. Running a "self-hosted" copy of the site still leaves all data in the hands of Firebase. See #4 for more discussion.

The upside is all forks interoperate; you can change the design or tweak the editor and still access same documents. E.g. and look different but access the same doc -- and real-time collaboration between them works!

I'm so far on the free Firebase plan - 100 devices (not sure if 1:1 with users), 1GB Data Storage (used < 100MB). => Will need 49USD/mo plan as soon as I get non-negligible usage. (only visible to me)

Deletion is impossible

The current Firebase security rules make document history append-only. That's a nice safety feature but it means that once a document's URL gets out, it's full history is forever accessible to the the world. This must change eventually (#92).

Browser support

Basically whatever CodeMirror supports: IE8+ and about everything else. But mobile is currently almost unusable (#81).

JavaScript is required (and this includes running the non-Free firebase.js in your browser). You can't even read documents without JavaScript; reading won't be hard to fix (#7) — but editing documents without JavaScript is implausible (I plan to settle for append-only form).


The only cookies I'm aware of:

  • GEAR session cookie set by OpenShift hosting (I presume for server stickiness, which I don't actually need).
  • cookie set for a year(?) if you manually change MathJax settings.

I'm not sure Firebase never sets cookies. Things will change once I implement login (#50).

Installing dependencies

Dependency Status devDependency Status

  1. After checking out, run this to materialize client-side dependencies:

    git submodule update --init --recursive

Append --remote to upgrade to newest versions of all submodules (need to commit afterwards if anything changed). Known constraints on updating all deps:

* firepad only includes pre-built dist/firepad.js in tagged versions (after every release they strip it back).
* [CodeMirror-MathJax currently doesn't support MathJax 2.5](

(I'm directly working in gh-pages branch without a master branch. GH Pages automatically resolves https://... submodules. It's no longer the primary hosting but it's still useful to test the static version works.)

  1. To install server-side dependencies (and devDependencies) listed in package.json run:

    npm install

(But when deploying to RHcloud or Heroku, npm install might run in --production mode and devDependencies won't be available.)

To see whether any updates are needed/possible, run npm outdated. To update run:

npm update --save
npm shrinkwrap

Then commit the new `package.json` and `npm-shrinkwrap.json`.

TODO: find way to use same node.js version in dev and prod?


Travis test runner Saucelabs browser tests

test/ runs tests on several browsers using free browser testing courtesy of Sause Labs. There are pathetically few tests.

To run the tests:

npm install  # once
npm test

To run only some tests and/or browsers, use:

./node_modules/.bin/mocha --grep firefox

The test runs automatically on any commit and pull request. I've tried several free services for this, and currently prefer Travis:

  • Travis - works, open source code. Controlled by .travis.yml.
  • Wercker - works. Controlled by wercker.yml.
  • Drone - Docker-based, open source rewrite in progress. Alas, always timed out during test. Test config on the web.
  • Shippable - builds history only accessible by me? Bad, I want public. Controlled by .travis.yml.
  • Codeship - same, dashboard is private. Test config on the web.

Where it's deployed and how to run your fork

The main deployment runs on (Openshift hosting operated by RedHat), and points to it. The dynamic server has also been tested on Heroku. See deployment/ subdirectory for details.

However you run it, you can open the same document ids (doc=...) and real-time collaboration will work!

Quick ways to run:

![Launch on OpenShift]( ON.svg) — make sure to replace with your fork & branch as needed. Don't enable scaling without reading "Creating an app" in deployment/ Grab a tea - takes up to 10 minutes. (Remember it'll not auto-update, it'll be up to you to git push newer versions...)

Deploy on Heroku:

heroku create my-mathdown --remote heroku-my-mathdown
git push heroku-my-mathdown gh-pages:master

Run local server (

npm install  # once
env PORT=8001 npm start  # Prints URL you can click

(you can choose any port of course. Ctrl+C when done.)

This app mostly works as pure static pages, and I intend to keep it this way.

  • From a checkout, just open index.html in your browser.

  • Github Pages serves the gh-pages branch at

  • For other branches/commits, there is no trivial solution - would be great but doesn't currently support submodules.

  • The easiest way to run (and share) uncommitted modifications is probably Cloud 9. TODO: test, details.

The only benefits the dynamic server is going to bring (not implemented yet) will be:

  1. Including the document text in the HTTP response for search engines (#7).
  2. Prettier instead of URLs (#59).

Other things called "mathdown":

I should really talk to these folk whether it's OK that I'm using the name and the domain...