diff options
Diffstat (limited to 'src/scripts/index.js')
| -rw-r--r-- | src/scripts/index.js | 379 |
1 files changed, 379 insertions, 0 deletions
diff --git a/src/scripts/index.js b/src/scripts/index.js new file mode 100644 index 0000000..3671a87 --- /dev/null +++ b/src/scripts/index.js @@ -0,0 +1,379 @@ +const toaster = new Toaster(); + +const searchForm = document.querySelector("#search-form"); +const searchInput = document.querySelector("#search-input"); + +const cardsContainer = document.querySelector("#cards"); + +const commandElements = { + createCardModalButton: document.querySelector("#open-new-card-modal-button"), + importPinsModalButton: document.querySelector("#open-import-pins-modal-button"), + openOptionsModalButton: document.querySelector("#open-options-modal-button") +}; + +const cardModalElements = { + modal: document.querySelector("#card-modal"), + save: document.querySelector("#card-modal #save-card-button"), + name: document.querySelector("#card-modal #card-name"), + header: document.querySelector("#card-modal .modal-title"), + cardLinksContainer: document.querySelector("#card-modal #card-links"), + cardLinksUrlInputs: document.querySelectorAll( + "#card-modal #card-links .link input[name='url']" + ), +}; + +const optionsModalElements = { + modal: document.querySelector("#options-modal"), + save: document.querySelector("#options-modal #save-options-button") +}; + + +const fileInput = document.querySelector("#file"); + +const cardModalBootstrap = new bootstrap.Modal(cardModalElements.modal); +const showCardModal = () => cardModalBootstrap.show(); +const hideCardModal = () => cardModalBootstrap.hide(); + +const optionsModalBootstrap = new bootstrap.Modal(optionsModalElements.modal); +const showOptionsModal = () => optionsModalBootstrap.show(); +const hideOptionsModal = () => optionsModalBootstrap.hide(); + +const constants = { + messages: {}, + database: { + name: "startpage", + keys: { + cards: "cards", + pins: "pins", + settings: "settings" + }, + }, + flags: { + debug: true, + }, + toast_types: { + success: "success", + error: "error", + info: "info", + }, +}; + +let database; + +function initialize() { + database = new PouchDB(constants.database.name); + addEventListeners(); + renderCards(); +} + +function addEventListeners() { + cardModalElements.save.addEventListener("click", submitCardForm); + cardModalElements.cardLinksUrlInputs.forEach((element) => { + element.addEventListener("keypress", keypressOnLinkUrlInput); + }); + + commandElements.createCardModalButton.addEventListener("click", openCreateCardModal); + commandElements.openOptionsModalButton.addEventListener("click", openSettingsModal); + commandElements.importPinsModalButton.addEventListener("click", () => fileInput.click()); + + fileInput.addEventListener("change", function () { + const file = fileInput.files[0]; + console.log(file); + const reader = new FileReader(); + reader.onload = content => importPins(content.target.result); + reader.readAsText(file); + }); + + cardModalElements.modal.addEventListener("hidden.bs.modal", function (event) { + cardModalElements.name.value = ""; + cardModalElements.cardLinksContainer.empty(); + }); + document.addEventListener("hidden.bs.modal", function (event) { + searchInput.focus(); + }); +} + +function openSettingsModal() { + showOptionsModal(); +} + +let pin = { + description: "", + extended: "", + hash: "", + href: "", + meta: "", + tags: "", + time: "", + toread: "", +}; + + +function importPins(json) { + JSON.parse(json).forEach(pin => { + if (!addPin(pin)) throw new Error("WHOOPS"); + }); +} + +function logPins() { + database + .get(constants.database.keys.pins) + .then(pins => { + pins.data.forEach(element => { + console.log(element); + }); + }); +} + +function addPin(pin) { + database + .get(constants.database.keys.pins) + .then((pins) => { + pins._rev = pins._rev; + pins._id = constants.database.keys.pins; + if (!pin._id) pin._id = guid(); + let current = pins.data.find((item) => item._id == pin._id); + if (current) return true; + pins.data.push(pin); + return database.put(pins); + }) + .then((updated) => { + return true; + }) + .catch((err) => { + if (err.status === 404) { + let newPinsDb = { + _id: constants.database.keys.pins, + data: [], + }; + pin._id = guid(); + newPinsDb.data.push(pin); + database + .put(newPinsDb) + .then((added) => { + console.log("created pins"); + }) + .catch((err) => { logError("failed to create pins", err); return false; }); + } else { + logError("failed to update pins", err); + return false; + } + }); +} + + +function openEditCardModal(id) { + if (!id) return; + database + .get(constants.database.keys.cards) + .then((cardDoc) => { + console.log(cardDoc); + let card = cardDoc.data.find((card) => card._id == id); + if (!card) { + renderCards(); + console.log("did not find card"); + } + cardModalElements.name.value = card.name; + cardModalElements.header.innerText = "Rediger " + card.name; + cardModalElements.modal.dataset.id = card._id; + card.links.forEach((link) => addNewLinkInputs(false, link.name, link.url)); + }) + .catch((err) => logError(err)); + showCardModal(); + cardModalElements.name.focus(); + cardModalElements.modal.scrollTop = cardModalElements.modal.scrollHeight; +} + +function openCreateCardModal() { + cardModalElements.header.innerText = "Nytt kort"; + cardModalElements.name.value = ""; + showCardModal(); + cardModalElements.name.focus(); + addNewLinkInputs(); +} + +function submitCardForm(event) { + event.preventDefault(); + if (!cardModalElements.name.value) return; + let card = { + name: cardModalElements.name.value, + links: [], + }; + if (cardModalElements.modal.dataset.id) card._id = cardModalElements.modal.dataset.id; + document.querySelectorAll("#card-modal #card-links .link").forEach((linkElement) => { + let name = linkElement.firstElementChild.value; + let url = linkElement.lastElementChild.value; + if (!name || !url || !url.startsWith("http")) return; + name = name.trim(); + url = url.trim(); + card.links.push({ + name, + url, + }); + }); + if (card.links.length == 0) return; + addOrUpdateCard(card); + hideCardModal(); +} + +function addNewLinkInputs(changeFocus, name, url) { + let inputGroup = document.createElement("div"); + inputGroup.className = "link mt-1 p-2 d-flex align-items-center"; + let nameInput = document.createElement("input"); + nameInput.name = "name"; + nameInput.className = "form-control"; + nameInput.placeholder = "Navn"; + if (name) nameInput.value = name; + inputGroup.appendChild(nameInput); + let arrow = document.createElement("img"); + arrow.src = "assets/icons/arrow-right.svg"; + arrow.className = "mx-3"; + arrow.alt = ""; + arrow.width = "32"; + arrow.height = "32"; + inputGroup.appendChild(arrow); + let urlInput = document.createElement("input"); + urlInput.name = "url"; + urlInput.className = "form-control"; + urlInput.placeholder = "Url"; + urlInput.value = url ? url : "http://"; + urlInput.addEventListener("keypress", keypressOnLinkUrlInput); + inputGroup.appendChild(urlInput); + cardModalElements.cardLinksContainer.appendChild(inputGroup); + if (changeFocus) nameInput.focus(); + cardModalElements.modal.scrollTop = cardModalElements.modal.scrollHeight; +} + +function keypressOnLinkUrlInput(event) { + if (event.keyCode == 13) { + let shouldCreateNewInputs = event.target.parentElement.nextElementSibling == null; + if (shouldCreateNewInputs) { + addNewLinkInputs(true); + } + } +} + +function addOrUpdateCard(card) { + if (!card.name) return; + database + .get(constants.database.keys.cards) + .then((cards) => { + cards._rev = cards._rev; + cards._id = constants.database.keys.cards; + if (!card._id) card._id = guid(); + let current = cards.data.find((item) => item._id == card._id); + if (current) { + current.name = card.name; + current.links = card.links; + } else cards.data.push(card); + return database.put(cards); + }) + .then((updated) => { + console.log("updated carddb"); + renderCards(); + }) + .catch((err) => { + if (err.status === 404) { + let newCardDocument = { + _id: constants.database.keys.cards, + data: [], + }; + card._id = guid(); + newCardDocument.data.push(card); + database + .put(newCardDocument) + .then((added) => { + console.log("created carddb"); + renderCards(); + }) + .catch((err) => logError("failed to create carddb", err)); + } else { + logError("failed to update carddb", err); + } + }); +} + + +function logError(msg, error) { + console.error("!!!"); + console.error(msg); + toaster.error("Whoops!", msg); + console.error(error); + console.error("!!!"); +} + +function wipeData() { + new PouchDB(constants.database.names.cards) + .destroy() + .then(function () { + console.log("wiped data"); + }) + .catch(function (err) { + console.error(err); + }); +} + +function renderCards() { + cardsContainer.empty(); + database + .get(constants.database.keys.cards) + .then((cardDoc) => { + cardDoc.data.sort((a, b) => a.order - b.order).forEach((card) => { + let columElement = document.createElement("div"); + columElement.className = "col"; + let cardElement = document.createElement("div"); + cardElement.className = "card"; + columElement.appendChild(cardElement); + let cardHeader = document.createElement("div"); + cardHeader.className = + "card-header d-flex text-truncate justify-content-between align-items-center"; + let cardHeaderText = document.createElement("span"); + cardHeaderText.innerText = card.name; + cardHeader.appendChild(cardHeaderText); + let cardHeaderEditButton = document.createElement("img"); + cardHeaderEditButton.src = "assets/icons/pencil-square.svg"; + cardHeaderEditButton.title = "Rediger " + card.name; + cardHeaderEditButton.width = "18"; + cardHeaderEditButton.type = "button"; + cardHeaderEditButton.height = "18"; + cardHeaderEditButton.onclick = () => openEditCardModal(card._id); + cardHeader.appendChild(cardHeaderEditButton); + cardElement.appendChild(cardHeader); + let linkList = document.createElement("ul"); + linkList.className = "list-group list-group-flush"; + card.links.forEach((link) => { + let listItem = document.createElement("a"); + listItem.innerText = link.name; + listItem.className = "list-group-item"; + listItem.href = link.url; + linkList.appendChild(listItem); + }); + cardElement.appendChild(linkList); + cardsContainer.append(columElement); + }); + + }) + .catch((error) => { + if (error.status !== 404) logError(error); + }); +} + +function guid() { + let index, + tea, + result = ""; + for (index = 0; index < 32; index++) + (tea = (16 * Math.random()) | 0), + (8 != index && 12 != index && 16 != index && 20 != index) || (result += "-"), + (result += (12 == index ? 4 : 16 == index ? (3 & tea) | 8 : tea).toString(16)); + return result; +} + +Element.prototype.empty = function () { + while (this.firstChild) { + this.removeChild(this.firstChild); + } +}; + +initialize(); + |
