/*! Flatdoc (http://ricostacruz.com/flatdoc) (c) 2013 Rico Sta. Cruz. MIT licensed. Also includes: marked a markdown parser (c) 2011-2013, Christopher Jeffrey. (MIT Licensed) https://github.com/chjj/marked base64.js http://github.com/dankogai/js-base64 */ (function($) { var exports = this; var marked; /** * Basic Flatdoc module. * * The main entry point is `Flatdoc.run()`, which invokes the [Runner]. * * Flatdoc.run({ * fetcher: Flatdoc.github('rstacruz/backbone-patterns'); * }); */ var Flatdoc = exports.Flatdoc = {}; /** * Creates a runner. * See [Flatdoc]. */ Flatdoc.run = function(options) { $(function() { (new Flatdoc.runner(options)).run(); }); }; /** * File fetcher function. * * Fetches a given `url` via AJAX. * See [Runner#run()] for a description of fetcher functions. */ Flatdoc.file = function(url) { return function(callback) { $.get(url) .fail(function(e) { callback(e, null); }) .done(function(data) { callback(null, data); }); }; }; /** * Github fetcher. * Fetches from repo `repo` (in format 'user/repo'). * * If the parameter `filepath` is supplied, it fetches the contents of that * given file in the repo. * * See [Runner#run()] for a description of fetcher functions. * * See: http://developer.github.com/v3/repos/contents/ */ Flatdoc.github = function(repo, filepath) { var url; if (filepath) { url = 'https://api.github.com/repos/'+repo+'/contents/'+filepath; } else { url = 'https://api.github.com/repos/'+repo+'/readme'; } return function(callback) { $.get(url) .fail(function(e) { callback(e, null); }) .done(function(data) { var markdown = exports.Base64.decode(data.content); callback(null, markdown); }); }; }; /** * Parser module. * Parses a given Markdown document and returns a JSON object with data * on the Markdown document. * * var data = Flatdoc.parser.parse('markdown source here'); * console.log(data); * * data == { * title: 'My Project', * content: '

This project is a...', * menu: {...} * } */ var Parser = Flatdoc.parser = {}; /** * Parses a given Markdown document. * See `Parser` for more info. */ Parser.parse = function(source) { marked = exports.marked; Parser.setMarkedOptions(); var html = $("

" + marked(source)); var h1 = html.find('h1').eq(0); var title = h1.text(); // Mangle content Transformer.mangle(html); var menu = Transformer.getMenu(html); return { title: title, content: html, menu: menu }; }; Parser.setMarkedOptions = function() { marked.setOptions({ highlight: function(code, lang) { if (lang) { var fn = Flatdoc.highlighters[lang] || Flatdoc.highlighters.generic; return fn(code); } return code; } }); }; /** * Transformer module. * This takes care of any HTML mangling needed. The main entry point is * `.mangle()` which applies all transformations needed. * * var $content = $("

Hello there, this is a docu..."); * Flatdoc.transformer.mangle($content); * * If you would like to change any of the transformations, decorate any of * the functions in `Flatdoc.transformer`. */ var Transformer = Flatdoc.transformer = {}; /** * Takes a given HTML `$content` and improves the markup of it by executing * the transformations. * * > See: [Transformer](#transformer) */ Transformer.mangle = function($content) { this.addIDs($content); this.buttonize($content); this.smartquotes($content); }; /** * Adds IDs to headings. */ Transformer.addIDs = function($content) { $content.find('h1, h2, h3').each(function() { var $el = $(this); var text = $el.text(); var id = slugify(text); $el.attr('id', id); }); }; /** * Returns menu data for a given HTML. * * menu = Flatdoc.transformer.getMenu($content); * menu == { * level: 0, * items: [{ * section: "Getting started", * level: 1, * items: [...]}, ...]} */ Transformer.getMenu = function($content) { var root = {items: [], id: '', level: 0}; var cache = [root]; function mkdir_p(level) { var parent = (level > 1) ? mkdir_p(level-1) : root; if (!cache[level]) { var obj = { items: [], level: level }; cache[level] = obj; parent.items.push(obj); return obj; } return cache[level]; } $content.find('h1, h2, h3').each(function() { var $el = $(this); var level = +(this.nodeName.substr(1)); var parent = mkdir_p(level-1); var obj = { section: $el.text(), items: [], level: level, id: $el.attr('id') }; parent.items.push(obj); cache[level] = obj; }); return root; }; /** * Changes "button >" text to buttons. */ Transformer.buttonize = function($content) { $content.find('a').each(function() { var $a = $(this); var m = $a.text().match(/^(.*) >$/); if (m) $a.text(m[1]).addClass('button'); }); }; /** * Applies smart quotes to a given element. * It leaves `code` and `pre` blocks alone. */ Transformer.smartquotes = function ($content) { var nodes = getTextNodesIn($content), len = nodes.length; for (var i=0; i/g, '>') .replace(/("[^\"]*?")/g, '$1') .replace(/('[^\']*?')/g, '$1') .replace(/\/\/(.*)/gm, '//$1') .replace(/\/\*(.*)\*\//gm, '/*$1*/') .replace(/(\d+\.\d+)/gm, '$1') .replace(/(\d+)/gm, '$1') .replace(/\bnew *(\w+)/gm, 'new $1') .replace(/\b(function|new|throw|return|var|if|else)\b/gm, '$1'); }; Highlighters.html = function(code) { return code .replace(//g, '>') .replace(/("[^\"]*?")/g, '$1') .replace(/('[^\']*?')/g, '$1') .replace(/<!--(.*)-->/g, '<!--$1-->') .replace(/<([^!][^ ]*)/g, '<$1'); }; Highlighters.generic = function(code) { return code .replace(//g, '>') .replace(/("[^\"]*?")/g, '$1') .replace(/('[^\']*?')/g, '$1') .replace(/(\/\/|#)(.*)/gm, '$1$2') .replace(/(\d+\.\d+)/gm, '$1') .replace(/(\d+)/gm, '$1'); }; /** * Menu view. Renders menus */ var MenuView = Flatdoc.menuView = function(menu) { var $el = $("