Phil's Musings2018-10-20T01:39:09+00:00http://philschatz.comPhilip SchatzEmbeddable and Accessible PuzzleScript Games2018-09-22T00:00:00+00:00http://philschatz.com/2018/09/22/puzzlescript<p>I ❤️ Puzzle Games.</p>
<blockquote>
<p>I want kids to play games that don’t talk down to them, encourage them to figure out game mechanics on their own, <strong>and</strong> I want non-sighted people to play video games.</p>
</blockquote>
<p>Introducing: the <a href="https://github.com/philschatz/puzzlescript">puzzlescript</a> package.</p>
<p><a href="https://github.com/philschatz/puzzlescript"><img src="https://user-images.githubusercontent.com/253202/47249708-7a204080-d3dc-11e8-8aee-73179c314a41.gif" alt="a couple levels of the game Pot Wash Panic" /></a></p>
<p>Many games have puzzles sprinkled in, but I like ones that are distilled into just solving puzzles.</p>
<p>I first realized how engrossing these were while playing <a href="http://the-witness.net">The Witness</a> with friends. One person would control the player but everyone would be solving the puzzle in their head just by watching. We played together for days.</p>
<p>Then, I tried it with kids. It turns out, a 6-year-old grasped the puzzle concepts in <a href="http://the-witness.net">The Witness</a> faster than her parents and was <em>explaining to her parents</em> how the puzzles worked.</p>
<p>Then, I started looking for more and stumbled upon <a href="https://twitter.com/increpare">increpare</a>’s awesome <a href="https://www.puzzlescript.net">PuzzleScript</a>.</p>
<p>Unlike other video games that tend to teach kids to memorize facts (like “Carmen San Diego”, “Oregon Trail”, or “Math Blaster”), these encourage kids to think critically, and problem-solve in groups.</p>
<p>Specifically, PuzzleScript games are interesting because the whole game is a text file and the levels are typically pretty small. This means it’s easy for kids to explore but it also makes the games playable by people that can’t see. The reason is that all of the sprites have human-readable names. So we can read those out instead of just showing colored pixels.</p>
<h2 id="background">Background</h2>
<p>Inspiration came from both the tons of easter-eggs found in software (<a href="https://www.blog.google/products/chrome/chrome-dino/">Chrome Dino Game</a>, <a href="https://en.wikipedia.org/wiki/List_of_Easter_eggs_in_Microsoft_products">various Microsoft ones</a> and commandline tools) as well as a desire to get more people in general to play video games.</p>
<h2 id="goals">Goals</h2>
<ul>
<li>get kids in classrooms playing these games</li>
<li>get vision-impaired people playing these games</li>
<li>get 404 pages to have these games when something is broken (like the <a href="https://www.blog.google/products/chrome/chrome-dino/">Chrome Dino Game</a>). Example: <a href="/404">this website’s 404 page</a></li>
<li>get people to play these instead of Sudoku to exercise their brains more</li>
</ul>
<h2 id="try-it-out">Try it out!</h2>
<p>Games can run embedded in a webpage <strong>or</strong> in a commandline terminal. There are <strong>over 200 games</strong> to choose from!</p>
<h3 id="terminal">Terminal</h3>
<p>All you need is <a href="https://nodejs.org">node 4 or higher</a> and then run the following:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm install <span class="nt">--global</span> puzzlescript
puzzlescript
</code></pre></div></div>
<p>See <a href="https://github.com/philschatz/puzzlescript#screencaps">philschatz/puzzlescript</a> for examples.</p>
<h3 id="embed-in-a-webpage">Embed in a Webpage</h3>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><body></span>
<span class="nt"><table</span> <span class="na">id=</span><span class="s">"the-game-ui"</span><span class="nt">></table></span>
<span class="nt"><script </span><span class="na">src=</span><span class="s">"https://unpkg.com/[email protected]/lib/webpack-output.js"</span><span class="nt">></script></span>
<span class="nt"><script></span>
<span class="kd">const</span> <span class="nx">gameSourceString</span> <span class="o">=</span> <span class="s1">'...'</span> <span class="c1">// Source code for the game</span>
<span class="kd">const</span> <span class="nx">table</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="s1">'#the-game-ui'</span><span class="p">)</span>
<span class="kd">const</span> <span class="nx">engine</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">PuzzleScript</span><span class="p">.</span><span class="nx">TableEngine</span><span class="p">(</span><span class="nx">table</span><span class="p">)</span>
<span class="nx">engine</span><span class="p">.</span><span class="nx">setGame</span><span class="p">(</span><span class="nx">gameSourceString</span><span class="p">,</span> <span class="mi">0</span> <span class="cm">/*startLevel*/</span><span class="p">)</span>
<span class="nx">engine</span><span class="p">.</span><span class="nx">start</span><span class="p">()</span>
<span class="nt"></script></span>
<span class="nt"></body></span>
</code></pre></div></div>
<p>Visit the <a href="/puzzlescript/">demo page</a> to play several games (there are <strong>over 200</strong>), or see <a href="/404">this website’s 404 page</a> for an example of using it in a 404 page.</p>
<h2 id="for-next-time">For Next Time</h2>
<p>I would like to go through more details on how the code is organized. But if something does not work or is not clear, <a href="https://github.com/philschatz/puzzlescript/issues">create an issue</a>.</p>
<h2 id="more-examples">More Examples</h2>
<h3 id="mirror-isles-original">Mirror Isles (<a href="http://www.draknek.org/games/puzzlescript/mirrors.php">original</a>)</h3>
<p><img src="https://user-images.githubusercontent.com/253202/47133542-ce0d1700-d26e-11e8-851f-233d27aaf0b8.gif" alt="mirror-isles" /></p>
<p>More examples can be found in the <a href="https://github.com/philschatz/puzzlescript">README</a> or see the <a href="/puzzlescript/">demo page</a> to play them in a browser.</p>
Automatically record HTTPS requests instead of manually creating mock files2017-05-15T00:00:00+00:00http://philschatz.com/2017/05/15/record-fetch-requests<p>Creating mock HTTP requests seems to be the go-to method for developers.</p>
<p>But wouldn’t is be easier if you could just point your tests to a server and record the HTTP responses and then play them back?</p>
<p>Now there is <a href="https://github.com/philschatz/fetch-vcr">fetch-vcr</a>!</p>
<p>Since <a href="https://fetch.spec.whatwg.org">fetch</a> API is in browsers, there is an easier way to do this in JavaScript!</p>
<p><a href="https://github.com/philschatz/fetch-vcr">fetch-vcr</a> will record and play back those HTTP requests for you.</p>
<h1 id="how-does-it-work">How does it work?</h1>
<p>Just load the <code class="highlighter-rouge">fetch-vcr</code> package in your tests and it will proxy any calls to <code class="highlighter-rouge">fetch(url, options)</code>.</p>
<p>Each HTTP response is saved to the filesystem as a cassette (also known as a fixture).
The cassette contains all of the response headers and the response body.</p>
<p>Depending on the <code class="highlighter-rouge">VCR_MODE</code> it will do the following:</p>
<ul>
<li><code class="highlighter-rouge">playback</code>: (default) it will load the recorded cassette of the request instead of talking to the server</li>
<li><code class="highlighter-rouge">record</code>: it will talk to the server and save the response to a local file (cassette)</li>
<li><code class="highlighter-rouge">cache</code>: it will try to load the cassette and if the cassette is not found, it will do the same thing as <code class="highlighter-rouge">record</code></li>
</ul>
<p>Because it saves files to the filesystem, it works slightly differently when used in NodeJS and when used in a browser.</p>
<h1 id="how-does-it-work-in-nodejs">How does it work in NodeJS?</h1>
<p>It’s super-simple. When running tests:</p>
<ol>
<li>Make sure your code runs <code class="highlighter-rouge">var fetch = require('fetch-vcr');</code> instead of <code class="highlighter-rouge">var fetch = require('node-fetch');</code></li>
<li>Record your cassettes by running <code class="highlighter-rouge">VCR_MODE=record npm test</code> instead of just <code class="highlighter-rouge">npm test</code></li>
<li>Optionally, change the directory that cassettes are saved to by running <code class="highlighter-rouge">fetchVCR.configure({fixturePath: '/path/to/folder/'})</code></li>
</ol>
<p>Viola! you have just recorded your HTTP requests.</p>
<h1 id="how-does-it-work-in-a-browser">How does it work in a browser?</h1>
<p>For browser tests it is a little bit more complicated because browsers do not save files to the filesystem.</p>
<p>Fortunately, browsers like PhantomJS or Selenium allow you to send data from the browser using <code class="highlighter-rouge">alert(msg)</code>. There are other ways if you would rather do it differently.</p>
<p>You can use the steps listed above but will need to do the following additional steps:</p>
<ol>
<li>Pass the <code class="highlighter-rouge">VCR_MODE</code> environment variable to the browser</li>
<li>Replace <code class="highlighter-rouge">fetchVCR.saveFile(rootPath, filename, contents) => Promise</code> with a function that calls <code class="highlighter-rouge">alert(JSON.stringify([rootPath, filename, contents]))</code></li>
<li>Parse the alerts and save them to disk using <code class="highlighter-rouge">fs.writeFile(filePath, contents)</code>
<ul>
<li><strong>Note:</strong> This code does not run in the browser; it runs in the JS file given to PhantomJS (or Selenium if you are using Selenium)</li>
</ul>
</li>
</ol>
<p>Check out <a href="https://github.com/philschatz/fetch-vcr">fetch-vcr</a> for more info.</p>
Introducing a Serverless Issue board for GitHub repositories and organizations!2016-04-06T00:00:00+00:00http://philschatz.com/2016/04/06/gh-board<p>At <a href="https://openstax.org">openstax.org</a> different projects use different ticket trackers because different people have different likes and preferences. We use Trello, Pivotal, GitHub Issues, and Wunderlist. But one thing that is common across all of our open source projects is GitHub.</p>
<p>As we’ve grown and as our projects have started to overlap we’ve realized there is value in having a common place to look and see what’s going on in all of the projects.</p>
<p><a href="https://github.com/philschatz/gh-board"><img src="https://cloud.githubusercontent.com/assets/253202/13620649/4ef888cc-e55f-11e5-8576-8970abba8660.png" alt="image" /></a></p>
<p><strong><a href="https://github.com/philschatz/gh-board">gh-board</a></strong> does all the things we need <em>and more</em>. If it doesn’t do something, submit a Pull Request and it will!</p>
<h1 id="other-ticket-trackers">Other ticket trackers</h1>
<p>Any ticket tracker presents additional friction to use with GitHub:</p>
<ul>
<li>it’s hidden behind a login
<ul>
<li>(when a ticket is linked in our IM client we don’t get a nice preview)</li>
</ul>
</li>
<li>the logins are different so <code class="highlighter-rouge">@mentioning</code> people is annoying</li>
<li>you have to remember a different type of markup language</li>
<li>you have to remember to link everything twice
<ul>
<li>so people looking at the ticket can get to the code and vice-versa</li>
</ul>
</li>
<li>you have to update the tracker when the Pull Request status changes (created, review, tested, merged, etc)</li>
<li>they don’t show the state of Pull Requests so you then have to click to see what the Pull Request status is</li>
<li>URL’s are difficult to share because frequently the state of the page is not in the URL
<ul>
<li>ie which milestones, columns, or other filter criteria are being used</li>
</ul>
</li>
</ul>
<p>As a developer/tester/UX: you’d still have to check multiple places to stay on top of everything (or hope that your email client doesn’t explode!)</p>
<h1 id="github-isnt-perfect-either">GitHub isn’t perfect either</h1>
<p>But GitHub Issues is not without its limitations:</p>
<ul>
<li>Issues are per-repository (we have 100 repositories)</li>
<li>Milestones are per-repository</li>
<li>Labels are per-repository</li>
<li>It is difficult to add additional metadata to a ticket</li>
<li>There is no <em>easy</em> way to have kanban-style columns</li>
</ul>
<h1 id="how-is-this-different-from-other-github-based-trackers">How is this different from other GitHub-based trackers?</h1>
<p>It has a few features that other ticket trackers lack (like huboard or waffleio):</p>
<ul>
<li>open source & free!</li>
<li>you can run it anywhere!
<ul>
<li>you can still use vanilla GitHub (nothing to import/export and <strong>no vendor lock-in</strong>)</li>
</ul>
</li>
<li>real-time collaborative editing of Issues</li>
<li>shows the state of related Issues/Pull Requests</li>
<li>shows CI status and merge conflict status</li>
<li>has charts (burndown, gantt, etc)</li>
<li>keeps track of multiple repositories from different organizations</li>
<li>and <strong>productivity-enhancing</strong> Easter Eggs!</li>
</ul>
<h2 id="related-issues-and-pull-requests">Related Issues and Pull Requests</h2>
<p><img src="https://cloud.githubusercontent.com/assets/253202/13620658/63f99478-e55f-11e5-8e9f-9babcfb69a29.png" alt="image" /></p>
<h2 id="ci-status-and-merge-conflict">CI Status and Merge Conflict</h2>
<ul>
<li>CI Status shows up as a green check mark or a red <code class="highlighter-rouge">x</code> on the top-right corner of a card</li>
<li>Merge conflicts are shown with a yellow warning and have a diagonal striped background</li>
</ul>
<p><img src="https://cloud.githubusercontent.com/assets/253202/13621863/bac1f62a-e568-11e5-9761-ce41c84b4eef.png" alt="image" /> <img src="https://cloud.githubusercontent.com/assets/253202/13621876/d1bcfeb0-e568-11e5-8a73-c5ef61645a88.png" alt="image" /> <img src="https://cloud.githubusercontent.com/assets/253202/13621905/dfee5920-e568-11e5-94df-98a887f63d24.png" alt="image" /></p>
<h2 id="real-time-collaborative-editing">Real-time Collaborative Editing</h2>
<p><img src="https://cloud.githubusercontent.com/assets/253202/13621429/8c917166-e565-11e5-8e80-10fab6d51253.gif" alt="gh-board_realtime-editing4" /></p>
<h2 id="issue-images">Issue Images</h2>
<p>If an Issue or Pull Request contains an image then it will be shown in the Issue</p>
<p><img src="https://cloud.githubusercontent.com/assets/253202/14223380/bbc026c2-f84c-11e5-9ccb-639f62aaf6d7.png" alt="image" /></p>
<h1 id="easter-eggs">Easter Eggs</h1>
<p>Plus, it comes with productivity-enhancing easter eggs you can unlock!</p>
<p><img src="https://cloud.githubusercontent.com/assets/253202/14037438/185532ee-f21a-11e5-8b83-20f8cd21b753.gif" alt="easter-eggs" /></p>
<h1 id="charts">Charts</h1>
<p>Since it stores all the open and closed tickets locally, we can generate all the fancy charts that other ticket trackers generate.</p>
<ul>
<li>Burnup chart: it clearly shows when new work is added to a Milestone</li>
<li>Gantt chart: shows when milestones are due and colors the bar based on the status of all the Issues</li>
</ul>
<p><img src="https://cloud.githubusercontent.com/assets/253202/14406693/5e05c870-fe7d-11e5-9564-ecddb08ebe0d.png" alt="burnup-chart" /></p>
<h1 id="how-does-it-work">How does it work?</h1>
<p>It:</p>
<ul>
<li>uses <a href="https://github.com/philschatz/octokat.js">octokat.js</a> and polls the GitHub API for changes</li>
<li>uses the 1st repository in the list to find the column labels and milestones
<ul>
<li>columns are defined as labels of the form <code class="highlighter-rouge">## - Column Title</code> or you can specify a regular expression</li>
</ul>
</li>
<li>stores all the Issues and Pull Requests in the browser (thanks to <code class="highlighter-rouge">IndexedDB</code>)
<ul>
<li>think of it like <code class="highlighter-rouge">git clone</code> but for Issues & Pull Requests</li>
</ul>
</li>
<li>searches the <code class="highlighter-rouge">IndexedDB</code> to find related issues</li>
</ul>
Introducing the atom pull-requests package!2016-04-02T00:00:00+00:00http://philschatz.com/2016/04/02/atom-pull-requests<p>As a programmer that uses GitHub, Pull Requests are a great way to discuss code but whenever I get feedback on code in a large codebase it is annoying to have to find where that change was. Since I use <a href="https://atom.io">atom.io</a> as my text editor, I decided to write a plugin that adds a great feature of GitHub (Pull Requests) into my text editor (which also happens to be written by GitHub). And <em>viola</em>! <strong><a href="https://github.com/philschatz/atom-pull-requests">pull-requests</a></strong>.</p>
<p>Whenever you check out a branch that has a Pull Request and open it in GitHub, you’ll see the GitHub comment directly on the line of code inside atom. And to help you find it, the Tree view on the left will show how many comments are in the directory so you can find the file.</p>
<p><a href="https://atom.io/packages/pull-requests"><img src="https://cloud.githubusercontent.com/assets/253202/11326511/82360626-9139-11e5-8466-ed2d356cb0d8.png" alt="screenshot" /></a></p>
<p>Here’s a slightly out-of-date screencast showing the whole process (including installing the plugin):</p>
<p><img src="https://cloud.githubusercontent.com/assets/253202/11237087/a3568100-8dab-11e5-8d9d-3bc9cc3dc5af.gif" alt="process" /></p>
<h1 id="hows-it-made">How’s it made?</h1>
<p>It uses the <a href="https://github.com/philschatz/octokat.js">octokat.js</a> npm package and the <a href="https://atom.io/packages/linter">linter</a> atom package. <a href="https://github.com/philschatz/atom-pull-requests">pull-requests</a> checks if your code is in a git repository (actually, a GitHub one) and then checks if the branch corresponds to a Pull Request in the repository (or the parent repository if this is a forked repository) and pulls out the comments on the changes of a file.</p>
<p>Then, it uses <code class="highlighter-rouge">linter</code> to add lint messages on the lines of the files. Since <code class="highlighter-rouge">linter</code> supports HTML, <a href="https://github.com/philschatz/atom-pull-requests">pull-requests</a> also converts the comment into HTML (complete with emojis) and adds a link to get back to the Pull Request on GitHub so you can continue discussing.</p>
<p>Hope that helps you!</p>
The Death of Openstax?2015-06-09T00:00:00+00:00http://philschatz.com/2015/06/09/death-of-openstax<p><a href="https://openstaxcollege.org">OpenStax</a> isn’t going anywhere; we have a ton of high-quality content and are revolutionizing textbook publishing for the benefit of students. But here are my thoughts on how it could be made a bit more open (and cheaper to boot!).</p>
<h1 id="problem">Problem</h1>
<p>As background, openstax books contain a few pieces:</p>
<ol>
<li>an editor for creating a part of a book (Aloha) and organizing parts of a book into a Table of Contents</li>
<li>rules for attribution (who authored the book)</li>
<li>a way to convert the book into various formats (ePUB and PDF)</li>
<li>a way to read the book online for free</li>
<li>a way to mix-and-match and create a new book</li>
<li>Ideally, openstax will have a way to allow others to suggest edits to a book</li>
</ol>
<h1 id="solution">Solution</h1>
<h2 id="non-tech-savvy">Non-tech-savvy</h2>
<ol>
<li>Use GitHub to store the book content (<a href="http://philschatz.com/books">book viewer</a>, <a href="https://github.com/search?p=1&q=user%3Aphilschatz+Openstax">source for openstax books</a>, and <a href="../../../2014/07/07/tiny-book-reader">blog post</a>)</li>
<li>Use a <a href="https://github.com/oerpub/github-bookeditor">browser editor</a> to edit the book</li>
<li>Use a little server to automatically create PDFs and ePubs (<a href="https://travis-ci.org">travis-ci</a> or <a href="https://github.com/philschatz/pdf-ci">philschatz/pdf-ci</a>)</li>
<li>Support attribution automatically (<a href="https://github.com/Connexions/webview/graphs/contributors">example</a>)</li>
<li>Support “Suggested Edits” to content automatically via <a href="https://help.github.com/articles/using-pull-requests/">GitHub’s “Pull Request”</a> (<a href="https://github.com/philschatz/anatomy-book/commit/bd695b8c50bbdfccc4d892e521b7f8b48d1b55ba?short_path=b4f3573#diff-b4f3573b2f24d5af026c33acf52ff716">example</a>)</li>
<li>Support “Derived Copies” of content automatically via <a href="https://help.github.com/articles/using-pull-requests/">GitHub’s “Fork”</a></li>
<li>Development of code and content using GitHub Issues via <a href="https://github.com/philschatz/gh-board">philschatz/gh-board</a></li>
</ol>
<h2 id="tech-savvy">Tech-savvy</h2>
<ol>
<li>All book content is stored directly in GitHub (see <a href="https://github.com/search?p=1&q=user%3Aphilschatz+Openstax">sources</a> for examples)</li>
<li>Replace the editor with <a href="https://github.com/oerpub/github-bookeditor">oerpub/github-book-editor</a> which saves <strong>directly</strong> to GitHub</li>
<li>Replace the web view with <a href="https://pages.github.com">Autogenerated Sites using GitHub</a> (see <a href="http://philschatz.com/books">philschatz.com/books</a> and the <a href="https://github.com/search?p=1&q=user%3Aphilschatz+Openstax">various book repositories</a> )</li>
<li>Replace PDF (and ePub) generation with <a href="https://travis-ci.org">travis-ci</a> or <a href="https://github.com/philschatz/pdf-ci">philschatz/pdf-ci</a> (Every time GitHub updates, generate a new PDF)</li>
<li>Support Derived copies using <a href="https://help.github.com/articles/using-pull-requests/">GitHub’s “Fork”</a></li>
<li>Support “Suggested Edits” (which openstax used to have) via <a href="https://help.github.com/articles/using-pull-requests/">GitHub’s Pull Requests</a></li>
<li>Support a diff of “Suggested Edits” via GitHub’s Markdown diff view (see <a href="http://philschatz.com/2014/07/07/tiny-book-reader/">blog post on books in GitHub</a>)</li>
<li>Support software (and content) development using something on top of GitHub Issues like huboard or ideally <a href="https://github.com/philschatz/gh-board">philschatz/gh-board</a> (no server/subscription required)</li>
</ol>
<h1 id="free-stuff">Free Stuff!</h1>
<ol>
<li>No server costs! (except PDF generation which can be minimized)</li>
<li>autogenerated PDFs for every edition (see GitHub Tags and Releases)</li>
<li>easy contributions from the entire world
<ul>
<li><a href="https://travis-ci.org">travis-ci</a> can make sure the Markdown is well-formed</li>
</ul>
</li>
<li>human-readable changes</li>
<li>Issue tracking for content in each book</li>
<li>Revisions for each book</li>
<li>More reusability/remixability than you could imagine</li>
</ol>
<h1 id="the-kink">The Kink</h1>
<p>There is a “kink” in this process which I’d be happy to elaborate on:</p>
<ol>
<li>Each book needs to be a separate repository (cannot “easily” combine multiple books into 1)</li>
<li>Need to learn MarkDown (specifically kramdown) unless the editor is “smart enough”</li>
</ol>
<p>That’s how I would “kill” OpenStax with minimal effort and stay true to the openness that has helped <a href="https://openstaxcollege.org">OpenStax</a> thrive.</p>
<p>For more projects check out <a href="https://github.com/philschatz?tab=repositories">my repositories</a></p>
Openstax Textbooks Reader on GitHub using Kramdown2014-07-07T00:00:00+00:00http://philschatz.com/2014/07/07/tiny-book-reader<p>Over the weekend I converted all of our popular textbooks from our new HTML format to <a href="http://kramdown.gettalong.org">kramdown</a> (a Markdown variant that allows classes) and <a href="http://philschatz.com/books/">tossed it on GitHub</a>.</p>
<p>Since <a href="http://pages.github.com">gh-pages</a> automatically converts kramdown files to HTML using <a href="http://jekyllrb.com">jekyll</a>, I also created a <a href="https://github.com/philschatz/book-viewer">tiny book reader</a> and linked to it in the book.
Click the screenshot to <a href="http://philschatz.com/books/"><em>read all the books!</em></a></p>
<p><a href="http://philschatz.com/books/"><img src="https://cloud.githubusercontent.com/assets/253202/3496567/d862235a-05e3-11e4-876e-80bdcf516938.png" alt="image" /></a></p>
<h1 id="githubcom-bonus-features">GitHub.com Bonus Features</h1>
<p>By using a Markdown variant instead of HTML, there are several things GitHub provides for free.</p>
<ul>
<li>repo pages will render instead of just showing the markup</li>
<li>links between pages <em>just work</em></li>
<li>diffs and Pull Requests render and add changebars/colors to make the changes clearer</li>
</ul>
<h2 id="clickable-table-of-contents">Clickable Table of Contents</h2>
<p><a href="https://github.com/philschatz/anatomy-book/blob/gh-pages/SUMMARY.md"><img src="https://cloud.githubusercontent.com/assets/253202/3496418/69b29f3a-05e2-11e4-8b72-de53ad718207.png" alt="image" /></a></p>
<p><strong>Note:</strong> <code class="highlighter-rouge">{: ...}</code> is kramdown’s way of adding classes</p>
<p><a href="https://github.com/philschatz/anatomy-book/blob/gh-pages/SUMMARY.md">The markup</a>:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>---
layout: page
---
1. {: .preface} [Preface](contents/m46844.md)
2. {: .part} Unit 1: Levels of Organization
1. {: .chapter} [An Introduction to the...](contents/m45981.md)
1. [Overview of Anatomy and Physiology](contents/m45983.md)
2. [Structural Organization of the...](contents/m45985.md)
3. [Functions of Human Life](contents/m45986.md)
2. {: .chapter} [The Chemical Level of...](contents/m45996.md)
1. [Elements and Atoms: The Building...](contents/m45998.md)
2. [Chemical Bonds](contents/m46000.md)
3. [Chemical Reactions](contents/m46004.md)
</code></pre></div></div>
<h2 id="rendering-in-normal-githubcom">Rendering in normal GitHub.com</h2>
<p>The markdown files render on GitHub.com repo pages (<a href="https://github.com/philschatz/anatomy-book/blob/gh-pages/contents/m46377.md">see example page on GitHub</a>)</p>
<p><a href="https://github.com/philschatz/anatomy-book/blob/gh-pages/contents/m46377.md"><img src="https://cloud.githubusercontent.com/assets/253202/3496428/7f327c7c-05e2-11e4-9c90-6a4dde496886.png" alt="image" /></a></p>
<p>and the <a href="https://raw.githubusercontent.com/philschatz/anatomy-book/gh-pages/contents/m46377.md">raw kramdown</a>:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>---
title: "Anatomy of Selected Synovial Joints"
layout: page
---
<div data-type="abstract" markdown="1">
By the end of this section, you will be able to:
* Describe the bones that articulate together to form selected...
* Discuss the movements available at each joint
* Describe the structures that support and prevent excess movements
</div>
# Articulations of the Vertebral Column
In addition to being held together by the intervertebral discs,
vertebrae also articulate with each other at synovial joints formed
between the superior and inferior articular processes called
**zygapophysial joints**{: data-type="term" :} (facet joints) (see
[link text](m46383.md#fig-ch09_01_02)). These are
plane joints that provide for only limited motions between the
vertebrae. The orientation of the articular processes at these joint
varies in different regions of the vertebral column and serves to
determine the types of motions available in each vertebral region.
The cervical and lumbar regions have the greatest ranges of motions.
{: data-title="Figure Title" .half-page :}
</code></pre></div></div>
<h3 id="rendered-changes">Rendered Changes</h3>
<p>GitHub will also render the changes of kramdown files instead of showing a diff (<a href="https://github.com/philschatz/anatomy-book/commit/bd695b8c50bbdfccc4d892e521b7f8b48d1b55ba?short_path=b4f3573#diff-b4f3573b2f24d5af026c33acf52ff716">see example of deleting a chapter</a>)</p>
<p><a href="https://github.com/philschatz/anatomy-book/commit/bd695b8c50bbdfccc4d892e521b7f8b48d1b55ba?short_path=b4f3573#diff-b4f3573b2f24d5af026c33acf52ff716"><img src="https://cloud.githubusercontent.com/assets/253202/3496449/b512862a-05e2-11e4-8264-19ed006cfa77.png" alt="image" /></a></p>
<h1 id="features">Features</h1>
<ul>
<li>unobtrusive design</li>
<li>big next/prev buttons</li>
<li>reading progress bar</li>
<li>keyboard left/right buttons work</li>
<li>Table of Contents shows pages you have read</li>
<li>spinner shows when page is loading</li>
<li>just 300 lines of code</li>
<li>works on any book hosted on <em>cnx.org</em> too! <a href="http://mountainbunker.org/~schatz/ed/simple-webview/">Examples of all the Openstax College books</a></li>
<li>search button reuses the Table of Contents</li>
<li>does not require building the files for <code class="highlighter-rouge">gh-pages</code> (unlike <a href="http://gitbook.io">GitBook</a>)</li>
</ul>
<h1 id="book-reader-links">Book Reader Links</h1>
<ul>
<li><a href="https://github.com/philschatz/book-viewer">reader code</a> (it’s in <code class="highlighter-rouge">./dist</code> because I’m lazy)</li>
<li><a href="http://philschatz.com/books/">reader for Openstax books hosted in GitHub</a></li>
<li><a href="http://mountainbunker.org/~schatz/ed/simple-webview/">Openstax College books using the <em>same</em> reader code</a> (loads archive.cnx.org)</li>
</ul>
<h1 id="notes">Notes</h1>
<ul>
<li>the reader works even if files are stored in places other than <code class="highlighter-rouge">/contents</code> (thanks to URI.js)</li>
<li>kramdown does not support <code class="highlighter-rouge"><figure></code> (unless you HTML escape it) so I added attributes to the <code class="highlighter-rouge"><img></code> tag (for the title) and JS converts them to a <code class="highlighter-rouge"><figure></code></li>
<li>internal links do not work on the OSC cnx.org books because I ran out of time and used <code class="highlighter-rouge"><base href="archive.cnx.org"></code></li>
<li>Jekyll requires all Markdown that is converted to HTML <em>must</em> have a YAML header</li>
<li>I removed autogenerated ids on paragraphs and lists so the kramdown is cleaner (<code class="highlighter-rouge">id="fs-id*"</code>)</li>
</ul>
Creating a "DSL" for GitHub's API (rewriting philschatz/octokit.js)2014-05-25T00:00:00+00:00http://philschatz.com/2014/05/25/octokat<p>I originally wrote <a href="/tags#octokit">philschatz/octokit.js</a> to interact with GitHub’s API using Promises. I started by forking an existing API <a href="https://github.com/michael/github">michael/github</a> and rewrote it using CoffeeScript and jQuery Promises. Then, thanks to contributions, they removed the dependence on Underscore and I removed the dependence on jQuery (and added support for <em>several</em> promise implementations).</p>
<p>But the code was getting too large as people incrementally added features so I thought it was time to rewrite with a focus on implementing as much of GitHub’s API concisely and consistently (with the hope of getting it adopted by GitHub officially).</p>
<p>Given my interest in Programming Languages, I decided to make a <a href="https://en.wikipedia.org/wiki/Domain-specific_language">“Domain Specific Language”</a> for GitHub (it’s in quotes because technically it’s still just Javascript but read on.</p>
<p>The results are at <strong><a href="https://github.com/philschatz/octokat.js">philschatz/octokat.js</a></strong>.</p>
<h1 id="necessary-features">Necessary Features</h1>
<p>This new library needed to:</p>
<ol>
<li>support ~100% of the GitHub API from the start</li>
<li>have as little code as possible to maintain</li>
<li>work in NodeJS and the browser</li>
<li>have multiple source files</li>
<li>have tons of unit tests</li>
<li>support NodeJS callbacks and Promises</li>
</ol>
<p>Let me quickly go through each of these and the challenges each presented.</p>
<h1 id="support-100-of-the-github-api">Support ~100% of the GitHub API</h1>
<p>This library abstracts all the authentication, status codes, caching, headers, HyperMedia (URL templates), and pagination returned from GitHub.</p>
<h2 id="abstracting-requests-and-responses">Abstracting Requests and Responses</h2>
<p>Understanding how to form a request and how to parse the response is the bulk of the API; the logic is in <code class="highlighter-rouge">src/request.coffee</code> and <code class="highlighter-rouge">src/replacer.coffee</code>.</p>
<p>The other part is a chaining function that constructs a valid request.</p>
<h2 id="constructing-valid-requests">Constructing Valid Requests</h2>
<p>In the original <a href="/tags#octokit">philschatz/octokit.js</a> there was a ton of copy/pasta dedicated to just constructing valid URLs.</p>
<p>Instead, this library has a regular expression that validates all URLs before calling GitHub and constructs objects dynamically through chaining (see <code class="highlighter-rouge">src/grammar.coffee</code>).</p>
<p>By reading the documentation at https://developer.github.com/v3/ a developer implicitly constructs a URL and then issues a request by calling one of the verb methods.</p>
<p>For example, to <a href="https://developer.github.com/v3/issues/comments/#list-comments-on-an-issue">list all comments on an issue</a> convert the documentation URL to the following:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>GET /repos/:owner/:repo/issues/:number/comments
.repos(owner, repo).issues(number).comments.fetch()
</code></pre></div></div>
<p>Here are 3 ways to list all comments on an issue:</p>
<div class="language-coffee highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">octo</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Octokat</span><span class="p">()</span>
<span class="nx">REPO</span> <span class="o">=</span> <span class="nx">octo</span><span class="p">.</span><span class="na">repos</span><span class="p">(</span><span class="s">'octokit/octokit.rb'</span><span class="p">)</span> <span class="c1"># for brevity</span>
<span class="c1"># Option 1: using callbacks</span>
<span class="nx">REPO</span><span class="p">.</span><span class="na">issues</span><span class="p">(</span><span class="mi">1</span><span class="p">).</span><span class="na">comments</span><span class="p">.</span><span class="na">fetch</span> <span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">comments</span><span class="p">)</span> <span class="o">-></span>
<span class="nx">console</span><span class="p">.</span><span class="na">error</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="k">if</span> <span class="nx">err</span>
<span class="nx">console</span><span class="p">.</span><span class="na">log</span><span class="p">(</span><span class="nx">comments</span><span class="p">)</span> <span class="nx">unless</span> <span class="nx">err</span>
<span class="c1"># Option 2: using Promises</span>
<span class="nx">REPO</span><span class="p">.</span><span class="na">issues</span><span class="p">(</span><span class="mi">1</span><span class="p">).</span><span class="na">comments</span><span class="p">.</span><span class="na">fetch</span><span class="p">()</span>
<span class="p">.</span><span class="na">then</span> <span class="p">(</span><span class="nx">comments</span><span class="p">)</span> <span class="o">-></span>
<span class="nx">console</span><span class="p">.</span><span class="na">log</span><span class="p">(</span><span class="nx">comments</span><span class="p">)</span>
<span class="c1"># Option 3: using methods on the fetched Repository object</span>
<span class="nx">REPO</span><span class="p">.</span><span class="na">fetch</span><span class="p">()</span>
<span class="p">.</span><span class="na">then</span> <span class="p">(</span><span class="nx">repo</span><span class="p">)</span> <span class="o">-></span>
<span class="c1"># `repo` contains returned JSON and additional methods</span>
<span class="nx">repo</span><span class="p">.</span><span class="na">issues</span><span class="p">(</span><span class="mi">1</span><span class="p">).</span><span class="na">fetch</span><span class="p">()</span>
<span class="p">.</span><span class="na">then</span> <span class="p">(</span><span class="nx">issue</span><span class="p">)</span> <span class="o">-></span>
<span class="c1"># `repo` contains returned JSON and additional methods</span>
<span class="nx">issue</span><span class="p">.</span><span class="na">comments</span><span class="p">.</span><span class="na">fetch</span><span class="p">()</span>
<span class="p">.</span><span class="na">then</span> <span class="p">(</span><span class="nx">comments</span><span class="p">)</span> <span class="o">-></span>
<span class="nx">console</span><span class="p">.</span><span class="na">log</span><span class="p">(</span><span class="nx">comments</span><span class="p">)</span>
</code></pre></div></div>
<p>There are several <em>verb methods</em> to choose from:</p>
<ul>
<li><code class="highlighter-rouge">.fetch()</code> sends a <code class="highlighter-rouge">GET</code> and yields an Object</li>
<li><code class="highlighter-rouge">.read()</code> sends a <code class="highlighter-rouge">GET</code> and yields raw data</li>
<li><code class="highlighter-rouge">.create(...)</code> sends a <code class="highlighter-rouge">POST</code> and yields an Object</li>
<li><code class="highlighter-rouge">.update(...)</code> sends a <code class="highlighter-rouge">PATCH</code> and yields an Object</li>
<li><code class="highlighter-rouge">.remove()</code> sends a <code class="highlighter-rouge">DELETE</code> and yields a boolean</li>
<li><code class="highlighter-rouge">.add(...)</code> sends a <code class="highlighter-rouge">PUT</code> and yields a boolean</li>
<li><code class="highlighter-rouge">.contains(...)</code> sends a <code class="highlighter-rouge">GET</code> and yields a boolean</li>
</ul>
<h2 id="pagination-and-html-templates">Pagination and HTML templates</h2>
<p>GitHub returns headers for lists of results. These are automatically converted to <code class="highlighter-rouge">nextPage</code>, <code class="highlighter-rouge">previousPage</code>, <code class="highlighter-rouge">firstPage</code>, and <code class="highlighter-rouge">lastPage</code> methods on the resulting JSON.</p>
<p>Paging through all the issues on a repository looks like this:</p>
<div class="language-coffee highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Create an issues object (but does not fetch anything yet)</span>
<span class="nx">ISSUES</span> <span class="o">=</span> <span class="nx">octo</span><span class="p">.</span><span class="na">repos</span><span class="p">(</span><span class="s">'octokit/octokit.rb'</span><span class="p">).</span><span class="na">issues</span>
<span class="c1"># Option 1: with callbacks</span>
<span class="nx">ISSUES</span><span class="p">.</span><span class="na">fetch</span> <span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">issues</span><span class="p">)</span> <span class="o">-></span>
<span class="nx">console</span><span class="p">.</span><span class="na">log</span><span class="p">(</span><span class="nx">issues</span><span class="p">)</span>
<span class="nx">issues</span><span class="p">.</span><span class="na">nextPage</span> <span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">moreIssues</span><span class="p">)</span> <span class="o">-></span>
<span class="nx">console</span><span class="p">.</span><span class="na">log</span><span class="p">(</span><span class="nx">moreIssues</span><span class="p">)</span>
<span class="c1"># Option 2: with Promises</span>
<span class="nx">ISSUES</span><span class="p">.</span><span class="na">fetch</span><span class="p">()</span>
<span class="p">.</span><span class="na">then</span> <span class="p">(</span><span class="nx">issues</span><span class="p">)</span> <span class="o">-></span>
<span class="nx">console</span><span class="p">.</span><span class="na">log</span><span class="p">(</span><span class="nx">issues</span><span class="p">)</span>
<span class="nx">issues</span><span class="p">.</span><span class="na">nextPage</span><span class="p">()</span>
<span class="p">.</span><span class="na">then</span> <span class="p">(</span><span class="nx">moreIssues</span><span class="p">)</span> <span class="o">-></span>
<span class="nx">console</span><span class="p">.</span><span class="na">log</span><span class="p">(</span><span class="nx">moreIssues</span><span class="p">)</span>
</code></pre></div></div>
<h1 id="maintain-a-minimal-amount-of-code">Maintain a Minimal Amount of Code</h1>
<p>With this API you are prevented from constructing an invalid URL because every request is validated against the <code class="highlighter-rouge">URL_VALIDATOR</code> regular expression before sending the request to GitHub.</p>
<p>Instead of writing largely copy/pasta code that constructs classes I opted for a regular expression that represents the entire GitHub API.</p>
<p>This can be found in <code class="highlighter-rouge">src/grammar.coffee</code>.</p>
<h1 id="works-in-nodejs-and-the-browser">Works in NodeJS and the Browser</h1>
<p>Getting this to work was a bit challenging.</p>
<p>NodeJS and AMD have slightly different syntaxes; enough to require adding in some boilerplate code to convert between the two.</p>
<p>Each of the source files contains 2 lines of boilerplate on the top of the file and the bottom.</p>
<h2 id="attempt-1">Attempt 1</h2>
<p>Originally, I had the entire library in a single file.</p>
<p>I was able to have very little boilerplate. Just something like the following:</p>
<div class="language-coffee highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="vi">@</span><span class="na">define</span> <span class="o">?=</span> <span class="p">(</span><span class="nx">cb</span><span class="p">)</span> <span class="o">-></span> <span class="nx">cb</span><span class="p">((</span><span class="nx">dep</span><span class="p">)</span> <span class="o">-></span> <span class="nx">require</span><span class="p">(</span><span class="nx">dep</span><span class="p">.</span><span class="na">replace</span><span class="p">(</span><span class="s">'cs!'</span><span class="p">,</span> <span class="s">''</span><span class="p">)))</span>
<span class="nx">define</span> <span class="p">(</span><span class="nx">require</span><span class="p">)</span> <span class="o">-></span>
<span class="nx">foo</span> <span class="o">=</span> <span class="nx">require</span> <span class="s">'foo'</span>
<span class="p">...</span>
<span class="nx">module</span><span class="o">?</span><span class="p">.</span><span class="na">exports</span> <span class="o">=</span> <span class="nx">Octokat</span> <span class="c1"># For NodeJS</span>
<span class="nb">window</span><span class="o">?</span><span class="p">.</span><span class="na">Octokat</span> <span class="o">=</span> <span class="nx">Octokat</span> <span class="c1"># For browsers</span>
<span class="k">return</span> <span class="nx">Octokat</span> <span class="c1"># For browsers using AMD</span>
</code></pre></div></div>
<p>This worked well but resulted in a single large file.</p>
<h2 id="attempt-2">Attempt 2</h2>
<p>I then split up the library into multiple files and got it working but ran into a problem when trying to build everything into one file.</p>
<p>I spent about a week trying to get <a href="https://github.com/jrburke/r.js">jrburke/r.js</a> and <a href="https://github.com/jrburke/almond">jrburke/almond</a> to build a single file but could not get NodeJS, AMD, and the single file to all work at the same time.</p>
<h2 id="attempt-3">Attempt 3</h2>
<p>Finally, I opted for compiling the coffee files and concatenating them together with a custom <code class="highlighter-rouge">define</code> method similar to what other libraries do (see the <code class="highlighter-rouge">require()</code> method in <a href="https://github.com/less/less.js">less/less.js</a>.</p>
<p>With this option, the tests run:</p>
<ul>
<li>on the source coffee files in NodeJS</li>
<li>on the source coffee files in the browser</li>
<li>on the built <code class="highlighter-rouge">dist/octokat.js</code> file in the browser</li>
</ul>
<p>Finally, multiple source files <strong>and</strong> tests running in all 3 environments!</p>
<h1 id="fixtures-and-recording-http-requests">Fixtures and Recording HTTP Requests</h1>
<p>The library currently runs about 400 tests. There are about 120 unique tests, 80 are alternates using callbacks instead of promises, and 200 are the same tests but running in the browser.</p>
<p>In order to not pummel GitHub with duplicate requests I use <a href="https://github.com/linkedin/sepia">linkedin/sepia</a> to generate “cassettes” to replay the HTTP requests.</p>
<p>Unfortunately, sepia only runs on NodeJS so I wrote <a href="https://github.com/philschatz/sepia.js">philschatz/sepia.js</a> which plays back (and can record) <code class="highlighter-rouge">linkedin/sepia</code> tests.</p>
<h1 id="support-nodejs-callbacks-and-promises">Support NodeJS Callbacks and Promises</h1>
<p>In order to support both callbacks and promises, the asynchronous methods (called <em>verb methods</em>) all support a function as the final argument and return a Promise.</p>
<p>This way, you can always end each line with a callback or with a <code class="highlighter-rouge">.then()</code> and the code just works.</p>
<p>The client only returns a Promise if one of the supported Promise libraries are detected (<code class="highlighter-rouge">jQuery</code>, <code class="highlighter-rouge">angular</code>, <code class="highlighter-rouge">Q</code>, or native Promises).</p>
<p>If you use one of those libraries but still prefer to use callbacks just make sure the callback is the last argument and the code will <em>just work</em><sup><small>TM</small></sup>.</p>
Removing jQuery, and adding Promises2014-04-14T00:00:00+00:00http://philschatz.com/2014/04/14/removing-jquery<p>This weekend I decided to remove jQuery from 2 of my libraries: <a href="/css-polyfills.js/">css-polyfills.js</a> and <a href="https://github.com/philschatz/octokit.js">octokit.js</a>.</p>
<p>It was motivated by requests from octokit users and by the horrible performance of <a href="/css-polyfills.js/">css-polyfills.js</a> on large textbooks (took ~1.5 hours).</p>
<h2 id="refactoring-css-polyfillsjs">Refactoring <a href="/css-polyfills.js/">css-polyfills.js</a></h2>
<p>I started with <a href="http://youmightnotneedjquery.com/">you-might-not-need-jquery</a> and made incremental progress through the codebase. Initially my hope was to decrease the time by 50% but the refactor was only providing marginal improvements, until I hit the following line of code:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$el.find('#' + id)
</code></pre></div></div>
<p>This uses <a href="http://sizzlejs.com">Sizzle</a> to find an element by id. As soon as I turned it into the following…</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>document.getElementById(id)
</code></pre></div></div>
<p><strong>BAM!</strong> the time dropped from ~1.5 hours down to 4.5 <em>minutes</em>.</p>
<p>The other annoying bit was that the DOM sometimes returns a live list, meaning if you remove an element, it is directly reflected in the <code class="highlighter-rouge">NodeList</code>.</p>
<p>To avoid the problem of iterating over live <code class="highlighter-rouge">NodeList</code> and removing some of them I wrapped it in <code class="highlighter-rouge">_.toArray()</code>.</p>
<h2 id="refactoring-octokitjs">Refactoring <a href="https://github.com/philschatz/octokit.js">octokit.js</a></h2>
<p>This one was a bit easier.</p>
<p><a href="https://github.com/philschatz/octokit.js">octokit.js</a> uses 2 features in jQuery: <code class="highlighter-rouge">jQuery.ajax()</code> and <code class="highlighter-rouge">jQuery.Deferred</code>.</p>
<p>ECMAScript 6 has native support for <code class="highlighter-rouge">Promise</code> (and the more fun generators) and it seems the <code class="highlighter-rouge">XMLHTTPRequest</code> object is not going away any time soon.</p>
<p>Fortunately, it does not do any DOM manipulation so refactoring this only required changing a few isolated spots in the code.</p>
<p>There were some benefits in making the change as well as drawbacks.</p>
<p>As a reward for being almost a decade ahead of its time, jQuery promises are almost, but not quote the same as ECMAScript Promises. They are constructed differently, have different names for functions, and have more features than the native Promises.</p>
<p>For example:</p>
<ul>
<li>waiting on all Promises to complete in jQuery requires calling <code class="highlighter-rouge">jQuery.when</code></li>
<li>jQuery promises have <code class="highlighter-rouge">.notify()</code> and <code class="highlighter-rouge">.progress()</code> methods</li>
<li>distinguish between waiting on a promise and extending one (<code class="highlighter-rouge">.done()</code> vs <code class="highlighter-rouge">.then()</code>)</li>
<li>allow multiple arguments to <code class="highlighter-rouge">.resolve()</code></li>
</ul>
<p>For octokit this would have been a nice way to provide paginated results as a second argument.</p>
Skeleton Auto-generation2014-04-07T00:00:00+00:00http://philschatz.com/2014/04/07/skeleton-generator<p>In <a href="/2014/03/16/slots-and-skeletons-intro/">Building CSS skeletons and slots</a> I showed how to style a piece of content based on where it occurs (the <code class="highlighter-rouge">@contexts...</code> parameters).</p>
<p>The code can be found at <strong><a href="https://github.com/philschatz/skeleton-generator.css">philschatz/skeleton-generator.css</a></strong>.</p>
<p>Here’s a discussion on the machinery needed to realize that.</p>
<h1 id="types-and-templates">Types and Templates</h1>
<p>First, we need a way to describe the selector for a particular type. Let’s use a subfigure (figure inside a figure) for this example.</p>
<p>A typical figure would have styling like:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>figure {
border: 1px solid blue;
figcaption {
color: green;
}
}
</code></pre></div></div>
<p>When split into slots and skeletons (let’s ignore <code class="highlighter-rouge">@kind</code> and <code class="highlighter-rouge">@part</code> for now) it would look like:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// The "skeleton":
figure {
#content>#figure>.style();
figcaption {
#content>#figure>.caption();
}
}
// The "slots":
#content {
#figure {
.style() { border: 1px solid blue; }
.caption() { color: green; }
}
}
</code></pre></div></div>
<p>Now, we have something to work with.
To make a <em>subfigure</em> have a <code class="highlighter-rouge">yellow</code> border, the CSS would look something like:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>figure {
figure { // Note: it's inside another figure
border: 1px solid yellow;
}
}
</code></pre></div></div>
<p>and the skeleton and slots would look like:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// The "skeleton" for a subfigure:
figure {
figure {
#content>#figure>.style(figure); // the arg denotes that this is
// _inside_ another `figure`
figcaption {
#content>#figure>.caption(figure);
}
}
}
// The "slots" for a subfigure:
#content {
#figure {
.style(figure) { border: 1px solid yellow; }
}
}
</code></pre></div></div>
<p>Now, we could write all possible permutations of figures, notes, tables, examples, etc in the skeleton file but that would be <em>tedious</em>. Instead, we can autogenerate them using the <code class="highlighter-rouge">...</code> list expander in LessCSS.</p>
<p>First, we need to break up the figure into a couple pieces:</p>
<ol>
<li>the selector that distinguishes a <code class="highlighter-rouge">figure</code> from a <code class="highlighter-rouge">.note</code> or a <code class="highlighter-rouge">.example</code></li>
<li>the template of the “structure” inside a figure (it has a <code class="highlighter-rouge">.style()</code> and <code class="highlighter-rouge">.caption()</code>)</li>
</ol>
<p>For each <code class="highlighter-rouge">@type</code> (figure, note, table) the selector can be defined as:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#skeleton {
.selector(figure) {
figure { .x-template-helper(figure); }
}
}
</code></pre></div></div>
<p>Assuming <code class="highlighter-rouge">.x-template-helper</code> does something (for now), this would allow us to create:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>figure {
figure {
... // figures all the way down!
}
}
</code></pre></div></div>
<p>… since the only thing this mixin defines is the selector <code class="highlighter-rouge">figure { ... }</code> (so it can be nested).</p>
<p>Next, we need to define what can be inside a figure (or subfigure). This is defined by the <code class="highlighter-rouge">#skeleton>.template(figure)</code> mixin. We can define it as:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#skeleton {
.template(figure) {
// All figures have a style and may have a caption.
#content>#figure>.style();
figcaption { #content>#figure>.caption(); }
}
}
</code></pre></div></div>
<p>This says a figure has a <code class="highlighter-rouge">.style()</code> and a style on the caption.</p>
<p>So far we have defined both what a figure is (the <code class="highlighter-rouge">.selector(@type)</code>) and what it can contain (the <code class="highlighter-rouge">.template(@type)</code>).</p>
<p>Now we need to combine these to generate the expected CSS:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>figure {
figure {
border: 1px solid yellow;
}
}
</code></pre></div></div>
<p>To do it, we need to build a bit more machinery first!</p>
<h1 id="list-evaluation-in-lesscss">List evaluation in LessCSS</h1>
<p>There are several <code class="highlighter-rouge">@types</code> we need to permute. In this example there are only figures, but for a book there will be several types (ie <code class="highlighter-rouge">figure</code>, <code class="highlighter-rouge">note</code>, <code class="highlighter-rouge">table</code>, <code class="highlighter-rouge">example</code>).</p>
<p>The generator needs to iterate over all these types so for now let’s put them in a variable.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>@all-types: figure, note, table, example;
</code></pre></div></div>
<p>LessCSS has a way way of going through and recursively evaluating a list:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.recursive() { } // base case
.recursive(@head; @tail...) {
content: @head;
.recursive(@tail...); // The `@tail` list is expanded to
// multiple arguments
}
</code></pre></div></div>
<p>Using this structure and the <code class="highlighter-rouge">.selector(@type)</code> and <code class="highlighter-rouge">.template(@type)</code> mixins we can create the following:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.build-children(@type; @rest...) {
.selector(@type);
// The recusion step is in `.x-template-helper`
}
// This mixin was "assumed" above but is defined here.
// It is used in the `.selector()` mixin.
.x-template-helper(@type) {
.template(@type);
// Recurse!
.build-children(@all-types...);
}
</code></pre></div></div>
<p>In order to prevent an infinite loop, we define a <code class="highlighter-rouge">@max-depth</code> and add a <code class="highlighter-rouge">@depth</code> param to the mixins:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.build-children(0; ...) { } // base case
.build-children(@depth; @type; @rest...) {
.selector(@type);
.build-children(@depth; @rest...);
}
.x-template-helper(@type) {
.template(@type);
.build-children((@depth - 1); @all-types...);
}
</code></pre></div></div>
<p>Now, when <code class="highlighter-rouge"><span class="p">.</span><span class="n">build-children</span><span class="p">(</span><span class="mi">2</span><span class="p">;</span><span class="w"> </span><span class="kd">@base</span><span class="n">-types</span><span class="p">...)</span></code> is called, we get the following:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>figure {
border: 1px solid blue; // Recall figures have a blue border
figcaption {
color: green; // Recall figure captions are green
}
figure {
border: 1px solid yellow; // Recall subfigures have a
// yellow border
}
}
</code></pre></div></div>
<p>Which is the desired result.</p>
<h2 id="custom-classes">Custom classes</h2>
<p>So far, we’ve ignored the <code class="highlighter-rouge">@kind</code> and <code class="highlighter-rouge">@part</code> parameters to mixins.
In this section we can add them back in.</p>
<p>The <code class="highlighter-rouge">@part</code> parameter describes which part of the book the content is in. Some examples are <code class="highlighter-rouge">preface</code>, <code class="highlighter-rouge">chapter</code>, or <code class="highlighter-rouge">appendix</code>. The <code class="highlighter-rouge">@kind</code> parameter describes the custom class on a piece of content. These are specific to a book (<code class="highlighter-rouge">.how-to</code> for physics or <code class="highlighter-rouge">.timeline</code> for a history book).</p>
<p>To handle the <code class="highlighter-rouge">@part</code> (<code class="highlighter-rouge">preface</code>, <code class="highlighter-rouge">chapter</code>, <code class="highlighter-rouge">appendix</code>, etc) we merely need to add a <code class="highlighter-rouge">@part</code> parameter to <code class="highlighter-rouge">.build-children()</code>.</p>
<p>To handle the <code class="highlighter-rouge">@kind</code> parameter we need to let the skeleton-generation code know what are all the possible classes (<code class="highlighter-rouge">@kind</code>) for a given type (<code class="highlighter-rouge">figure</code>, <code class="highlighter-rouge">example</code>, <code class="highlighter-rouge">section</code>, etc).</p>
<p>To do this, we add a “function” mixin to the <code class="highlighter-rouge">#content</code> namespace which will “return” a list of classes for each type.
Because these are specific to each book they are defined as a <code class="highlighter-rouge">#content.kinds(@type)</code> mixin that “returns” by setting a <code class="highlighter-rouge">@return</code> variable.</p>
<p>For example, we could have several classes on a figure: <code class="highlighter-rouge">.full-width</code>and <code class="highlighter-rouge">.half-width</code>.</p>
<p>The expected CSS would be:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>figure.full-width { width: 100%; }
figure.half-width { width: 50%; }
</code></pre></div></div>
<p>To define these for the skeleton-generator and the add styling in the slots, it would look like:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#content {
.kinds(figure) {
// all the possible classes on a `figure` for this book:
@return: 'full-width', 'half-width';
}
#figure {
.style('full-width') { width: 100%; }
.style('half-width') { width: 50%; }
}
}
</code></pre></div></div>
<p>Then, we can add a <code class="highlighter-rouge">@kind</code> parameter to the definition of <code class="highlighter-rouge">.selector(@type)</code> and change the corresponding definitions of <code class="highlighter-rouge">.build-children()</code> and <code class="highlighter-rouge">.x-template-helper()</code>.</p>
<h2 id="custom-classes-in-contexts">Custom classes in <code class="highlighter-rouge">@contexts...</code></h2>
<p>So far, the <code class="highlighter-rouge">@contexts...</code> arguments only contain a list of types.
For example, styling any <code class="highlighter-rouge">exercise</code> inside an <code class="highlighter-rouge">example</code> inside a <code class="highlighter-rouge">chapter</code> is done like this:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#content {
#exercise {
.style(any; chapter; example) { ... }
}
}
</code></pre></div></div>
<p>To style an <code class="highlighter-rouge">exercise</code> inside a <code class="highlighter-rouge">.how-to</code> example we need a bit more information; namely the class <code class="highlighter-rouge">.how-to</code>. To do this, we can use the <code class="highlighter-rouge">#content>.kinds(@type)</code> mixin defined above when permuting.</p>
<p>It looks something like:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.x-helper-base-types(@depth; @contexts; @type; @rest...) {
.selector(@type; @depth; any; @contexts...);
// Loop for any custom `@kind`s
#content>.kinds(@type);
@kinds: @return;
.x-helper-custom-types(@depth; @contexts; @type; @kinds...);
// recurse
.x-helper-base-types(@depth; @contexts; @rest...);
}
</code></pre></div></div>
<p>Now, we can style a figure based on:</p>
<ul>
<li>the class</li>
<li>the context <code class="highlighter-rouge">@type</code>s</li>
<li>the context <code class="highlighter-rouge">@kind</code>s (classes)</li>
</ul>
<h2 id="optimizing">Optimizing</h2>
<p>Checking all possible combinations of <code class="highlighter-rouge">@type</code>s is computation and memory intensive.
Fortunately, we can short-cut several of these.
For example, a <code class="highlighter-rouge">figure</code> can never contain a <code class="highlighter-rouge">section</code> or an <code class="highlighter-rouge">example</code></p>
<p>Instead of using a global <code class="highlighter-rouge">@all-types</code> for recursion, we can recurse based on valid children by defining a mixin that “returns” the valid children.</p>
<p>This would look like:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#skeleton {
#content {
.children(figure) { @return: figure, table; } //style subfigures
//and tables
//inside a figure
// Defines the "root" children for a piece of content
.children(none) { @return: table, figure, exercise, example; }
}
}
</code></pre></div></div>
<p>This mixin is used in <code class="highlighter-rouge">.x-helper-base-types</code> to restrict the list of children that are permuted.</p>
<p>Now, we can <em>quickly</em> style a <code class="highlighter-rouge">figure</code> based on:</p>
<ul>
<li>the class</li>
<li>the context <code class="highlighter-rouge">@type</code>s</li>
<li>the context <code class="highlighter-rouge">@kind</code>s (classes)</li>
</ul>
Building CSS skeletons and slots2014-03-16T00:00:00+00:00http://philschatz.com/2014/03/16/slots-and-skeletons-intro<h2 id="simple-slots-and-skeletons">Simple Slots and Skeletons</h2>
<p>We are transitioning from 1 output format for CSS (Docbook HTML) to multiple HTML formats (PDF, EPUB, web).</p>
<p>To reuse the same CSS we split the selectors and rules into mixins that are namespaced based on their logical part in the book. Instead of a <code class="highlighter-rouge">.note { color: blue; }</code> we split it into 2 parts:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// The skeleton:
.note { #content>#note>.style(); }
// and the slot:
#content {
#note {
.style() { color: blue; }
}
}
</code></pre></div></div>
<p>This allows us to reuse the logical styling when the HTML format changes.</p>
<p>Now, styling a book merely involves <em>filling in</em> the right slots.</p>
<h2 id="slot-parameters">Slot parameters</h2>
<h3 id="custom-classes">Custom classes</h3>
<p>Often we have custom “features” in a book. These are often written and as notes or sections with a custom class name.</p>
<p>To style these, every slot has a <code class="highlighter-rouge">@kind</code> parameter as the 1st argument to the mixin.
Here is an example of a “How To” feature:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#content {
#note {
.style('how-to') { color: orange; }
}
}
</code></pre></div></div>
<h3 id="parts-of-a-book">Parts of a book</h3>
<p>But sometimes it is necessary to style the feature differently based on which part of the book it is in. Most commonly this is used for numbering. For example, a Figure in a chapter would be labeled <code class="highlighter-rouge">Figure 4.3</code> but in an appendix it would be labeled <code class="highlighter-rouge">Figure A3</code>.</p>
<p>To support this, the 2nd argument is the <code class="highlighter-rouge">@part</code>. For <em>most</em> slot definitions this will just be <code class="highlighter-rouge">any</code>.</p>
<h2 id="contexts">Contexts</h2>
<p>When styling a book, it is often important to know what an element is in. For example, a worked-out example is actually an <code class="highlighter-rouge">exercise</code> inside an <code class="highlighter-rouge">example</code>. It should not be numbered (since it is not a homework problem) and might be rendered with text other than <code class="highlighter-rouge">Problem</code> and <code class="highlighter-rouge">Solution</code>.</p>
<p>To handle this case, the final arguments to a mixin are <code class="highlighter-rouge">@contexts...</code>.</p>
<p>Here is how to style an exercise inside an example:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#content {
#exercise {
.style(any; @part; example) { color: green; }
}
}
</code></pre></div></div>
<p>This makes <code class="highlighter-rouge">any</code> exercise (regardless of class) in any <code class="highlighter-rouge">@part</code> that occurs inside an <code class="highlighter-rouge">example</code> green.</p>
<p>If you wanted to make any exercise inside a <code class="highlighter-rouge">how-to</code> example yellow you would write:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#content {
#exercise {
.style(any; @part; example; 'how-to') { color: yellow; }
}
}
</code></pre></div></div>
<p>Similarly, any exercise in an example in the <code class="highlighter-rouge">review-questions</code> section at the end of a chapter would be something like:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#content {
#exercise {
.style(any; chapter; end-of; section; 'review-questions'; example) { color: aqua; }
}
}
</code></pre></div></div>
<p>That’s about it for the intro to slots and skeletons. I should have another post on the namespace organization of a book and a post on how the machinery needed to get the <code class="highlighter-rouge">@contexts...</code> parameter to work.</p>
Pattern matching with LESS (CSS)2014-03-08T00:00:00+00:00http://philschatz.com/2014/03/08/slots-and-skeletons<h1 id="background">Background</h1>
<p>Pattern-matching mixins is <strong>the</strong> distinguishing characteristic that separates Less from other CSS preprocessors like Stylus and Sass.</p>
<blockquote>
<p>Let the compiler do what it’s good at: match templates and error early (when one is missing).</p>
</blockquote>
<p>LESS is a beautiful language (like CSS) <em>precisely</em> because it is <strong>not</strong> Turing-complete.
Instead, it explores just how far you can take a language with those restrictions.</p>
<p>For example, you can provide conditionals using the pattern-matching <code class="highlighter-rouge">when</code> keyword instead of relying explicit control flow like <code class="highlighter-rouge">if then else</code>. You can match arguments to mixins either by value or with <code class="highlighter-rouge">...</code> (like Ruby, CoffeeScript, and recent versions of Java).</p>
<p>This frees the author up to put conditional styles elsewhere in a file (much like XSLT templates). Similarly, mixins whose arguments match are always applied. This can initially be an annoying feature but for styling textbooks this is central in our design.</p>
<h1 id="case-study-making-textbooks">Case Study: Making Textbooks</h1>
<p>In textbooks we need to style elements differently depending on where they are (context is crucial).</p>
<p>A subfigure (<code class="highlighter-rouge">figure > figure</code>) should render differently than a <code class="highlighter-rouge">figure</code>.</p>
<p>A table should be numbered differently when it is in a chapter (ie <code class="highlighter-rouge">Table 4.3</code>) than when it is in an appendix (<code class="highlighter-rouge">Table A3</code>).</p>
<p>Or take problems and solutions: a problem and solution at the end of a chapter should be “treated” differently than an example with a worked out problem and solution. The former should be numbered (and solution should be moved to the back of a textbook) while the latter should be included in the example (since it is a worked-out example).</p>
<p>Or take a definition of a term. If an equation is used then it should not be numbered; referring to an equation inside a definition does not make a whole lot of sense.</p>
<p>In each of these cases it is the <strong>context</strong> that is important when deciding how to style an element.</p>
<p>Additionally, the HTML elements differ for different outputs; online the CSS selectors are different than for a single-page book or a multi-page EUPB.</p>
<h1 id="solution-slots-and-skeletons">Solution: Slots and Skeletons</h1>
<p>To handle multiple HTML formats (EPUB, online, PDF) we came up with the idea of separating the CSS selectors (skeleton) from the rules (slots).</p>
<p>Each book contains a very similar HTML structure (depending on the output format; PDF, EPUB, online) but very different styling (fonts, colors, numbering schemes) so we split the CSS into 2 types of files: <strong>slots</strong> for the styling and <strong>skeletons</strong> for the HTML structure and linked them together using a namespaced tree of mixins that represent the <em>logical</em> structure of a book.</p>
<p>In this way a page header would be defined as (the <em>slot</em>):</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#page>#top>.outside() { content: 'Chapter 1'; } and used as (the _skeleton_):
@page:left { @top-left: #page>#top>.outside(); }
</code></pre></div></div>
<p>This allows us to separate the styling (slots) from the content (skeleton).</p>
<h2 id="context">Context</h2>
<p>Getting back to pattern matching; the context of an element (ie “Exercise”) is important for styling that element.
To accomplish this, we created rules for defining mixins and special “generators” for all the possible permutations of contexts.</p>
<p>In our system a mixin has roughly the following signature: <code class="highlighter-rouge">#logical-type>.mixin(@kind; @part; @contexts...) { }</code>.</p>
<p>Let’s go through the various parts:</p>
<ul>
<li><code class="highlighter-rouge">#logical-type</code> roughly corresponds to the structural element (and <code class="highlighter-rouge">data-type</code> attribute). Some examples are <code class="highlighter-rouge">exercise</code>, <code class="highlighter-rouge">note</code>, <code class="highlighter-rouge">term</code>, <code class="highlighter-rouge">example</code></li>
<li><code class="highlighter-rouge">.mixin(...)</code> is the kind of slot (<code class="highlighter-rouge">.style(...)</code> for the whole element, <code class="highlighter-rouge">.title(...)</code> for the title, <code class="highlighter-rouge">.numbering(...)</code> for how to number the element, etc)</li>
<li><code class="highlighter-rouge">@kind</code> corresponds to a specific class for a particular book (a science book may have <code class="highlighter-rouge">experiment</code> while a history book may have <code class="highlighter-rouge">did-you-know</code>)</li>
<li><code class="highlighter-rouge">@part</code> represents which part of a book the element occurs in (<code class="highlighter-rouge">preface</code>, <code class="highlighter-rouge">chapter</code>, <code class="highlighter-rouge">appendix</code>, <code class="highlighter-rouge">any</code>) and is largely used for numbering an element</li>
<li><code class="highlighter-rouge">@contexts...</code> represents what this <code class="highlighter-rouge">logical-type</code> occurs inside (and where all the fun happens). Some examples would be <code class="highlighter-rouge">#figure>.style(any; any; figure) { }</code> (a subfigure), or <code class="highlighter-rouge">#table>.style(any; any; glossary)</code> (a table inside a glossary)</li>
</ul>
<p>This gives us the flexibility to style the content of a book based on the logical parts (using namespaces) and the context it occurs in (using <code class="highlighter-rouge">@contexts...</code>) and have it work for different output formats (by having different skeleton files).</p>
<p>To accomplish the <code class="highlighter-rouge">@contexts...</code> bit we created some mixins that generate all permutations of different contexts. This is accomplished by adding list-expansion to LESS which allows us to expand a list when calling a mixin. For example:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.context-expander(@head; @tail...) {
.mixin-call(@tail...); // <-- The new piece added to LESS
}
</code></pre></div></div>
<p><strong>Note:</strong> For most of these permutations, no mixins will match so they will be omitted from the generated CSS.</p>
<p>The result is a single skeleton file for each output format (EPUB, PDF, online) and a slots file (~100 lines) for each book.</p>
css-polyfills for Books2014-03-07T00:00:00+00:00http://philschatz.com/2014/03/07/polyfills-for-books<h2 id="why-css-instead-of-javascript">“Why CSS instead of Javascript?””</h2>
<p>Wouldn’t it be great if authors could customize their books without having them write (or run) arbitrary JavaScript? This post shows a way to do it.</p>
<h2 id="our-problem">Our Problem</h2>
<p>Our books end up being published in various formats with various support for CSS.</p>
<p>We use Docbook for PDFs partly because we need to move content around (ie collating exercises at the end of a chapter, making an index) and XSLT provides a way to move XML around.</p>
<p>Unfortunately, this means 4 things:</p>
<ul>
<li>developers need to learn XSLT</li>
<li>we must regression-test all of our books whenever we fix a bug or add a feature</li>
<li>CSS for the PDF is different for ePUB and online</li>
<li>numbering things like exercises is different in a PDF than online</li>
</ul>
<p>Fortunately, there are a few W3C Drafts that help fill in some of the gaps: <a href="http://www.w3.org/TR/css3-gcpm/">Generated Content for Paged Media</a> and <a href="http://www.w3.org/TR/css3-content/">CSS3 Generated and Replaced Content Module</a>.</p>
<p>Intersection of Some CSS Features:</p>
<table>
<thead>
<tr>
<th style="text-align: left">Feature</th>
<th style="text-align: center">EPUB2</th>
<th style="text-align: center">Browsers</th>
<th style="text-align: center">PrinceXML (PDF)</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left"><code class="highlighter-rouge">::before</code></td>
<td style="text-align: center"><strong>no</strong></td>
<td style="text-align: center">yes</td>
<td style="text-align: center">yes</td>
</tr>
<tr>
<td style="text-align: left"><code class="highlighter-rouge">counter-increment:</code></td>
<td style="text-align: center"><strong>no</strong></td>
<td style="text-align: center">yes</td>
<td style="text-align: center">yes</td>
</tr>
<tr>
<td style="text-align: left"><code class="highlighter-rouge">content:</code></td>
<td style="text-align: center"><strong>no</strong></td>
<td style="text-align: center"><em>partial</em></td>
<td style="text-align: center">yes</td>
</tr>
<tr>
<td style="text-align: left"><code class="highlighter-rouge">target-text()</code></td>
<td style="text-align: center"><strong>no</strong></td>
<td style="text-align: center"><strong>no</strong></td>
<td style="text-align: center">yes</td>
</tr>
<tr>
<td style="text-align: left"><code class="highlighter-rouge">page-break-*:</code></td>
<td style="text-align: center"><strong>no</strong></td>
<td style="text-align: center"><strong>no</strong></td>
<td style="text-align: center">yes</td>
</tr>
<tr>
<td style="text-align: left"><code class="highlighter-rouge">move-to:</code></td>
<td style="text-align: center"><strong>no</strong></td>
<td style="text-align: center"><strong>no</strong></td>
<td style="text-align: center"><strong>no</strong></td>
</tr>
<tr>
<td style="text-align: left"><code class="highlighter-rouge">::outside::before</code></td>
<td style="text-align: center"><strong>no</strong></td>
<td style="text-align: center"><strong>no</strong></td>
<td style="text-align: center"><strong>no</strong></td>
</tr>
<tr>
<td style="text-align: left"><code class="highlighter-rouge">:has()</code></td>
<td style="text-align: center"><strong>no</strong></td>
<td style="text-align: center"><strong>no</strong></td>
<td style="text-align: center"><strong>no</strong></td>
</tr>
</tbody>
</table>
<p>To replace Docbook and have one CSS file to style the various formats we need to support all of these features <strong>and</strong> note a few differences:</p>
<ul>
<li>PDF is generated using a single large HTML file (CSS needs to operate on all chapters)</li>
<li>ePUB needs to be chunked into multiple HTML files (ideally using CSS <code class="highlighter-rouge">page-break-*</code>)</li>
<li>Online, a single HTML file can be viewed outside the context of a book</li>
</ul>
<h3 id="browsers">Browsers</h3>
<p>Ideally, we would be able to get access to all of these unsupported selectors and rules using the <a href="http://www.w3.org/TR/DOM-Level-2-Style/css.html">CSS Document Object Model</a> but browsers only expose the selectors and rules they understand.</p>
<h2 id="the-solution">The Solution</h2>
<p>Enter <a href="/css-polyfills.js/">CSS-Polyfills</a>.</p>
<p>The project uses <a href="http://lesscss.org">LessCSS</a> and <a href="http://jquery.org">jQuery</a> to parse the CSS file and “bake” the changes into the HTML.</p>
<p>With it you can do things that are not possible using CSS supported by browsers. For example, you can style an element based on children inside:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.note:has(> .title) { /* Give it a fancier border */ }
</code></pre></div></div>
<p>Or, you can automatically generate a glossary at the and of a chapter based on terms in the chapter:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.term > .definition { move-to: glossary-area; }
.chapter:after {
content: pending(glossary-area);
}
</code></pre></div></div>
<p>You can even style links depending on the target:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>a[href] {
// Use x-target-is as a switch for which link text to use
content: x-target-is(attr(href), 'figure') 'See Figure';
content: x-target-is(attr(href), 'table') 'See Table';
content: x-target-is(attr(href), '.example') 'See Example';
}
</code></pre></div></div>
<p>In another post, I’ll go over some of the “Freebies” that come out of this project like CSS Coverage and CSS+HTML Diffs for regression testing.</p>
<h2 id="bonuses">Bonuses</h2>
<p>By parsing the CSS file and “baking” the styles into the HTML there are a few “freebies” that come out.</p>
<h3 id="easy-css-coverage">Easy CSS Coverage</h3>
<p>As a free perk, you can easily generate Coverage data for your CSS files by transforming a HTML and CSS file from the commandline and filtering stderr.</p>
<h3 id="htmlcss-diffs">HTML+CSS Diffs</h3>
<p>To do regression tests on books we merely need to generate the “baked” HTML file twice, once with the old CSS and once with the new CSS (all the styles are “baked” into <code class="highlighter-rouge">style="..."</code> attributes).
Then, a quick XSLT file can compare the two and generate a version of the page with <code class="highlighter-rouge"><span></code> tags marking the places where styling changed.</p>
<p>See <a href="https://github.com/philschatz/css-diff.js">https://github.com/philschatz/css-diff.js</a> for a package that does this.</p>
HTML+CSS Diffs2014-03-02T00:00:00+00:00http://philschatz.com/2014/03/02/css-diff<h2 id="css-diffs">CSS Diffs</h2>
<p>Our textbooks are frequently hundreds of pages long and use a single CSS file, so making a CSS change can change content in unexpected places.</p>
<p>Again, <a href="/css-polyfills.js/">CSS Polyfills</a> and a little XSLT file makes this easy.</p>
<p><a href="https://github.com/philschatz/css-diff.js">CSS-Diff</a> takes an HTML and CSS file and produces an HTML file with all the styling “baked” in.
Then, the provided XSLT file can compare 2 “baked” HTML files and inject <code class="highlighter-rouge"><span></code> tags whenever the styles differ.</p>
<p>See the <a href="https://github.com/philschatz/css-diff.js">CSS-Diff</a> project to run it from the commandline.</p>
css-polyfills.js do more with CSS!2013-12-10T00:00:00+00:00http://philschatz.com/2013/12/10/css-polyfills<p>I often hear “CSS is meant to style and HTML should describe content”, but CSS alone is not enough to make textbooks.</p>
<p>Sometimes you need to:</p>
<ol>
<li>style an element based on what’s inside (notes containing a title)</li>
<li>change link text based on the target (<code class="highlighter-rouge">See Figure 2a</code> vs <code class="highlighter-rouge">See Table 4.3</code>)</li>
<li>move elements somewhere else in the book (answers to the back)</li>
</ol>
<p>Well, now there is <strong><a href="/css-polyfills.js/">css-polyfills.js</a></strong>, based on some great <a href="http://www.w3.org/TR/css3-content/">CSS3</a> <a href="http://www.w3.org/TR/css3-gcpm/">specs</a>, <a href="http://sizzlejs.com">Sizzle</a>, and <a href="https://github.com/josh/selector-set">selector-set</a>.</p>
<h2 id="simple-examples-from-bootstrap">Simple Examples (from Bootstrap)</h2>
<p>With <strong><a href="/css-polyfills.js/">css-polyfills.js</a></strong> you can do things that are not possible using CSS in browsers. Bootstrap’s <a href="http://getbootstrap.com/components/#alerts-dismissable">Dismissable Alerts</a> require adding a special class on the alert if it contains a close button. This can be accomplished without adding the <code class="highlighter-rouge">alert-dismissable</code> class by using <code class="highlighter-rouge">:has</code>:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.alert:has(> .close) { /* Styles for `.alert-dismissable` */ }
</code></pre></div></div>
<p>Bootstrap <a href="http://getbootstrap.com/javascript/#modals">Modals</a> and <a href="http://getbootstrap.com/components/#input-groups-buttons">Input Groups</a> require adding wrapper elements in order to style properly; with nested <code class="highlighter-rouge">::before</code> and <code class="highlighter-rouge">::outside</code> these are unnecessary.</p>
<p>You can construct the 2 additional elements (<code class="highlighter-rouge">.modal</code>, <code class="highlighter-rouge">.modal-dialog</code>) around <code class="highlighter-rouge">.modal-content</code> using the following CSS:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.modal-content { ... }
.modal-content::outside { /* Styles for `.modal-dialog` */}
.modal-content::outside::outside{ /* Styles for `.modal` */ }
</code></pre></div></div>
<h2 id="more-examples">More Examples</h2>
<p>You can even style links depending on the target:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>a[href^="#"] {
// Use target-is as a guard for which link text to use
content: target-is(attr(href), 'figure') 'See Figure';
content: target-is(attr(href), 'table') 'See Table';
content: target-is(attr(href), '.example') 'See Example';
}
</code></pre></div></div>
<p>Or, you can move elements down the page (ie collate footnotes at the bottom of a wikipedia article):</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.footnote { move-to: footnotes-area; }
#footnotes {
content: pending(footnotes-area);
}
</code></pre></div></div>
<p>You can also create the linkable footnotes on wikipedia by keeping the references near the content and use CSS to create links and move the references to the bottom of the page (See <a href="/css-polyfills.js/#section-footnotes">Clickable Footnotes</a>).</p>
<p>Of course, there’s much more that can be accomplished using <a href="/css-polyfills.js">css-polyfills.js</a>; check the <a href="https://github.com/philschatz/css-polyfills.js">README.md</a> for more details.</p>
CSS Coverage for Javascript Unit Tests2013-12-01T00:00:00+00:00http://philschatz.com/2013/12/01/css-coverage<p>There are many CSS coverage projects but none plug <strong>directly into JS unit tests</strong>, instrumenting at the same time as the JavaScript coverage.</p>
<p>Assuming you have BlanketJS and Mocha tests, just grab <strong><a href="https://github.com/philschatz/css-coverage.js">css-coverage.js</a></strong> and add the following into the test harness HTML file :</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code><link rel="stylesheet/coverage" href="path/to/file.less" />
<script src="css-coverage.js"></script>
</code></pre></div></div>
<h2 id="lessjs--mocha--blanketjs">Less.js + Mocha + BlanketJS</h2>
<p>The coverage script uses the <code class="highlighter-rouge">less.tree</code> AST to parse all the selectors and the <code class="highlighter-rouge">debugInfo</code> data to get line numbers. It adds a <code class="highlighter-rouge">testdone</code> hook to Mocha that runs all the selectors on the page. Then, the line number and coverage counts are given to BlanketJS so LESS/CSS files are included in the coverage report.</p>
<h2 id="example-screenshot">Example Screenshot</h2>
<p><a href="/css-coverage.js/test/mocha-demo/"><img src="https://f.cloud.github.com/assets/253202/2317474/4856dbea-a34e-11e3-92ae-70f53672cb93.png" alt="CSS Coverage Demo" /></a></p>
<p>Check out the <a href="/css-coverage.js/test/mocha-demo/">Mocha + BlanketJS demo</a> and the <a href="https://github.com/philschatz/css-coverage.js">github repo</a> for more!</p>
<h2 id="other-features">Other Features</h2>
<ul>
<li>Loading LESS files using RequireJS is supported with 0 additional work</li>
<li>Instrumented less files that import other files are automatically instrumented</li>
</ul>
Building an eBook using GitHub2013-06-03T00:00:00+00:00http://philschatz.com/2013/06/03/github-bookeditor<p>At <a href="http://cnx.org">Connexions</a> we build free textbooks. To do it we have roughly 4 services:</p>
<ul>
<li>Published Repository (Published Books)</li>
<li>Authoring Repository (Drafts)</li>
<li>Generate exports (EPUB, PDF)</li>
<li>Website</li>
</ul>
<p>As someone who uses GitHub and Travis daily, I wondered how much of this GitHub (and Travis) can do for us.
It turns out, quite a bit. If each book is a repository whose contents is an <em>Unzipped EPUB</em> then amazing things are possible.</p>
<blockquote>
<p>Book Publishing = GitHub Repo + GitHub Pages + “Download as Zip” + Service Hooks</p>
</blockquote>
<ul>
<li>the <code class="highlighter-rouge">master</code> branch is the published version of the book</li>
<li>tags are various editions of a book</li>
<li><code class="highlighter-rouge">gh-pages</code> branch can be used as a book reader (Website)</li>
<li>“Download as Zip” is the EPUB version of the book</li>
<li>The editor can “save” via the GitHub API</li>
<li>Travis-CI can be used to generate a PDF (and “push” to AWS or some other place)</li>
</ul>
<p>So, I took our editor and made it save EPUB files to GitHub and <em>viola</em>, a book editor using GitHub!</p>
<ul>
<li>the <a href="https://github.com/oerpub/github-bookeditor">WYSIWYG Book Editor</a> and <a href="http://oerpub.github.io/github-bookeditor">Demo</a></li>
<li>the <a href="https://github.com/philschatz/pdf-ci">PDF-CI server</a> and demo that <a href="http://pdf.oerpub.org">generates PDFs on commit</a></li>
<li>example of the <a href="http://philschatz.com/epub-anatomy/reader/">book reader</a> using <code class="highlighter-rouge">gh-pages</code> and <a href="https://github.com/futurepress/epub.js">epub.js</a></li>
</ul>
Introducing octokit.js2013-03-10T00:00:00+00:00http://philschatz.com/2013/03/10/octokit<p><a href="https://github.com/philschatz/octokit.js">octokit.js</a> is a JavaScript client that interacts with GitHub using their API. Try it out with the <strong><a href="/octokit.js/demo/">interactive demo</a></strong>!</p>
<p>Some unique features:</p>
<ul>
<li>works in a browser or in NodeJS</li>
<li>uses <a href="http://api.jquery.com/category/deferred-object/">Promises</a></li>
<li>written in CoffeeScript</li>
<li>Mocha unit tests that actually talk to GitHub (and run in the browser or commandline)</li>
<li>Code Coverage using BlanketJS (that runs in the browser or commandline)</li>
<li>supports <a href="http://wiki.ecmascript.org/doku.php?id=harmony:generators">Harmony Generators</a></li>
</ul>
<p>API Support:</p>
<ul>
<li>all repo operations (including create/remove)</li>
<li>Teams, Organizations, and Permissions</li>
<li>eTags for caching results</li>
<li>Notifications for rate limit changes</li>
<li>Committing multiple files at once</li>
</ul>
<p>Finding a test and coverage framework that works both in a browser and on the commandline was a bit challenging but I’ll return to that in another post.</p>
<p>This library also uses <code class="highlighter-rouge">jQuery.ajax</code> and <code class="highlighter-rouge">jQuery.Deferred</code>.</p>