decktape/decktape.js
2015-06-29 23:05:16 +02:00

174 lines
5.4 KiB
JavaScript

var page = require("webpage").create(),
printer = require("printer").create(),
system = require("system"),
fs = require("fs");
// Node to PhantomJS bridging required for nomnom
var process = {
exit : function(code) {
phantom.exit(code);
}
};
var opts = require("./libs/nomnom")
.nocolors()
.script("phantomjs decktape.js")
.options( {
url: {
position: 1,
required: true,
help: "URL of the slides deck"
},
filename: {
position: 2,
required: true,
help: "Filename of the output PDF file"
},
width: {
default: 1280,
help: "Width of the slides deck"
},
height: {
default: 720,
help: "Height of the slides deck"
},
pause: {
default: 1000,
help: "Duration in milliseconds before the next slide is exported"
}
} ).parse(system.args);
page.viewportSize = { width: opts.width, height: opts.height };
printer.paperSize = { width: opts.width + "px", height: opts.height + "px", margin: "0px" };
printer.outputFileName = opts.filename;
page.onLoadStarted = function() {
console.log("Loading page " + opts.url + " ...");
};
page.onResourceTimeout = function(request) {
console.log("+- Request timeout: " + JSON.stringify(request));
};
page.onResourceError = function(resourceError) {
console.log("+- Unable to load resource from URL: " + resourceError.url);
console.log("|_ Error code: " + resourceError.errorCode);
console.log("|_ Description: " + resourceError.errorString);
};
// FIXME: PhantomJS is emitting this event for both pages and frames
page.onLoadFinished = function(status) {
console.log("Loading page finished with status: " + status);
};
// Must be set before the page is opened
page.onConsoleMessage = function(msg) {
console.log(msg);
};
page.open(opts.url, function(status) {
if (status !== "success") {
console.log("Unable to load the address: " + opts.url);
phantom.exit(1);
} else {
var plugin = detectActivePlugin();
if (!plugin) {
// TODO: backend fallback manual support
console.log("No DeckTape plugin supported for the address: " + opts.url);
phantom.exit(1);
}
console.log(plugin.getName() + " DeckTape plugin activated");
configure(plugin);
printer.begin();
printSlide(plugin);
}
});
function printSlide(plugin) {
window.setTimeout(function() {
system.stdout.write('\r' + progressBar(plugin));
printer.printPage(page);
if (hasNextSlide(plugin)) {
nextSlide(plugin);
printSlide(plugin);
} else {
printer.end();
system.stdout.write("\nPrinted " + plugin.currentSlide + " slides\n");
phantom.exit();
}
}, opts.pause);
// TODO: support a more advanced "fragment to pause" mapping for special use cases like GIF animations
// TODO: add a plugin optional function to wait until a particular condition instead of a pause
}
// TODO: add progress bar, duration, ETA and file size
function progressBar(plugin) {
var cols = [];
var index = currentSlideIndex(plugin);
cols.push("Printing slide ");
cols.push(padding('#' + index, 8, ' ', false));
cols.push(" (");
cols.push(padding(plugin.currentSlide, plugin.totalSlides ? plugin.totalSlides.toString().length : 3, ' '));
cols.push('/');
cols.push(plugin.totalSlides ? plugin.totalSlides : " ?");
cols.push(") ...");
// erase overflowing slide fragments
cols.push(padding("", plugin.progressBarOverflow - Math.max(index.length + 1 - 8, 0), ' ', false));
plugin.progressBarOverflow = Math.max(index.length + 1 - 8, 0);
return cols.join('');
}
function padding(str, len, char, left) {
if (typeof str === "number")
str = str.toString();
var l = len - str.length;
var p = [];
while (l-- > 0)
p.push(char);
return left === undefined || left ?
p.join('').concat(str) :
str.concat(p.join(''));
}
function detectActivePlugin() {
var plugins = fs.list("plugins/");
for (var i = 0; i < plugins.length; i++) {
if (!fs.isFile("plugins/" + plugins[i]))
continue;
var matches = plugins[i].match(/^(.*)\.js$/);
if (!matches)
continue;
var plugin = require("./plugins/" + matches[1]);
if (page.evaluate(plugin.isActive))
return plugin;
}
}
var configure = function(plugin) {
plugin.progressBarOverflow = 0;
plugin.currentSlide = 1;
plugin.totalSlides = slideCount(plugin);
if (typeof plugin.configure === "function")
return page.evaluate(plugin.configure);
};
var slideCount = function(plugin) {
return page.evaluate(plugin.slideCount);
};
var hasNextSlide = function(plugin) {
if (typeof plugin.hasNextSlide === "function")
return page.evaluate(plugin.hasNextSlide);
else
return plugin.currentSlide < plugin.totalSlides;
};
var nextSlide = function(plugin) {
plugin.currentSlide++;
return page.evaluate(plugin.nextSlide);
};
var currentSlideIndex = function(plugin) {
return page.evaluate(plugin.currentSlideIndex);
};