Pictures!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

slideshow.js 3.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. var fs = require("fs");
  2. var pathlib = require("path");
  3. var mimetype = require("./mimetype");
  4. var error = require("./error");
  5. module.exports = Slideshow;
  6. var htmlPre =
  7. "<html>"+
  8. "<head>"+
  9. "<link rel='stylesheet' href='/slide.css'>"+
  10. "<meta charset='utf-8'>"+
  11. "</head>"+
  12. "<body>"+
  13. "<div id='_wrapper'>";
  14. var htmlPost =
  15. "</div>"+
  16. "</body>"+
  17. "</html>";
  18. function sendFile(path, res) {
  19. res.writeHead(200, {
  20. "content-type": mimetype(path)
  21. });
  22. fs.createReadStream(path)
  23. .on("error", err => error(err, res))
  24. .pipe(res);
  25. }
  26. function sendIndex(path, res) {
  27. res.writeHead(200, {
  28. "content-type": "text/html"
  29. });
  30. res.write(htmlPre);
  31. fs.readFile(path, (err, text) => {
  32. if (err)
  33. text.write(err.toString());
  34. else
  35. res.write(text);
  36. res.end(htmlPost);
  37. });
  38. }
  39. // The individual slide
  40. function Slide(dir) {
  41. var self = {};
  42. self.dir = dir;
  43. self.name = pathlib.parse(dir).name;
  44. self.sendIndex = function(res) {
  45. sendIndex(pathlib.join(self.dir, "index.html"), res);
  46. }
  47. self.sendFile = function(name, res) {
  48. sendFile(pathlib.join(self.dir, name), res);
  49. }
  50. self.serveFiles = function(parts, res) {
  51. // Serve index if /{name} is reuested
  52. if (parts.pathname.replace(/\//g, "") === self.name) {
  53. // Redirect from /{name} to /{name}/
  54. if (parts.pathname[parts.pathname.length - 1] !== '/') {
  55. res.writeHead(302, {
  56. location: parts.pathname+"/"
  57. });
  58. res.end();
  59. // Serve index if it's already /{name}/
  60. } else {
  61. self.sendIndex(res);
  62. }
  63. } else {
  64. // Serve other files
  65. var name = parts.pathname.substring(1).replace(self.name, "");
  66. var path = pathlib.join(dir, name);
  67. sendFile(path, res);
  68. }
  69. }
  70. self.indexExists = function() {
  71. try {
  72. fs.accessSync(pathlib.join(dir, "index.html"));
  73. return true;
  74. } catch (err) {
  75. return false;
  76. }
  77. }
  78. try {
  79. self.meta = JSON.parse(
  80. fs.readFileSync(pathlib.join(dir, "meta.json")));
  81. } catch (err) {
  82. if (err.code !== "ENOENT")
  83. error(err);
  84. self.meta = {};
  85. }
  86. return self;
  87. }
  88. // The slideshow, whose job it is to manage all slides
  89. // and tell the client whether it has to update or not
  90. function Slideshow(dir, changeInterval) {
  91. var self = {};
  92. var currentSlide = null;
  93. var awaiters = [];
  94. var slides = [];
  95. var slideIndex = 0;
  96. var nextTimeout;
  97. self.sendEvent = function(evt, args) {
  98. var str = JSON.stringify({ evt: evt, args: args });
  99. awaiters.forEach(res => res.end(str));
  100. awaiters = [];
  101. }
  102. // Get the current slide's interval
  103. function currentInterval() {
  104. var itv = currentSlide.meta.interval;
  105. if (itv === undefined)
  106. itv = changeInterval;
  107. return itv;
  108. }
  109. // Return if the current slide is enabled and valid
  110. function currentEnabled() {
  111. return currentSlide.indexExists() && !currentSlide.disabled;
  112. }
  113. self.getSlideName = getSlideName;
  114. function getSlideName() {
  115. return currentSlide ? currentSlide.name : ""
  116. }
  117. self.pushAwaiter = pushAwaiter;
  118. function pushAwaiter(res) {
  119. awaiters.push(res);
  120. }
  121. self.getSlides = getSlides;
  122. function getSlides() {
  123. return slides;
  124. }
  125. self.next = next;
  126. function next() {
  127. slideIndex += 1;
  128. // Go to the next slide, or restart
  129. if (slideIndex >= slides.length) {
  130. clearTimeout(nextTimeout);
  131. init();
  132. } else {
  133. currentSlide = slides[slideIndex];
  134. nextTimeout = setTimeout(next, currentInterval());
  135. }
  136. // End all awaiting connections to notify slide change,
  137. if (currentEnabled()) {
  138. self.sendEvent("next", { name: currentSlide.name });
  139. // Or go to the next slide if the current one doesn't have an index.html
  140. // or if the slide is disabled
  141. } else {
  142. clearTimeout(nextTimeout);
  143. setTimeout(next, 0);
  144. }
  145. }
  146. // This function starts the slideshow and goes through the slides
  147. // one by one. When done, it starts again by calling this function again.
  148. function init() {
  149. slides = fs.readdirSync(dir)
  150. .sort()
  151. .map(file => Slide(pathlib.join(dir, file)));
  152. slideIndex = 0;
  153. currentSlide = slides[slideIndex];
  154. nextTimeout = setTimeout(next, currentInterval());
  155. }
  156. init();
  157. return self;
  158. }