/** * Author: Vera Konigin * Site: https://groundedwren.neocities.org * Contact: vera@groundedwren.com * * File Description: Comments Control */ registerNamespace("GW.Controls", function (ns) { ns.CommentForm = class CommentForm extends HTMLElement { //#region staticProperties static observedAttributes = []; static instanceCount = 0; static instanceMap = {}; //#endregion //#region instance properties instanceId; isInitialized; titleText; discordURL; //#region element properties formEl; titleEl; dispNameInpt; emailInpt; websiteInpt; respToInpt; commentInpt; resetBtn; submitBtn; //#endregion //#endregion constructor() { super(); this.instanceId = CommentForm.instanceCount++; CommentForm.instanceMap[this.instanceId] = this; } get idKey() { return `gw-comment-form-${this.instanceId}`; } //#region HTMLElement implementation connectedCallback() { if (this.isInitialized) { return; } this.titleText = this.getAttribute("titleText") || "Add a Comment"; this.discordURL = this.getAttribute("discordURL") this.renderContent(); this.registerHandlers(); this.isInitialized = true; } //#endregion renderContent() { //Markup this.innerHTML = `
${this.titleText}
Comments are manually approved
`; //element properties this.formEl = document.getElementById(`${this.idKey}-form`); this.titleEl = document.getElementById(`${this.idKey}-title`); this.dispNameInpt = document.getElementById(`${this.idKey}-dispName`); this.emailInpt = document.getElementById(`${this.idKey}-email`); this.websiteInpt = document.getElementById(`${this.idKey}-website`); this.respToInpt = document.getElementById(`${this.idKey}-respTo`); this.commentInpt = document.getElementById(`${this.idKey}-comment`); this.resetBtn = document.getElementById(`${this.idKey}-reset`); this.submitBtn = document.getElementById(`${this.idKey}-submit`); //default values this.dispNameInpt.value = localStorage.getItem("comment-name") || ""; this.emailInpt.value = localStorage.getItem("comment-email") || ""; this.websiteInpt.value = localStorage.getItem("comment-website") || ""; } //#region Handlers registerHandlers() { this.formEl.onsubmit = this.onSubmit; } onSubmit = (event) => { event.preventDefault(); const contentObj = { name: this.dispNameInpt.value, email: this.emailInpt.value, website: this.websiteInpt.value, responseTo: this.respToInpt.value, comment: this.commentInpt.value, timestamp: new Date().toUTCString(), }; const contentAry = []; for (let contentKey in contentObj) { contentAry.push(`${contentKey}=${contentObj[contentKey]}`); } const request = new XMLHttpRequest(); request.open( "POST", this.discordURL, true ); request.setRequestHeader("Content-Type", "application/json"); request.onreadystatechange = function () { if (request.readyState == 4) { console.log(request.responseText); } }; request.send(JSON.stringify({ content: contentAry.join("; ") })); localStorage.setItem("comment-name", contentObj.name); localStorage.setItem("comment-email", contentObj.email); localStorage.setItem("comment-website", contentObj.website); this.formEl.reset(); this.dispNameInpt.value = contentObj.name; this.emailInpt.value = contentObj.email; this.websiteInpt.value = contentObj.website; alert("Your message has been submitted!"); }; //#endregion }; customElements.define("gw-comment-form", ns.CommentForm); ns.CommentList = class CommentList extends HTMLElement { //#region staticProperties static observedAttributes = []; static instanceCount = 0; static instanceMap = {}; //#endregion //#region instance properties instanceId; isInitialized; gSpreadsheetId; gSheetId; isNewestFirst; gwCommentFormId; //#region element properties //#endregion //#endregion constructor() { super(); this.instanceId = CommentList.instanceCount++; CommentList.instanceMap[this.instanceId] = this; } get idKey() { return `gw-comment-list-${this.instanceId}`; } //#region HTMLElement implementation connectedCallback() { if (this.isInitialized) { return; } this.gSpreadsheetId = this.getAttribute("gSpreadsheetId"); this.gSheetId = this.getAttribute("gSheetId"); this.isNewestFirst = this.getAttribute("isNewestFirst"); this.gwCommentFormId = this.getAttribute("gwCommentFormId"); this.loadAndRender(); this.isInitialized = true; } //#endregion async loadAndRender() { this.innerHTML = `
Comments loading....
` const sheetReader = new GW.Gizmos.GoogleSheetsReader(this.gSpreadsheetId, this.gSheetId); const sheetData = await sheetReader.loadData(); this.innerHTML = ""; const allComments = sheetReader.rowData; if (this.isNewestFirst) { allComments.reverse(); } this.renderContent(); this.registerHandlers(); const allCommentsIndex = {}; const topLevelCommentIdxs = []; const childCommentIdxs = []; for (let i = 0; i < allComments.length; i++) { const comment = allComments[i]; allCommentsIndex[comment.ID] = i; if (!comment.ResponseTo) { topLevelCommentIdxs.push(i); } else { childCommentIdxs.push(i); } } childCommentIdxs.forEach(childIdx => { const replyId = allComments[childIdx].ResponseTo; const respondeeComment = allComments[allCommentsIndex[replyId]]; respondeeComment.childrenIdxs = respondeeComment.childrenIdxs || []; respondeeComment.childrenIdxs.push(childIdx); }); let commentsToBuild = []; topLevelCommentIdxs.forEach( topCommentIdx => commentsToBuild.push( { parent: this.containerEl, parentId: null, comment: allComments[topCommentIdx] } ) ); while (commentsToBuild.length > 0) { let { parent, parentId, comment } = commentsToBuild.shift(); if (!comment.Timestamp) { continue; } parent.insertAdjacentHTML("beforeend", ` `); const commentEl = document.getElementById(`${this.idKey}-cmt-${comment.ID}`); (comment.childrenIdxs || []).forEach( childIdx => commentsToBuild.push({ parent: commentEl.articleEl, parentId: comment.ID, comment: allComments[childIdx] }) ); } } renderContent() { //Markup this.innerHTML = `
`; //element properties this.containerEl = document.getElementById(`${this.idKey}-container`); } //#region Handlers registerHandlers() { } //#endregion }; customElements.define("gw-comment-list", ns.CommentList); ns.CommentCard = class CommentCard extends HTMLElement { //#region staticProperties static observedAttributes = []; static instanceCount = 0; static instanceMap = {}; //#endregion //#region instance properties instanceId; isInitialized; commentId; replyToId; commenterName; isoTimestamp; datetime; websiteURL; commentText; gwCommentFormId; //#region element properties articleEl; replyBtn; //#endregion //#endregion constructor() { super(); this.instanceId = CommentCard.instanceCount++; CommentCard.instanceMap[this.instanceId] = this; } get idKey() { return `gw-comment-card-${this.instanceId}`; } //#region HTMLElement implementation connectedCallback() { if (this.isInitialized) { return; } this.commentId = this.getAttribute("commentId"); this.replyToId = this.getAttribute("replyToId"); this.commenterName = this.getAttribute("commenterName"); this.isoTimestamp = this.getAttribute("isoTimestamp"); this.datetime = new Date(this.isoTimestamp); this.websiteURL = this.getAttribute("websiteURL"); this.commentText = this.getAttribute("commentText"); this.gwCommentFormId = this.getAttribute("gwCommentFormId"); this.renderContent(); this.registerHandlers(); this.isInitialized = true; } //#endregion renderContent() { const headerText = this.replyToId ? `Comment #${this.commentId} replying to #${this.replyToId}` : `Top level comment #${this.commentId}`; const displayTimestamp = this.datetime.toLocaleString( undefined, { dateStyle: "short", timeStyle: "short" } ); const commenterNameEl = this.websiteURL ? `${this.commenterName}` : `${this.commenterName}`; //Markup this.innerHTML = `
${headerText}
${commenterNameEl}
${this.commentText}
`; //element properties this.articleEl = document.getElementById(`${this.idKey}-article`); this.replyBtn = document.getElementById(`${this.idKey}-reply`); } //#region Handlers registerHandlers() { this.replyBtn.onclick = this.onReply; } onReply = () => { const gwCommentForm = document.getElementById(this.gwCommentFormId); const respToInpt = gwCommentForm.respToInpt; if (!respToInpt) { alert("Comment form not found"); return; } respToInpt.value = this.commentId; respToInpt.focus(); }; //#endregion }; customElements.define("gw-comment-card", ns.CommentCard); });