diff --git a/bin/cleaver b/bin/cleaver index 3e3898c..93fbbd8 100755 --- a/bin/cleaver +++ b/bin/cleaver @@ -9,14 +9,14 @@ var Cleaver; * Helper function to use the cleaver API to create and save a new * presentation */ -function createAndSave(file) { +function createAndSave(file, options) { fs.readFile(file, 'utf-8', function (err, contents) { - var presentation = new Cleaver(contents, path.resolve(path.dirname(file))); + var presentation = new Cleaver(contents, options, path.resolve(path.dirname(file))); var promise = presentation.run(); promise .then(function (product) { - var outputFile = presentation.metadata.output || path.basename(file, '.md') + '-cleaver.html'; + var outputFile = presentation.options.output || path.basename(file, '.md') + '-cleaver.html'; fs.writeFile(outputFile, product); }) .fail(function (err) { @@ -38,7 +38,7 @@ program .description('Watch for changes on markdown file') .action(function () { var file = program.args[0]; - createAndSave(file); + createAndSave(file, parsedOpts); console.log('Watching for changes on ' + file + '. Ctrl-C to abort.'); fs.watchFile(file, { persistent: true, interval: 100 }, function () { @@ -48,11 +48,43 @@ program }); program + .option('--title ', 'The title of the rendered document ' + + '(default: Untitled') + // TODO: author options + .option('--theme <theme>', 'An theme to load') + .option('--style <css>', 'A particular stylesheet to load') + .option('--output <filename>', 'The filename of the rendered document ' + + '(default: [INPUT]-cleaver.html)') + .option('--controls', 'Whether or not to include simple navigation ' + + 'controls in your presentation (default: true)') + .option('--progress', 'Option to display a small progress bar at the top ' + + 'of your presentation (default: true)') + .option('--encoding <encoding>', 'Content encoding used for the rendered ' + + 'document (default: utf-8)') + .option('--template <path>', 'URL or path to a mustache template used for ' + + 'individual slides') + .option('--layout <path>', 'URL or path to a mustache template used to ' + + 'render the entire presentation') .option('--debug', 'Enable debug output'); program.parse(process.argv); +// TODO: a lot of code duplication here +var parsedOpts = { + title: program.title, + theme: program.theme, + style: program.style, + output: program.output, + controls: program.controls, + progress: program.progress, + encoding: program.encoding, + template: program.template, + layout: program.layout, + debug: program.debug +}; + if (!program.args.length) { + // TODO: custom help screen program.help(); } else { /** @@ -71,5 +103,5 @@ if (!program.args.length) { // Load cleaver with the new ENV for debugging // TODO: This seems a little janky, maybe handle this in lib/ Cleaver = require('../'); - createAndSave(program.args[0]); + createAndSave(program.args[0], parsedOpts); } diff --git a/docs/options.md b/docs/options.md index 4f34f19..08fdcef 100644 --- a/docs/options.md +++ b/docs/options.md @@ -19,7 +19,7 @@ YAML format. A typical option setup resembles the following. ### title -The title of the slidshow. +The title of the rendered document. **Default**: Untitled @@ -90,13 +90,13 @@ Content encoding to use on the rendered document. ### template -URL or absolute/relative path to a mustache template used to render the slides. +URL or path to a mustache template used to render the slides. See [default.mustache](https://github.com/jdan/cleaver/blob/master/templates/default.mustache) for inspiration. ### layout -URL or absolute/relative path to a mustache template used to render the entire +URL or path to a mustache template used to render the entire slideshow. See [layout.mustache](https://github.com/jdan/cleaver/blob/master/templates/layout.mustache) for inspiration. diff --git a/lib/index.js b/lib/index.js index 7c7b74d..25a552b 100644 --- a/lib/index.js +++ b/lib/index.js @@ -13,12 +13,19 @@ var helper; * include external dependencies (stylesheets, scripts, etc) * * @param {string} document The input document + * @param {!Object} options Optional settings object to overwrite the options + * listed in the cleaver document * @param {!string} includePath Optional resource path (default: '.') * @constructor */ -function Cleaver(document, includePath) { +function Cleaver(document, options, includePath) { this.document = document.toString(); + this.options = options || {}; this.path = path.resolve(includePath || '.'); + /** + * Require helper using `this.path` as a context for where to locate any + * specified assets + */ helper = require('./helper')(this.path); this.templates = { @@ -37,7 +44,6 @@ function Cleaver(document, includePath) { style: [] }; - this.metadata = null; this.slides = []; this.override = false; @@ -68,16 +74,21 @@ Cleaver.prototype.loadDocument = function () { /** - * Parses the metadata and renders the slides. + * Parses the options and renders the slides. * * @return {Promise} */ Cleaver.prototype.renderSlides = function () { var slices = this.slice(this.document); - var i; + var i, options; - this.metadata = yaml.safeLoad(slices[0].content) || {}; - debug('parsed metadata'); + options = yaml.safeLoad(slices[0].content) || {}; + // Load any options that are not already present from the constructor + for (i in options) { + this.options[i] = this.options[i] || options[i]; + } + + debug('parsed options'); for (i = 1; i < slices.length; i++) { this.slides.push({ @@ -89,16 +100,16 @@ Cleaver.prototype.renderSlides = function () { } // insert an author slide (if necessary) at the end - if (this.metadata.author) { - if (this.metadata.author.twitter && - !this.metadata.author.twitter.match(/^@/)) { - this.metadata.author.twitter = '@' + this.metadata.author.twitter; + if (this.options.author) { + var twitter = this.options.author.twitter; + if (twitter && !twitter.match(/^@/)) { + this.options.author.twitter = '@' + this.options.author.twitter; } this.slides.push({ id: i, hidden: true, - content: this.renderAuthorSlide(this.metadata.author) + content: this.renderAuthorSlide(this.options.author) }); } @@ -107,7 +118,7 @@ Cleaver.prototype.renderSlides = function () { /** - * Populates `slides` and some extra loaded content, based on the metadata + * Populates `slides` and some extra loaded content, based on the options * listed in the document. * * @return {Promise} @@ -116,24 +127,24 @@ Cleaver.prototype.populateResources = function () { var promises = []; // maybe load an external stylesheet - if (this.metadata.style) { - promises.push(helper.populateSingle(this.metadata.style, this.external, 'style') + if (this.options.style) { + promises.push(helper.populateSingle(this.options.style, this.external, 'style') .then(function () { debug('loaded user stylesheet'); })); } // maybe load an external template - if (this.metadata.template) { - promises.push(helper.populateSingle(this.metadata.template, this.templates, 'slides') + if (this.options.template) { + promises.push(helper.populateSingle(this.options.template, this.templates, 'slides') .then(function () { debug('loaded user template'); })); } // maybe load an external layout - if (this.metadata.layout) { - promises.push(helper.populateSingle(this.metadata.layout, this.templates, 'layout') + if (this.options.layout) { + promises.push(helper.populateSingle(this.options.layout, this.templates, 'layout') .then(function () { debug('loaded user layout'); })); @@ -150,8 +161,8 @@ Cleaver.prototype.populateResources = function () { */ Cleaver.prototype.populateThemeResources = function () { // maybe load a theme - if (this.metadata.theme) { - return helper.loadTheme(this.metadata.theme, this).then(function () { + if (this.options.theme) { + return helper.loadTheme(this.options.theme, this).then(function () { debug('loaded theme'); }); } @@ -179,8 +190,8 @@ Cleaver.prototype.loadStaticAssets = function () { * @return {Promise} */ Cleaver.prototype.renderSlideshow = function () { - var putControls = this.metadata.controls || (this.metadata.controls === undefined); - var putProgress = this.metadata.progress || (this.metadata.progress === undefined); + var putControls = this.options.controls || (this.options.controls === undefined); + var putProgress = this.options.progress || (this.options.progress === undefined); var style, script, output; // Render the slides in a template (maybe as specified by the user) @@ -193,8 +204,8 @@ Cleaver.prototype.renderSlideshow = function () { debug('rendered slides'); // TODO: handle defaults gracefully - var title = this.metadata.title || 'Untitled'; - var encoding = this.metadata.encoding || 'utf-8'; + var title = this.options.title || 'Untitled'; + var encoding = this.options.encoding || 'utf-8'; if (this.override && this.external.loaded.style) { style = this.external.loaded.style && this.external.loaded.style.join('\n'); @@ -221,9 +232,9 @@ Cleaver.prototype.renderSlideshow = function () { title: title, encoding: encoding, style: style, - author: this.metadata.author, + author: this.options.author, script: script, - metadata: this.metadata + options: this.options }; /* Return the rendered slideshow */ @@ -249,7 +260,7 @@ Cleaver.prototype.renderSlide = function (content) { /** * Renders the author slide. * - * @param {string} content The author field of the slideshow metadata + * @param {string} content The author field of the slideshow options * @return {string} The formatted author slide */ Cleaver.prototype.renderAuthorSlide = function (content) { @@ -282,7 +293,7 @@ Cleaver.prototype.slice = function(document) { continue; } else { - /* If we leave out metadata, add an empty slide at the beginning */ + /* If we leave out options, add an empty slide at the beginning */ if (i === 0) { slices.push({ content: '' }); } @@ -319,7 +330,7 @@ Cleaver.prototype.slice = function(document) { Cleaver.prototype.run = function () { var self = this; - // Load document -> Parse Metadata / Render Slides -> Populate Resources + // Load document -> Parse options / Render Slides -> Populate Resources var documentChain = this.loadDocument() .then(self.renderSlides.bind(self)) .then(self.populateThemeResources.bind(self))