var fs = require("fs");
var pathlib = require("path");
var marked = require("marked");
var mimetype = require("./mimetype");
var error = require("./error");
module.exports = Slideshow;
marked.setOptions({
sanitize: true
});
var htmlPre =
""+
"
"+
""+
""+
""+
""+
"";
var htmlPost =
""+
"
"+
""+
"";
function sendFile(path, res) {
res.writeHead(200, {
"content-type": mimetype(path)
});
fs.createReadStream(path)
.on("error", err => error(err, res))
.pipe(res);
}
function sendIndex(html, res) {
res.writeHead(200, {
"content-type": "text/html"
});
res.write(htmlPre);
res.write(html);
res.end(htmlPost);
}
// The individual slide
function Slide(dir) {
var self = {};
self.dir = dir;
self.name = pathlib.parse(dir).name;
self.stat = null;
self.html = "";
self.sendIndex = function(res) {
var path = pathlib.join(self.dir, "index.md");
fs.stat(pathlib.join(self.dir, "index.md"), (err, stat) => {
if (err) {
sendIndex("", res);
console.log(err);
return;
}
if (!self.stat || !stat || self.stat.mtime !== stat.mtime) {
fs.readFile(path, "utf-8", (err, md) => {
if (err) {
sendIndex(err);
console.log(err);
return;
}
self.html = marked(md);
sendIndex(self.html, res);
});
} else {
sendIndex(self.html, res);
}
self.stat = stat;
});
}
self.sendFile = function(name, res) {
sendFile(self.html, res);
}
self.serveFiles = function(parts, res) {
// Serve index if /{name} is requested
if (parts.pathname.replace(/\//g, "") === self.name) {
// Redirect from /{name} to /{name}/
if (parts.pathname[parts.pathname.length - 1] !== '/') {
res.writeHead(302, {
location: parts.pathname+"/"
});
res.end();
// Serve index if it's already /{name}/
} else {
self.sendIndex(res);
}
} else {
// Serve other files
var name = parts.pathname.substring(1).replace(self.name, "");
var path = pathlib.join(dir, name);
sendFile(path, res);
}
}
self.indexExists = function() {
try {
fs.accessSync(pathlib.join(dir, "index.md"));
return true;
} catch (err) {
return false;
}
}
try {
self.meta = JSON.parse(
fs.readFileSync(pathlib.join(dir, "meta.json")));
} catch (err) {
if (err.code !== "ENOENT")
error(err);
self.meta = {};
}
return self;
}
// The slideshow, whose job it is to manage all slides
// and tell the client whether it has to update or not
function Slideshow(dir, changeInterval) {
var self = {};
var currentSlide = null;
var awaiters = [];
var slides = [];
var slideIndex = 0;
var nextTimeout;
self.sendEvent = function(evt, args) {
var str = JSON.stringify({ evt: evt, args: args });
awaiters.forEach(res => res.end(str));
awaiters = [];
}
// Get the current slide's interval
function currentInterval() {
var itv = currentSlide.meta.interval;
if (itv === undefined)
itv = changeInterval;
return itv;
}
// Return if the current slide is enabled and valid
function currentEnabled() {
return currentSlide.indexExists() && !currentSlide.disabled;
}
self.getSlideName = getSlideName;
function getSlideName() {
return currentSlide ? currentSlide.name : ""
}
self.pushAwaiter = pushAwaiter;
function pushAwaiter(res) {
awaiters.push(res);
}
self.getSlides = getSlides;
function getSlides() {
return slides;
}
self.next = next;
function next() {
slideIndex += 1;
// Go to the next slide, or restart
if (slideIndex >= slides.length) {
clearTimeout(nextTimeout);
init();
} else {
currentSlide = slides[slideIndex];
nextTimeout = setTimeout(next, currentInterval());
}
// End all awaiting connections to notify slide change,
if (currentEnabled()) {
self.sendEvent("next", { name: currentSlide.name });
// Or go to the next slide if the current one doesn't have an index.html
// or if the slide is disabled
} else {
clearTimeout(nextTimeout);
setTimeout(next, 0);
}
}
// This function starts the slideshow and goes through the slides
// one by one. When done, it starts again by calling this function again.
function init() {
slides = fs.readdirSync(dir)
.sort()
.map(file => Slide(pathlib.join(dir, file)));
slideIndex = 0;
currentSlide = slides[slideIndex];
nextTimeout = setTimeout(next, currentInterval());
}
init();
return self;
}