aboutsummaryrefslogtreecommitdiffstats
path: root/src/scripts
diff options
context:
space:
mode:
authorivarlovlie <ivar.lovlie@gmail.com>2020-07-12 19:25:48 +0200
committerivarlovlie <ivar.lovlie@gmail.com>2020-07-12 19:25:48 +0200
commit25f512633704d3a1020a77addb77c3b9217f10fa (patch)
tree19a11f675d66a8895ccba37571d809d765c4219f /src/scripts
downloadstartpage-25f512633704d3a1020a77addb77c3b9217f10fa.tar.xz
startpage-25f512633704d3a1020a77addb77c3b9217f10fa.zip
init
Diffstat (limited to 'src/scripts')
-rw-r--r--src/scripts/index.js379
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();
+