-
Thanksgiving!
+
[[holidayTitle]]
time
Photos taken on [[memoryDate]]
@@ -551,6 +566,10 @@
date: {
type: String,
value: window.moment().format("YYYY-MM-DD")
+ },
+ holiday: {
+ type: String,
+ value: "Christmas"
}
},
@@ -846,6 +865,12 @@
"iron-resize" : "onResize"
},
+ loadHoliday: function(event) {
+ this.holiday = event.model.item;
+ this.resetPhotos();
+ this._loadPhotos();
+ },
+
loadPath: function(event) {
this._pathLoad(event.model.item.path);
},
@@ -1695,8 +1720,8 @@
path = "";
} else if (mode == "memories") {
path = "memories/" + (this.date.replace(this.year + "-", "") || "");
- } else if (mode == "thanksgiving") {
- path = "thanksgiving/";
+ } else if (mode == "holiday") {
+ path = "holiday/" + this.holiday;
}
}
var username = this.user ? this.user.username : "";
@@ -1711,7 +1736,7 @@
if ((username != (this.user ? this.user.username : "")) ||
(mode != this.mode) ||
((mode == "albums") && (path != (this.path || ""))) ||
- ((mode == "thanksgiving") && (path != ("thanksgiving/"))) ||
+ ((mode == "holiday") && (path != ("holiday/" + this.holiday))) ||
((mode == "memories") && (path != ("memories/" + (this.date.replace(this.year + "-", "") || ""))))) {
console.log("Skipping results for old query. Triggering re-fetch of photos for new path or mode.");
this._loadPhotos();
@@ -1750,6 +1775,9 @@
this._loadPhotos(results.cursor, true, this.limit * 2);
}
+ if (this.mode == "holiday") {
+ this.holidayTitle = results.holiday;
+ }
}.bind(this, path));
},
@@ -1903,6 +1931,30 @@
}
},
+ loadHolidays: function() {
+ window.fetch("api/v1/holidays", function(error, xhr) {
+ if (error) {
+ console.error(JSON.stringify(error, null, 2));
+ return;
+ }
+
+ var results;
+ try {
+ results = JSON.parse(xhr.responseText);
+ } catch (___) {
+ this.$.toast.text = "Unable to parse holidays.";
+ this.$.toast.setAttribute("error", true);
+ this.$.toast.updateStyles();
+ this.$.toast.show();
+ console.log(xhr.responseText);
+ return;
+ }
+
+ this.holiday = results.next;
+ this.holidays = results.holidays;
+ }.bind(this));
+ },
+
userChanged: function(user) {
if (!this.firstRequest) {
this.mode = "loading";
@@ -1923,6 +1975,7 @@
if (!user.restriction) {
this.loginStatus = null;
this.mode = "memories";
+ this.loadHolidays();
this.setActions();
return;
}
diff --git a/package.json b/package.json
index 08f5d3e..6071bc4 100644
--- a/package.json
+++ b/package.json
@@ -25,6 +25,7 @@
"ldapauth-fork": "^4.0.2",
"mariasql": "^0.2.6",
"moment": "^2.22.2",
+ "moment-holiday": "^1.5.1",
"morgan": "^1.9.0",
"mustache": "^3.0.0",
"nodemailer": "^4.6.8",
diff --git a/server/app.js b/server/app.js
index d7eede7..ca98062 100755
--- a/server/app.js
+++ b/server/app.js
@@ -263,6 +263,7 @@ app.use(basePath, express.static(picturesPath, { index: false }));
app.use(basePath + "api/v1/photos", require("./routes/photos"));
app.use(basePath + "api/v1/days", require("./routes/days"));
app.use(basePath + "api/v1/albums", require("./routes/albums"));
+app.use(basePath + "api/v1/holidays", require("./routes/holidays"));
app.use(basePath + "api/v1/scan", require("./routes/scan")(scanner));
/* Declare the "catch all" index route last; the final route is a 404 dynamic router */
diff --git a/server/lib/pascha.js b/server/lib/pascha.js
new file mode 100644
index 0000000..74692dd
--- /dev/null
+++ b/server/lib/pascha.js
@@ -0,0 +1,108 @@
+//! moment-holiday.js locale configuration
+//! locale : pascha Related Holidays
+//! author : Kodie Grantham : https://github.com/kodie
+
+//(function() {
+// var moment = (typeof require !== 'undefined' && require !== null) && !require.amd ? require('moment') : this.moment;
+function init(moment) {
+// moment.holidays.pascha = {
+ moment.modifyHolidays.add({
+ "Lent": {
+ date: 'pascha-46|pascha-3'
+ },
+ /*
+ "Holy Monday": {
+ date: 'pascha-6',
+ keywords_y: ['great', 'monday']
+ },
+ "Holy Tuesday": {
+ date: 'pascha-5',
+ keywords_y: ['great', 'tuesday']
+ },
+ "Holy Wednesday": {
+ date: 'pascha-4',
+ keywords_y: ['great', 'wednesday']
+ },
+ "Holy Thursday": {
+ date: 'pascha-3',
+ keywords_y: ['great', 'thursday']
+ },
+ "Holy Friday": {
+ date: 'pascha-2',
+ keywords_y: ['great', 'friday']
+ },
+ "Holy Saturday": {
+ date: 'pascha-1',
+ keywords_y: ['holy', 'saturday']
+ },
+ */
+ "Pascha Sunday": {
+ date: 'pascha',
+ keywords_y: ['pascha'],
+ keywords: ['sunday']
+ },
+ "Bright Week": {
+ date: 'pascha+1|pascha+6'
+ },
+ "Pentecost Sunday": {
+ date: 'pascha+49',
+ keywords_y: ['pentecost'],
+ keywords: ['sunday']
+ },
+ //};
+ });
+
+ /**
+ * Calculates Easter in the Gregorian/Western (Catholic and Protestant) calendar
+ * based on the algorithm by Oudin (1940) from http://www.tondering.dk/claus/cal/easter.php
+ * @returns {array} [int month, int day]
+ */
+ var pascha = function(year) {
+ var f = Math.floor,
+ // Golden Number - 1
+ G = year % 19,
+ C = f(year / 100),
+ // related to Epact
+ H = (C - f(C / 4) - f((8 * C + 13)/25) + 19 * G + 15) % 30,
+ // number of days from 21 March to the Paschal full moon
+ I = H - f(H/28) * (1 - f(29/(H + 1)) * f((21-G)/11)),
+ // weekday for the Paschal full moon
+ J = (year + f(year / 4) + I + 2 - C + f(C / 4)) % 7,
+ // number of days from 21 March to the Sunday on or before the Paschal full moon
+ L = I - J,
+ month = 3 + f((L + 40)/44),
+ day = L + 28 - 31 * f(month / 4);
+
+ return moment([year, (month - 1),day]);
+ }
+
+ moment.modifyHolidays.extendParser(function(m, date){
+ if (~date.indexOf('pascha')) {
+ var dates = date.split('|');
+ var ds = [];
+
+ for (var i = 0; i < dates.length; i++) {
+ if (dates[i].substring(0, 6) === 'pascha') {
+ var e = pascha(m.year());
+
+ if (dates[i].charAt(6) === '-') { e.subtract(dates[i].substring(7), 'days'); }
+ if (dates[i].charAt(6) === '+') { e.add(dates[i].substring(7), 'days'); }
+
+ if (dates.length === 1) { return e; }
+ ds.push(e.format('M/D'));
+ } else {
+ ds.push(dates[i]);
+ }
+ }
+
+ if (ds.length) { return ds.join('|'); }
+ }
+ });
+
+ console.log("Pascha initialized");
+}
+
+module.exports = init;
+
+// if ((typeof module !== 'undefined' && module !== null ? module.exports : void 0) != null) { module.exports = moment; }
+//}).call(this);
\ No newline at end of file
diff --git a/server/routes/holidays.js b/server/routes/holidays.js
new file mode 100644
index 0000000..3340a29
--- /dev/null
+++ b/server/routes/holidays.js
@@ -0,0 +1,49 @@
+"use strict";
+
+const express = require("express"),
+ moment = require("moment-holiday");
+
+require("../lib/pascha.js")(moment);
+
+const router = express.Router();
+
+/* Remove the western Easter dates, except for Easter itself */
+[ 'Good Friday' ].forEach(function(holiday) {
+ moment.modifyHolidays.remove(holiday);
+});
+
+router.get("/", function(req, res/*, next*/) {
+ let holidays = [], skip = {};
+
+
+ moment("2000-01-01", "YYYY-MM-DD").holidaysBetween("2000-12-31").forEach(function(holiday) {
+ /* Dates with multiple holidays will return an array of items */
+ let names = holiday.isHoliday();
+ if (!Array.isArray(names)) {
+ names = [ names ];
+ }
+ names.forEach(function(name) {
+ if (name in skip) {
+ return;
+ }
+
+ /* If this holiday already exists, remove it from the holidays list
+ * as we only want single day events returned, and add to the 'skip'
+ * list */
+ let index = holidays.indexOf(name);
+ if (index != -1) {
+ holidays.splice(index, 1);
+ skip[name] = true;
+ } else {
+ holidays.push(name);
+ }
+ });
+ });
+
+ return res.status(200).send({
+ holidays: holidays,
+ next: moment().nextHoliday().isHoliday()
+ });
+});
+
+module.exports = router;
diff --git a/server/routes/photos.js b/server/routes/photos.js
index 0b5ed48..75f7459 100755
--- a/server/routes/photos.js
+++ b/server/routes/photos.js
@@ -3,11 +3,13 @@
const express = require("express"),
fs = require("fs"),
config = require("config"),
- moment = require("moment"),
+ moment = require("moment-holiday"),
crypto = require("crypto"),
util = require("util"),
Promise = require("bluebird");
+require("../lib/pascha.js")(moment);
+
const execFile = util.promisify(require("child_process").execFile);
let photoDB;
@@ -538,56 +540,40 @@ router.delete("/:id?", function(req, res/*, next*/) {
});
});
-router.get("/thanksgiving", function(req, res/*, next*/) {
- let thanksgiving = [
- "1995-11-23",
- "1996-11-28",
- "1997-11-27",
- "1998-11-26",
- "1999-11-25",
- "2000-11-23",
- "2001-11-22",
- "2002-11-28",
- "2003-11-27",
- "2004-11-25",
- "2005-11-24",
- "2006-11-23",
- "2007-11-22",
- "2008-11-27",
- "2009-11-26",
- "2010-11-25",
- "2011-11-24",
- "2012-11-22",
- "2013-11-28",
- "2014-11-27",
- "2015-11-26",
- "2016-11-24",
- "2017-11-23",
- "2018-11-22",
- "2019-11-28",
- "2020-11-26"
- ];
+router.get("/holiday/:holiday", function(req, res/*, next*/) {
+ let startYear = 1990,
+ dayIsHoliday = "",
+ holidayName;
- let dayIsThanksgiving = "";
- thanksgiving.forEach(function(date) {
- let comparison = "strftime('%Y-%m-%d',taken)='" + date + "'";
- if (!dayIsThanksgiving) {
- dayIsThanksgiving = comparison;
- } else {
- dayIsThanksgiving += " OR " + comparison;
+ let lookup = moment().holidays([req.params.holiday]);
+ if (!lookup) {
+ return res.status(404).send(req.params.holiday + " holiday not found.");
+ }
+ holidayName = Object.getOwnPropertyNames(lookup)[0];
+
+ for (let year = startYear; year <= moment().year(); year++) {
+ let holiday = moment(year + "-01-01", "YYYY-MM-DD").holiday(req.params.holiday);
+ if (!holiday) {
+ /* 'Leap Year' doesn't exist every year... */
+ continue;
}
- });
+
+ let comparison = "strftime('%Y-%m-%d',taken)='" + holiday.format("YYYY-MM-DD") + "'"
+ if (!dayIsHoliday) {
+ dayIsHoliday = comparison;
+ } else {
+ dayIsHoliday += " OR " + comparison;
+ }
+ }
let query = "SELECT photos.*,albums.path AS path FROM photos " +
"INNER JOIN albums ON (albums.id=photos.albumId) " +
- "WHERE (photos.duplicate=0 AND photos.deleted=0 AND photos.scanned NOT NULL AND (" + dayIsThanksgiving + ")) " +
+ "WHERE (photos.duplicate=0 AND photos.deleted=0 AND photos.scanned NOT NULL AND (" + dayIsHoliday + ")) " +
"ORDER BY strftime('%Y-%m-%d', taken) DESC,id DESC";
return photoDB.sequelize.query(query, {
type: photoDB.Sequelize.QueryTypes.SELECT
}).then(function(photos) {
- console.log(query);
- console.log(JSON.stringify(photos));
photos.forEach(function(photo) {
for (var key in photo) {
if (photo[key] instanceof Date) {
@@ -597,6 +583,7 @@ router.get("/thanksgiving", function(req, res/*, next*/) {
});
return res.status(200).json({
+ holiday: holidayName,
items: photos
});