532 lines
23 KiB
JavaScript
532 lines
23 KiB
JavaScript
|
|
/*
|
||
|
|
(PLEASE DO NOT DELETE THIS HEADER OR CREDIT!)
|
||
|
|
|
||
|
|
User customizable settings below!
|
||
|
|
Please refer to my guide over on https://virtualobserver.moe/ayano/comment-widget if you're confused on how to use this.
|
||
|
|
The IDs at the top are a requirement but everything else is optional!
|
||
|
|
Do not delete any settings even if you aren't using them! It could break the program.
|
||
|
|
|
||
|
|
After filling out your options, just paste this anywhere you want a comment section
|
||
|
|
(But change the script src URL to wherever you have this widget stored on your site!)
|
||
|
|
|
||
|
|
<div id="c_widget"></div>
|
||
|
|
<script src="comment-widget.js"></script>
|
||
|
|
|
||
|
|
Have fun! Bug reports are encouraged if you happen to run into any issues.
|
||
|
|
- Ayano (https://virtualobserver.moe/)
|
||
|
|
*/
|
||
|
|
|
||
|
|
// The values in this section are REQUIRED for the widget to work! Keep them in quotes!
|
||
|
|
const s_stylePath = '/comment-widget-pink.css';
|
||
|
|
const s_formId = '1FAIpQLSeoXQa-1tl1fy5T-gEByjFwzMKcdMIr2WWxoxzfDDbcFbhFsg';
|
||
|
|
const s_nameId = '1472166240';
|
||
|
|
const s_websiteId = '1345273360';
|
||
|
|
const s_textId = '1075384199';
|
||
|
|
const s_pageId = '975383655';
|
||
|
|
const s_replyId = '1265170837';
|
||
|
|
const s_sheetId = '1Y08cI8TPKlPznTVK1Vo_DfwuVW9m8jboZswDz76boL4';
|
||
|
|
|
||
|
|
// The values below are necessary for accurate timestamps, I've filled it in with EST as an example
|
||
|
|
const s_timezone = -5; // Your personal timezone (Example: UTC-5:00 is -5 here, UTC+10:30 would be 10.5)
|
||
|
|
const s_daylightSavings = true; // If your personal timezone uses DST, set this to true
|
||
|
|
// For the dates DST start and end where you live: [Month, Weekday, which number of that weekday, hour (24 hour time)]
|
||
|
|
const s_dstStart = ['March', 'Sunday', 2, 2]; // Example shown is the second Sunday of March at 2:00 am
|
||
|
|
const s_dstEnd = ['November', 'Sunday', 1, 2]; // Example shown is the first Sunday of November at 2:00 am
|
||
|
|
|
||
|
|
// Misc - Other random settings
|
||
|
|
const s_commentsPerPage = 5; // The max amount of comments that can be displayed on one page, any number >= 1 (Replies not counted)
|
||
|
|
const s_maxLength = 800; // The max character length of a comment
|
||
|
|
const s_maxLengthName = 16; // The max character length of a name
|
||
|
|
const s_commentsOpen = true; // Change to false if you'd like to close your comment section site-wide (Turn it off on Google Forms too!)
|
||
|
|
const s_collapsedReplies = true; // True for collapsed replies with a button, false for replies to display automatically
|
||
|
|
const s_longTimestamp = false; // True for a date + time, false for just the date
|
||
|
|
let s_includeUrlParameters = false; // Makes new comment sections on pages with URL parameters when set to true (If you don't know what this does, leave it disabled)
|
||
|
|
const s_fixRarebitIndexPage = false; // If using Rarebit, change to true to make the index page and page 1 of your webcomic have the same comment section
|
||
|
|
|
||
|
|
// Word filter - Censor profanity, etc
|
||
|
|
const s_wordFilterOn = false; // True for on, false for off
|
||
|
|
const s_filterReplacement = '****'; // Change what filtered words are censored with (**** is the default)
|
||
|
|
const s_filteredWords = [ // Add words to filter by putting them in quotes and separating with commas (ie. 'heck', 'dang')
|
||
|
|
'heck', 'dang'
|
||
|
|
]
|
||
|
|
|
||
|
|
// Text - Change what messages/text appear on the form and in the comments section (Mostly self explanatory)
|
||
|
|
const s_widgetTitle = 'Leave a comment!';
|
||
|
|
const s_nameFieldLabel = 'Name';
|
||
|
|
const s_websiteFieldLabel = 'Website (Optional)';
|
||
|
|
const s_textFieldLabel = '';
|
||
|
|
const s_submitButtonLabel = 'Submit';
|
||
|
|
const s_loadingText = 'Loading comments...';
|
||
|
|
const s_noCommentsText = 'No comments yet!';
|
||
|
|
const s_closedCommentsText = 'Comments are closed temporarily!';
|
||
|
|
const s_websiteText = 'Website'; // The links to websites left by users on their comments
|
||
|
|
const s_replyButtonText = 'Reply'; // The button for replying to someone
|
||
|
|
const s_replyingText = 'Replying to'; // The text that displays while the user is typing a reply
|
||
|
|
const s_expandRepliesText = 'Show Replies';
|
||
|
|
const s_leftButtonText = '<<';
|
||
|
|
const s_rightButtonText = '>>';
|
||
|
|
|
||
|
|
/*
|
||
|
|
DO NOT edit below this point unless you are confident you know what you're doing!
|
||
|
|
Everything else is automatic, you don't have to change anything else. ^^
|
||
|
|
However, feel free to edit this code as much as you like! Just please don't remove my credit if possible <3
|
||
|
|
*/
|
||
|
|
|
||
|
|
// Fix the URL parameters setting for Rarebit just in case
|
||
|
|
if (s_fixRarebitIndexPage) {s_includeUrlParameters = true}
|
||
|
|
|
||
|
|
// Apply CSS
|
||
|
|
const c_cssLink = document.createElement('link');
|
||
|
|
c_cssLink.type = 'text/css';
|
||
|
|
c_cssLink.rel = 'stylesheet';
|
||
|
|
c_cssLink.href = s_stylePath;
|
||
|
|
document.getElementsByTagName('head')[0].appendChild(c_cssLink);
|
||
|
|
|
||
|
|
// HTML Form
|
||
|
|
const v_mainHtml = `
|
||
|
|
<div id="c_inputDiv">
|
||
|
|
<form id="c_form" onsubmit="c_submitButton.disabled = true; v_submitted = true;" method="post" target="c_hiddenIframe" action="https://docs.google.com/forms/d/e/${s_formId}/formResponse"></form>
|
||
|
|
</div>
|
||
|
|
<div id="c_container">${s_loadingText}</div>
|
||
|
|
`;
|
||
|
|
const v_formHtml = `
|
||
|
|
<h2 id="c_widgetTitle">${s_widgetTitle}</h2>
|
||
|
|
|
||
|
|
<div id="c_nameWrapper" class="c-inputWrapper">
|
||
|
|
<label class="c-label c-nameLabel" for="entry.${s_nameId}">${s_nameFieldLabel}</label>
|
||
|
|
<input class="c-input c-nameInput" name="entry.${s_nameId}" id="entry.${s_nameId}" type="text" maxlength="${s_maxLengthName}" required>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div id="c_websiteWrapper" class="c-inputWrapper">
|
||
|
|
<label class="c-label c-websiteLabel" for="entry.${s_websiteId}">${s_websiteFieldLabel}</label>
|
||
|
|
<input class="c-input c-websiteInput" name="entry.${s_websiteId}" id="entry.${s_websiteId}" type="url" pattern="https://.*">
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div id="c_textWrapper" class="c-inputWrapper">
|
||
|
|
<label class="c-label c-textLabel" for="entry.${s_textId}">${s_textFieldLabel}</label>
|
||
|
|
<textarea class="c-input c-textInput" name="entry.${s_textId}" id="entry.${s_textId}" rows="4" cols="50" maxlength="${s_maxLength}" required></textarea>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<input id="c_submitButton" name="c_submitButton" type="submit" value="${s_submitButtonLabel}" disabled>
|
||
|
|
`;
|
||
|
|
|
||
|
|
// Insert main HTML to page
|
||
|
|
document.getElementById('c_widget').innerHTML = v_mainHtml;
|
||
|
|
const c_form = document.getElementById('c_form');
|
||
|
|
if (s_commentsOpen) {c_form.innerHTML = v_formHtml}
|
||
|
|
else {c_form.innerHTML = s_closedCommentsText}
|
||
|
|
|
||
|
|
// Initialize misc things
|
||
|
|
const c_container = document.getElementById('c_container');
|
||
|
|
let v_pageNum = 1;
|
||
|
|
let v_amountOfPages = 1;
|
||
|
|
let v_commentMax = 1;
|
||
|
|
let v_commentMin = 1;
|
||
|
|
|
||
|
|
// Set up the word filter if applicable
|
||
|
|
let v_filteredWords;
|
||
|
|
if (s_wordFilterOn) {
|
||
|
|
v_filteredWords = s_filteredWords.join('|');
|
||
|
|
v_filteredWords = new RegExp(String.raw `\b(${v_filteredWords})\b`, 'ig');
|
||
|
|
}
|
||
|
|
|
||
|
|
// The fake button is just a dummy placeholder for when comments are closed
|
||
|
|
let c_submitButton;
|
||
|
|
if (s_commentsOpen) {c_submitButton = document.getElementById('c_submitButton')}
|
||
|
|
else {c_submitButton = document.createElement('button')}
|
||
|
|
|
||
|
|
// Add invisible page input to document
|
||
|
|
let v_pagePath = window.location.pathname;
|
||
|
|
if (s_includeUrlParameters) {v_pagePath += window.location.search}
|
||
|
|
if (s_fixRarebitIndexPage && v_pagePath == '/') {v_pagePath = '/?pg=1'}
|
||
|
|
const c_pageInput = document.createElement('input');
|
||
|
|
c_pageInput.value = v_pagePath; c_pageInput.type = 'text'; c_pageInput.style.display = 'none';
|
||
|
|
c_pageInput.id = 'entry.' + s_pageId; c_pageInput.name = c_pageInput.id;
|
||
|
|
c_form.appendChild(c_pageInput);
|
||
|
|
|
||
|
|
// Add the "Replying to..." text to document
|
||
|
|
let c_replyingText = document.createElement('span');
|
||
|
|
c_replyingText.style.display = 'none'; c_replyingText.id = 'c_replyingText';
|
||
|
|
c_form.appendChild(c_replyingText);
|
||
|
|
c_replyingText = document.getElementById('c_replyingText');
|
||
|
|
|
||
|
|
// Add the invisible reply input to document
|
||
|
|
let c_replyInput = document.createElement('input');
|
||
|
|
c_replyInput.type = 'text'; c_replyInput.style.display = 'none';
|
||
|
|
c_replyInput.id = 'entry.' + s_replyId; c_replyInput.name = c_replyInput.id;
|
||
|
|
c_form.appendChild(c_replyInput);
|
||
|
|
c_replyInput = document.getElementById('entry.' + s_replyId);
|
||
|
|
|
||
|
|
// Add the invisible iFrame to the document for catching the default Google Forms submisson page
|
||
|
|
let v_submitted = false;
|
||
|
|
let c_hiddenIframe = document.createElement('iframe');
|
||
|
|
c_hiddenIframe.id = 'c_hiddenIframe'; c_hiddenIframe.name = 'c_hiddenIframe'; c_hiddenIframe.style.display = 'none'; c_hiddenIframe.setAttribute('onload', 'if(v_submitted){fixFrame()}');
|
||
|
|
c_form.appendChild(c_hiddenIframe);
|
||
|
|
c_hiddenIframe = document.getElementById('c_hiddenIframe');
|
||
|
|
|
||
|
|
// Fix the invisible iFrame so it doesn't keep trying to load stuff
|
||
|
|
function fixFrame() {
|
||
|
|
v_submitted = false;
|
||
|
|
c_hiddenIframe.srcdoc = '';
|
||
|
|
getComments(); // Reload comments after submission
|
||
|
|
}
|
||
|
|
|
||
|
|
// Processes comment data with the Google Sheet ID
|
||
|
|
function getComments() {
|
||
|
|
// Disable the submit button while comments are reloaded
|
||
|
|
c_submitButton.disabled;
|
||
|
|
|
||
|
|
// Reset reply stuff to default
|
||
|
|
c_replyingText.style.display = 'none';
|
||
|
|
c_replyInput.value = '';
|
||
|
|
|
||
|
|
// Clear input fields too
|
||
|
|
if (s_commentsOpen) {
|
||
|
|
document.getElementById(`entry.${s_nameId}`).value = '';
|
||
|
|
document.getElementById(`entry.${s_websiteId}`).value = '';
|
||
|
|
document.getElementById(`entry.${s_textId}`).value = '';
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get the data
|
||
|
|
const url = `https://docs.google.com/spreadsheets/d/${s_sheetId}/gviz/tq?`;
|
||
|
|
const retrievedSheet = getSheet(url);
|
||
|
|
|
||
|
|
// Do stuff with the data here
|
||
|
|
retrievedSheet.then(result => {
|
||
|
|
// The data comes with extra stuff at the beginning, get rid of it
|
||
|
|
const json = JSON.parse(result.split('\n')[1].replace(/google.visualization.Query.setResponse\(|\);/g, ''));
|
||
|
|
|
||
|
|
// Need index of page column for checking if comments are for the right page
|
||
|
|
const isPage = (col) => col.label == 'Page';
|
||
|
|
let pageIdx = json.table.cols.findIndex(isPage);
|
||
|
|
|
||
|
|
// Turn that data into usable comment data
|
||
|
|
// All of the messy val checks are because Google Sheets can be weird sometimes with comment deletion
|
||
|
|
let comments = [];
|
||
|
|
if (json.table.parsedNumHeaders > 0) { // Check if any comments exist in the sheet at all before continuing
|
||
|
|
for (r = 0; r < json.table.rows.length; r++) {
|
||
|
|
// Check for null rows
|
||
|
|
let val1;
|
||
|
|
if (!json.table.rows[r].c[pageIdx]) {val1 = ''}
|
||
|
|
else {val1 = json.table.rows[r].c[pageIdx].v}
|
||
|
|
|
||
|
|
// Check if the page name matches before adding to comment array
|
||
|
|
if (val1 == v_pagePath) {
|
||
|
|
let comment = {}
|
||
|
|
for (c = 0; c < json.table.cols.length; c++) {
|
||
|
|
// Check for null values
|
||
|
|
let val2;
|
||
|
|
if (!json.table.rows[r].c[c]) {val2 = ''}
|
||
|
|
else {val2 = json.table.rows[r].c[c].v}
|
||
|
|
|
||
|
|
// Finally set the value properly
|
||
|
|
comment[json.table.cols[c].label] = val2;
|
||
|
|
}
|
||
|
|
comment.Timestamp2 = json.table.rows[r].c[0].f;
|
||
|
|
comments.push(comment);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check for empty comments before displaying to page
|
||
|
|
if (comments.length == 0 || Object.keys(comments[0]).length < 2) { // Once again, Google Sheets can be weird
|
||
|
|
c_container.innerHTML = s_noCommentsText;
|
||
|
|
} else {displayComments(comments)}
|
||
|
|
|
||
|
|
c_submitButton.disabled = false // Now that everything is done, re-enable the submit button
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
// Fetches the Google Sheet resource from the provided URL
|
||
|
|
function getSheet(url) {
|
||
|
|
return new Promise(function (resolve, reject) {
|
||
|
|
fetch(url).then(response => {
|
||
|
|
if (!response.ok) {reject('Could not find Google Sheet with that URL')} // Checking for a 404
|
||
|
|
else {
|
||
|
|
response.text().then(data => {
|
||
|
|
if (!data) {reject('Invalid data pulled from sheet')}
|
||
|
|
resolve(data);
|
||
|
|
})
|
||
|
|
}
|
||
|
|
})
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
// Displays comments on page
|
||
|
|
let a_commentDivs = []; // For use in other functions
|
||
|
|
function displayComments(comments) {
|
||
|
|
// Clear for re-display
|
||
|
|
a_commentDivs = [];
|
||
|
|
c_container.innerHTML = '';
|
||
|
|
|
||
|
|
// Get all reply comments by taking them out of the comment array
|
||
|
|
let replies = [];
|
||
|
|
for (i = 0; i < comments.length; i++) {
|
||
|
|
if (comments[i].Reply) {
|
||
|
|
replies.push(comments[i]);
|
||
|
|
comments.splice(i, 1);
|
||
|
|
i--;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Values for pagination
|
||
|
|
v_amountOfPages = Math.ceil(comments.length / s_commentsPerPage);
|
||
|
|
v_commentMax = s_commentsPerPage * v_pageNum;
|
||
|
|
v_commentMin = v_commentMax - s_commentsPerPage;
|
||
|
|
|
||
|
|
// Main comments (not replies)
|
||
|
|
comments.reverse(); // Newest comments go to top
|
||
|
|
for (i = 0; i < comments.length; i++) {
|
||
|
|
let comment = createComment(comments[i]);
|
||
|
|
|
||
|
|
// Reply button
|
||
|
|
let button = document.createElement('button');
|
||
|
|
button.innerHTML = s_replyButtonText;
|
||
|
|
button.value = comment.id;
|
||
|
|
button.setAttribute('onclick', `openReply(this.value)`);
|
||
|
|
button.className = 'c-replyButton';
|
||
|
|
comment.appendChild(button);
|
||
|
|
|
||
|
|
// Choose whether to display or not based on page number
|
||
|
|
comment.style.display = 'none';
|
||
|
|
if (i >= v_commentMin && i < v_commentMax) {comment.style.display = 'block'}
|
||
|
|
|
||
|
|
comment.className = 'c-comment';
|
||
|
|
c_container.appendChild(comment);
|
||
|
|
a_commentDivs.push(document.getElementById(comment.id)); // Add to array for use later
|
||
|
|
}
|
||
|
|
|
||
|
|
// Replies
|
||
|
|
for (i = 0; i < replies.length; i++) {
|
||
|
|
let reply = createComment(replies[i]);
|
||
|
|
const parentId = replies[i].Reply;
|
||
|
|
const parentDiv = document.getElementById(parentId);
|
||
|
|
|
||
|
|
// Check if a container doesn't already exist for this comment, if not, make one
|
||
|
|
let container;
|
||
|
|
if (!document.getElementById(parentId + '-replies')) {
|
||
|
|
container = document.createElement('div');
|
||
|
|
container.id = parentId + '-replies';
|
||
|
|
if (s_collapsedReplies) {container.style.display = 'none'} // Default to hidden if collapsed
|
||
|
|
container.className = 'c-replyContainer';
|
||
|
|
parentDiv.appendChild(container);
|
||
|
|
} else {container = document.getElementById(parentId + '-replies')}
|
||
|
|
reply.className = 'c-reply';
|
||
|
|
container.appendChild(reply);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Handle adding the buttons to show or hide replies if collapsed replies are enabled
|
||
|
|
if (s_collapsedReplies) {
|
||
|
|
const containers = document.getElementsByClassName('c-replyContainer');
|
||
|
|
for (i = 0; i < containers.length; i++) {
|
||
|
|
const num = containers[i].childNodes.length;
|
||
|
|
const parentDiv = containers[i].parentElement;
|
||
|
|
|
||
|
|
// The button to expand replies
|
||
|
|
const button = document.createElement('button');
|
||
|
|
button.innerHTML = s_expandRepliesText + ` (${num})`;
|
||
|
|
button.setAttribute('onclick', `expandReplies(this.parentElement.id)`);
|
||
|
|
button.className = 'c-expandButton';
|
||
|
|
parentDiv.insertBefore(button, parentDiv.lastChild);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Handle pagination if there's more than one page
|
||
|
|
if (v_amountOfPages > 1) {
|
||
|
|
let pagination = document.createElement('div');
|
||
|
|
|
||
|
|
leftButton = document.createElement('button');
|
||
|
|
leftButton.innerHTML = s_leftButtonText; leftButton.id = 'c_leftButton'; leftButton.name = 'left';
|
||
|
|
leftButton.setAttribute('onclick', `changePage(this.name)`);
|
||
|
|
if (v_pageNum == 1) {leftButton.disabled = true} // Can't go before page 1
|
||
|
|
leftButton.className = 'c-paginationButton';
|
||
|
|
pagination.appendChild(leftButton);
|
||
|
|
|
||
|
|
rightButton = document.createElement('button');
|
||
|
|
rightButton.innerHTML = s_rightButtonText; rightButton.id = 'c_rightButton'; rightButton.name = 'right';
|
||
|
|
rightButton.setAttribute('onclick', `changePage(this.name)`);
|
||
|
|
if (v_pageNum == v_amountOfPages) {rightButton.disabled = true} // Can't go after the last page
|
||
|
|
rightButton.className = 'c-paginationButton';
|
||
|
|
pagination.appendChild(rightButton);
|
||
|
|
|
||
|
|
pagination.id = 'c_pagination';
|
||
|
|
c_container.appendChild(pagination);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Create basic HTML comment, reply or not
|
||
|
|
function createComment(data) {
|
||
|
|
let comment = document.createElement('div');
|
||
|
|
|
||
|
|
// Get the right timestamps
|
||
|
|
let timestamps = convertTimestamp(data.Timestamp);
|
||
|
|
let timestamp;
|
||
|
|
if (s_longTimestamp) {timestamp = timestamps[0]}
|
||
|
|
else {timestamp = timestamps[1]}
|
||
|
|
|
||
|
|
// Set the ID (uses Name + Full Timestamp format)
|
||
|
|
const id = data.Name + '|--|' + data.Timestamp2;
|
||
|
|
comment.id = id;
|
||
|
|
|
||
|
|
// Name of user
|
||
|
|
let name = document.createElement('h3');
|
||
|
|
let filteredName = data.Name;
|
||
|
|
if (s_wordFilterOn) {filteredName = filteredName.replace(v_filteredWords, s_filterReplacement)}
|
||
|
|
name.innerText = filteredName;
|
||
|
|
name.className = 'c-name';
|
||
|
|
comment.appendChild(name);
|
||
|
|
|
||
|
|
// Timestamp
|
||
|
|
let time = document.createElement('span');
|
||
|
|
time.innerText = timestamp;
|
||
|
|
time.className = 'c-timestamp';
|
||
|
|
comment.appendChild(time);
|
||
|
|
|
||
|
|
// Website URL, if one was provided
|
||
|
|
if (data.Website) {
|
||
|
|
let site = document.createElement('a');
|
||
|
|
site.innerText = s_websiteText;
|
||
|
|
site.href = data.Website;
|
||
|
|
site.className = 'c-site';
|
||
|
|
comment.appendChild(site);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Text content
|
||
|
|
let text = document.createElement('p');
|
||
|
|
let filteredText = data.Text;
|
||
|
|
if (s_wordFilterOn) {filteredText = filteredText.replace(v_filteredWords, s_filterReplacement)}
|
||
|
|
text.innerText = filteredText;
|
||
|
|
text.className = 'c-text';
|
||
|
|
comment.appendChild(text);
|
||
|
|
|
||
|
|
return comment;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Makes the Google Sheet timestamp usable
|
||
|
|
function convertTimestamp(timestamp) {
|
||
|
|
const vals = timestamp.split('(')[1].split(')')[0].split(',');
|
||
|
|
const date = new Date(vals[0], vals[1], vals[2], vals[3], vals[4], vals[5]);
|
||
|
|
const timezoneDiff = (s_timezone * 60 + date.getTimezoneOffset()) * -1;
|
||
|
|
let offsetDate = new Date(date.getTime() + timezoneDiff * 60 * 1000);
|
||
|
|
if (s_daylightSavings) {offsetDate = isDST(offsetDate)}
|
||
|
|
return [offsetDate.toLocaleString(), offsetDate.toLocaleDateString()];
|
||
|
|
}
|
||
|
|
// DST checker
|
||
|
|
function isDST(date) {
|
||
|
|
const dstStart = [getMonthNum(s_dstStart[0]), getDayNum(s_dstStart[1]), s_dstStart[2], s_dstStart[3]];
|
||
|
|
const dstEnd = [getMonthNum(s_dstEnd[0]), getDayNum(s_dstEnd[1]), s_dstEnd[2], s_dstEnd[3]];
|
||
|
|
|
||
|
|
const year = date.getFullYear();
|
||
|
|
let startDate = new Date(year, dstStart[0], 1);
|
||
|
|
startDate = nthDayOfMonth(dstStart[1], dstStart[2], startDate, dstStart[3]).getTime();
|
||
|
|
let endDate = new Date(year, dstEnd[0], 1);
|
||
|
|
endDate = nthDayOfMonth(dstEnd[1], dstEnd[2], endDate, dstEnd[3]).getTime();
|
||
|
|
time = date.getTime();
|
||
|
|
|
||
|
|
if (time >= startDate && time < endDate) {date.setHours(date.getHours() - 1)}
|
||
|
|
return date;
|
||
|
|
}
|
||
|
|
// Thank you to https://stackoverflow.com/questions/32192982/get-a-given-weekday-in-a-given-month-with-javascript for the below function
|
||
|
|
function nthDayOfMonth(day, n, date, hour) {
|
||
|
|
var count = 0;
|
||
|
|
var idate = new Date(date);
|
||
|
|
idate.setDate(1);
|
||
|
|
while ((count) < n) {
|
||
|
|
idate.setDate(idate.getDate() + 1);
|
||
|
|
if (idate.getDay() == day) {
|
||
|
|
count++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
idate.setHours(hour);
|
||
|
|
return idate;
|
||
|
|
}
|
||
|
|
// Convert weekday and month names into numbers
|
||
|
|
function getDayNum(day) {
|
||
|
|
let num;
|
||
|
|
switch (day.toLowerCase()) {
|
||
|
|
case 'sunday': num = 0; break;
|
||
|
|
case 'monday': num = 1; break;
|
||
|
|
case 'tuesday': num = 2; break;
|
||
|
|
case 'wednesday': num = 3; break;
|
||
|
|
case 'thursday': num = 4; break;
|
||
|
|
case 'friday': num = 5; break;
|
||
|
|
case 'saturday': num = 6; break;
|
||
|
|
default: num = 0; break;
|
||
|
|
}
|
||
|
|
return num;
|
||
|
|
}
|
||
|
|
function getMonthNum(month) {
|
||
|
|
let num;
|
||
|
|
switch (month.toLowerCase()) {
|
||
|
|
case 'january': num = 0; break;
|
||
|
|
case 'february': num = 1; break;
|
||
|
|
case 'march': num = 2; break;
|
||
|
|
case 'april': num = 3; break;
|
||
|
|
case 'may': num = 4; break;
|
||
|
|
case 'june': num = 5; break;
|
||
|
|
case 'july': num = 6; break;
|
||
|
|
case 'august': num = 7; break;
|
||
|
|
case 'september': num = 8; break;
|
||
|
|
case 'october': num = 9; break;
|
||
|
|
case 'november': num = 10; break;
|
||
|
|
case 'december': num = 11; break;
|
||
|
|
}
|
||
|
|
return num;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Handle making replies
|
||
|
|
const link = document.createElement('a');
|
||
|
|
link.href = '#c_inputDiv';
|
||
|
|
function openReply(id) {
|
||
|
|
if (c_replyingText.style.display == 'none') {
|
||
|
|
c_replyingText.innerHTML = s_replyingText + ` ${id.split('|--|')[0]}...`;
|
||
|
|
c_replyInput.value = id;
|
||
|
|
c_replyingText.style.display = 'block';
|
||
|
|
} else {
|
||
|
|
c_replyingText.innerHTML = '';
|
||
|
|
c_replyInput.value = '';
|
||
|
|
c_replyingText.style.display = 'none';
|
||
|
|
}
|
||
|
|
link.click(); // Jump to the space to type
|
||
|
|
}
|
||
|
|
|
||
|
|
// Handle expanding replies (should only be accessible with collapsed replies enabled)
|
||
|
|
function expandReplies(id) {
|
||
|
|
const targetDiv = document.getElementById(`${id}-replies`);
|
||
|
|
if (targetDiv.style.display == 'none') {targetDiv.style.display = 'block'}
|
||
|
|
else {targetDiv.style.display = 'none'}
|
||
|
|
}
|
||
|
|
|
||
|
|
function changePage(dir) {
|
||
|
|
const leftButton = document.getElementById('c_leftButton');
|
||
|
|
const rightButton = document.getElementById('c_rightButton');
|
||
|
|
|
||
|
|
// Find directional number
|
||
|
|
let num;
|
||
|
|
switch (dir) {
|
||
|
|
case 'left': num = -1; break;
|
||
|
|
case 'right': num = 1; break;
|
||
|
|
default: num = 0; break;
|
||
|
|
}
|
||
|
|
let targetPage = v_pageNum + num;
|
||
|
|
|
||
|
|
// Cancel if impossible direction for safety, should never happen though
|
||
|
|
if (targetPage > v_amountOfPages || targetPage < 1) {return}
|
||
|
|
|
||
|
|
// Enable/disable buttons if needed
|
||
|
|
leftButton.disabled = false; rightButton.disabled = false;
|
||
|
|
if (targetPage == 1) {leftButton.disabled = true} // Can't go before page 1
|
||
|
|
if (targetPage == v_amountOfPages) {rightButton.disabled = true} // Can't go past the last page
|
||
|
|
|
||
|
|
// Hide all comments and then display the correct ones
|
||
|
|
v_pageNum = targetPage;
|
||
|
|
v_commentMax = s_commentsPerPage * v_pageNum;
|
||
|
|
v_commentMin = v_commentMax - s_commentsPerPage;
|
||
|
|
for (i = 0; i < a_commentDivs.length; i++) {
|
||
|
|
a_commentDivs[i].style.display = 'none';
|
||
|
|
if (i >= v_commentMin && i < v_commentMax) {a_commentDivs[i].style.display = 'block'}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
getComments(); // Run once on page load
|