From 162172fce11252a423318d2dd5835bc204994b80 Mon Sep 17 00:00:00 2001 From: Lucas Winther Date: Sat, 14 Nov 2020 02:14:14 +0100 Subject: [PATCH] Started moving project to typescript, fixed a bug when editing subscriptions. --- .gitignore | 6 +- .idea/compiler.xml | 6 + .idea/inspectionProfiles/Project_Default.xml | 6 + .idea/vcs.xml | 6 + index.html | 2 - js/main.js | 953 +++++++++---------- js/main.js.map | 1 + js/main.ts | 640 +++++++++++++ tsconfig.json | 10 + 9 files changed, 1110 insertions(+), 520 deletions(-) create mode 100644 .idea/compiler.xml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/vcs.xml create mode 100644 js/main.js.map create mode 100644 js/main.ts create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore index ef8f3b1..a6600ee 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ -# Include your project-specific ignores in this file -# Read about how to use .gitignore: https://help.github.com/articles/ignoring-files -# Useful .gitignore templates: https://github.com/github/gitignore + +/.idea/ +/node_modules/ diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..1a2fb33 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..df7825d --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/index.html b/index.html index 2f13639..fc7e992 100644 --- a/index.html +++ b/index.html @@ -25,8 +25,6 @@ your browser to improve your experience and security.

- -
diff --git a/js/main.js b/js/main.js index b02e3a6..7ce6535 100644 --- a/js/main.js +++ b/js/main.js @@ -1,584 +1,507 @@ // Setup -subscriptions = [] - -currencies = new Map() -currencies.set("$", 6.78) -currencies.set("€", 7.47) -currencies.set("DKK", 1) - +let subscriptions = []; +const currencies = new Map(); +currencies.set("$", 6.78); +currencies.set("€", 7.47); +currencies.set("DKK", 1); // The name of the data in localStorage -localDataName = "subscriptionInformation" -localSortPreferenceName = "sortPreference" -localHasChangedOrder = "hasChangedOrder" - +const localDataName = "subscriptionInformation"; +let localSortPreferenceName = "sortPreference"; // Initial scroll distance to bottom of the page. -let scrollDistance = getDocHeight() - +let scrollDistance = getDocHeight(); // Function that top/bottom button calls. Scrolls to top if user has scrolled 50% down, bottom otherwise. let scrollToTopOrBottom = () => { - window.scrollTo({ - top: scrollDistance, - left: 0, - behavior: 'smooth' - }) -} - + window.scrollTo({ + top: scrollDistance, + left: 0, + behavior: 'smooth' + }); +}; // Gets the height of the page. function getDocHeight() { - let D = document; - return Math.max( - D.body.scrollHeight, D.documentElement.scrollHeight, - D.body.offsetHeight, D.documentElement.offsetHeight, - D.body.clientHeight, D.documentElement.clientHeight - ) + let D = document; + return Math.max(D.body.scrollHeight, D.documentElement.scrollHeight, D.body.offsetHeight, D.documentElement.offsetHeight, D.body.clientHeight, D.documentElement.clientHeight); } - // Keep track of the window height. let winheight = window.innerHeight || (document.documentElement || document.body).clientHeight; window.onresize = () => { - winheight = window.innerHeight || (document.documentElement || document.body).clientHeight; -} - -// Calculate whether the button should say top or bottom -window.onscroll = () => { - let docHeight = getDocHeight() - let trackLength = docHeight - winheight - - let scrollTop = window.pageYOffset || (document.documentElement || document.body.parentNode || document.body).scrollTop - let pctScrolled = Math.floor(scrollTop/trackLength * 100) // gets percentage scrolled (ie: 80 or NaN if tracklength == 0) - - if (pctScrolled > 50){ - document.querySelector('#myBtn').innerHTML = "Top"; - scrollDistance = 0 - } - else{ - document.querySelector('#myBtn').innerHTML = "Bottom" - scrollDistance = trackLength - } -} - + winheight = window.innerHeight || (document.documentElement || document.body).clientHeight; +}; // Returns true if valid subscription, false if not. +let isValidNewSubscription = (subscription) => { + // Title of subscription must be unique. + if (subscriptions.map((sub) => sub.title).includes(subscription.title)) { + alert("Subscription name not unique"); + return false; + } + return isValidSubscription(subscription); +}; let isValidSubscription = (subscription) => { - if (isNaN(subscription.amount)) { - alert("Amount is not a number") - return false - } - if (!([...currencies.keys()].includes(subscription.currency))) { - alert("Currency is not a valid currency") - return false - } - if (isNaN(subscription.months)) { - alert("Months are not a number") - return false - } - - // Title of subscription must be unique. - if (subscriptions.map((sub) => sub.title).includes(subscription.title)) { - alert("Subscription name not unique") - return false - } - - return true -} - + if (isNaN(subscription.price)) { + alert("Amount is not a number"); + return false; + } + if (!([...currencies.keys()].includes(subscription.currency))) { + alert("Currency is not a valid currency"); + return false; + } + if (isNaN(subscription.months)) { + alert("Months are not a number"); + return false; + } + return true; +}; +let createNewSubscription = (title, price, currency, months, notes) => { + const newSub = { + changed: false, + currency: currency, + indkk: 0, + monthly: 0, + months: months, + notes: notes, + percentOfWhole: null, + price: price, + title: title + }; + newSub.monthly = newSub.price / newSub.months; + newSub.indkk = newSub.monthly * currencies.get(newSub.currency); + return newSub; +}; // Create a new subscription let newSubscription = () => { - // Open the popup - Swal.fire({ - title: 'Insert subscription information', - html: - '
' + - ' ' + - '' + - ' ' + - '' + - ' ' + - '' + - ' ' + - '' + - ' ' + - '', - focusConfirm: false, - preConfirm: () => { - // Create a new temporary subscription - let newSub = { - title: document.getElementById('swal-input1').value, - amount: document.getElementById('swal-input2').value, - currency: document.getElementById('swal-input3').value, - months: document.getElementById('swal-input4').value, - notes: document.getElementById('swal-input5').value, - changed: false - } - - // Do some form validation - let valid = isValidSubscription(newSub) - - // Update the table if form was validated successfully - if (valid){ - subscriptions.push(newSub) - updateTable() - } - } - }) -} - + // Open the popup + // @ts-ignore + Swal.fire({ + title: 'Insert subscription information', + html: '
' + + ' ' + + '' + + ' ' + + '' + + ' ' + + '' + + ' ' + + '' + + ' ' + + '', + focusConfirm: false, + preConfirm: () => { + // Create a new temporary subscription + const title = document.getElementById('swal-input1').value; + const price = parseFloat(document.getElementById('swal-input2').value); + const currency = document.getElementById('swal-input3').value; + const months = parseInt(document.getElementById('swal-input4').value); + const notes = document.getElementById('swal-input5').value; + const newSub = createNewSubscription(title, price, currency, months, notes); + // Do some form validation + let valid = isValidNewSubscription(newSub); + // Update the table if form was validated successfully + if (valid) { + subscriptions.push(newSub); + updateTable(); + } + } + }); +}; // Edit a subscription let editSubscription = (btn) => { - // Find out which subscription we're editing - let currentSubscription = getSubscriptionFromButton(btn) - - // Open the popup, and fill in the current subscription information in the form - Swal.fire({ - title: 'Insert subscription information', - html: - '
' + - ' ' + - '' + - ' ' + - '' + - ' ' + - '' + - ' ' + - '' + - ' ' + - '', - focusConfirm: false, - cancelButtonColor: '#d33', - showCancelButton: true, - preConfirm: () => { - // Create a new temporary subscription - let newSub = { - title: document.getElementById('swal-input1').value, - amount: document.getElementById('swal-input2').value, - currency: document.getElementById('swal-input3').value, - months: document.getElementById('swal-input4').value, - notes: document.getElementById('swal-input5').value, - changed: false - } - - let valid = isValidSubscription(newSub) - if (valid){ - // Update the table if form was validated successfully - deleteSubscription(currentSubscription) - subscriptions.push(newSub) - updateTable() - } - } - }) -} - + // Find out which subscription we're editing + let currentSubscription = getSubscriptionFromButton(btn); + // Open the popup, and fill in the current subscription information in the form + // @ts-ignore + Swal.fire({ + title: 'Insert subscription information', + html: '
' + + ' ' + + '' + + ' ' + + '' + + ' ' + + '' + + ' ' + + '' + + ' ' + + '', + focusConfirm: false, + cancelButtonColor: '#d33', + showCancelButton: true, + preConfirm: () => { + // Create a new temporary subscription + const title = document.getElementById('swal-input1').value; + const price = parseFloat(document.getElementById('swal-input2').value); + const currency = document.getElementById('swal-input3').value; + const months = parseInt(document.getElementById('swal-input4').value); + const notes = document.getElementById('swal-input5').value; + const editedSub = createNewSubscription(title, price, currency, months, notes); + let valid = isValidSubscription(editedSub); + console.log(valid); + if (valid) { + console.log(editedSub); + // Update the table if form was validated successfully + deleteSubscription(currentSubscription); + subscriptions.push(editedSub); + updateTable(); + } + } + }); +}; // Add a new currency. Called by the add currency button let addCurrency = () => { - // Fire popup - Swal.fire({ - title: 'Insert currency information', - html: - '' + - '', - focusConfirm: false, - preConfirm: () => { - // Register the new possible currency - let newCurrency = { - symbol: document.getElementById('swal-input1').value, - rate: document.getElementById('swal-input2').value, - } - - // Check that the rate is a number - if (isNaN(newCurrency.rate)) { - alert("DKK exchange rate is now a number") - return - } - - // Update with the new currency, and refresh the table - currencies.set(newCurrency.symbol, newCurrency.rate) - updateTable() - } - }) -} - + // Fire popup + // @ts-ignore + Swal.fire({ + title: 'Insert currency information', + html: '' + + '', + focusConfirm: false, + preConfirm: () => { + // Register the new possible currency + let newCurrency = { + symbol: document.getElementById('swal-input1').value, + rate: parseFloat(document.getElementById('swal-input2').value), + }; + // Check that the rate is a number + if (isNaN(newCurrency.rate)) { + alert("DKK exchange rate is now a number"); + return; + } + // Update with the new currency, and refresh the table + currencies.set(newCurrency.symbol, newCurrency.rate); + updateTable(); + } + }); +}; // Delete all subscriptions. let Reset = () => { - Swal.fire({ - title: 'Are you sure?', - text: "You won't be able to revert this!", - icon: 'warning', - showCancelButton: true, - confirmButtonColor: '#3085d6', - cancelButtonColor: '#d33', - confirmButtonText: 'Yes, delete it!' - }).then((result) => { - if (result.value) { - subscriptions = [] - updateTable() - Swal.fire( - 'Reset!', - 'Your subscriptions have been reset.', - 'success' - ) - } - }) -} - - + // @ts-ignore + Swal.fire({ + title: 'Are you sure?', + text: "You won't be able to revert this!", + icon: 'warning', + showCancelButton: true, + confirmButtonColor: '#3085d6', + cancelButtonColor: '#d33', + confirmButtonText: 'Yes, delete it!' + }).then((result) => { + if (result.value) { + subscriptions.length = 0; + updateTable(); + // @ts-ignore + Swal.fire('Reset!', 'Your subscriptions have been reset.', 'success'); + } + }); +}; // Method for updating the table with new information. let updateTable = () => { - // Create a new replacement tbody - let newTbody = document.createElement('tbody'); - newTbody.classList.add("tbody") - - // Sort by users preference - - - // Add rows from data in memory. - populateTable(newTbody) - - // Replace the old tbody with the new cleared one - let oldTbody = document.querySelector('.tbody') - oldTbody.parentNode.replaceChild(newTbody, oldTbody) - - // Save the new information locally. - setStored(localDataName, JSON.stringify(getSaveInfo())) -} - + // Create a new replacement tbody + let newTbody = document.createElement('tbody'); + newTbody.classList.add("tbody"); + // Sort by users preference + // Add rows from data in memory. + populateTable(newTbody); + // Replace the old tbody with the new cleared one + let oldTbody = document.querySelector('.tbody'); + oldTbody.parentNode.replaceChild(newTbody, oldTbody); + // Save the new information locally. + setStored(localDataName, JSON.stringify(getSaveInfo())); +}; // Populate a tbody with subscriptions from the subscriptions array. let populateTable = (tbody) => { - // Store the total cost of all subscriptions - total = 0 - - // calculate the monthly cost in dkk of each item, and add it to the running total - for (elem of subscriptions) { - elem.monthly = elem.amount / elem.months - elem.indkk = elem.monthly * currencies.get(elem.currency) - total += elem.indkk - } - - // Update tablefoot with total monthly cost and amount of subscriptions. - document.querySelector('#total').innerHTML = total.toFixed(2) - document.querySelector('#amountOfSubscriptions').innerHTML = subscriptions.length - - // Begin to create table rows - for (let i = 0; i < subscriptions.length; i++) { - // Select a subscription on index i, and create a new empty row - let elem = subscriptions[i] - var tr = document.createElement('tr'); - - // Calculate the subscriptions percentage-wise cost of the monthly total. - elem.percentOfWhole = elem.indkk / total * 100; - - // If this element has been changed (ie. created, moved), mark it for the user. - if (elem.changed) { - tr.classList = "is-selected" - elem.changed = false + // Store the total cost of all subscriptions + let total = subscriptions.map((s) => s.monthly).reduce((acc, sub) => acc + sub); + // Update tablefoot with total monthly cost and amount of subscriptions. + document.querySelector('#total').innerHTML = total.toFixed(2); + document.querySelector('#amountOfSubscriptions').innerHTML = subscriptions.length.toString(); + // Begin to create table rows + for (let i = 0; i < subscriptions.length; i++) { + // Select a subscription on index i, and create a new empty row + let elem = subscriptions[i]; + let tr = document.createElement('tr'); + // Calculate the subscriptions percentage-wise cost of the monthly total. + elem.percentOfWhole = elem.indkk / total * 100; + // If this element has been changed (ie. created, moved), mark it for the user. + if (elem.changed) { + tr.classList.add("is-selected"); + elem.changed = false; + } + // Create the row. + tr.innerHTML = '' + + '' + + '' + + '' + + '' + + '' + + '' + + ''; + // Append the new row + tbody.appendChild(tr); } - - // Create the row. - tr.innerHTML = '' + - '' + - '' + - '' + - '' + - '' + - '' + - ''; - - // Append the new row - tbody.appendChild(tr); - } -} - +}; // Create a pie-chart with the cost of each subscription let PieChart = () => { - - // Get data ready for CanvasJS. - let data = [] - for (subscription of subscriptions) { - console.log(subscription) - data.push({y: subscription.percentOfWhole, label: subscription.title}) - } - - // Create the chart with CanvasJS. - let chart = new CanvasJS.Chart("chartContainer", { - animationEnabled: true, - title: { - text: "Monthly Expenses" - }, - data: [{ - type: "pie", - startAngle: 240, - yValueFormatString: "##0.00\"%\"", - indexLabel: "{label} {y}", - dataPoints: data - }] - }); - - // Lastly, render it. - chart.render() -} - + // Get data ready for CanvasJS. + let data = []; + for (let subscription of subscriptions) { + console.log(subscription); + data.push({ y: subscription.percentOfWhole, label: subscription.title }); + } + // Create the chart with CanvasJS. + // @ts-ignore + let chart = new CanvasJS.Chart("chartContainer", { + animationEnabled: true, + title: { + text: "Monthly Expenses" + }, + data: [{ + type: "pie", + startAngle: 240, + yValueFormatString: "##0.00\"%\"", + indexLabel: "{label} {y}", + dataPoints: data + }] + }); + // Lastly, render it. + chart.render(); +}; // Ask the user what to sort by, and then sorts the array. let Sort = async () => { - - // The input options for SWAL2 - const inputOptions = new Promise((resolve) => { - resolve({ - 'Lex': 'Lexi.', - 'Price': 'Price' - }) - }) - - // Fire the popup - const {value: sortBy} = await Swal.fire({ - title: 'Sort by what', - input: 'radio', - inputOptions: inputOptions, - inputValidator: (value) => { - if (!value) { - return 'You need to choose something!' - } + // The input options for SWAL2 + const inputOptions = new Promise((resolve) => { + resolve({ + 'Lex': 'Lexi.', + 'Price': 'Price' + }); + }); + // Fire the popup + // @ts-ignore + const { value: sortBy } = await Swal.fire({ + title: 'Sort by what', + input: 'radio', + inputOptions: inputOptions, + inputValidator: (value) => { + if (!value) { + return 'You need to choose something!'; + } + } + }); + // Sort the array, and save the sorting preference + if (sortBy) { + if (sortBy == "Lex") { + setStored(localSortPreferenceName, "Lex"); + sortSubscriptionsByName(); + updateTable(); + } + else if (sortBy == "Price") { + setStored(localSortPreferenceName, "Price"); + sortSubscriptionsByPrice(); + updateTable(); + } } - }) - - // Sort the array, and save the sorting preference - if (sortBy) { - if (sortBy == "Lex") { - setStored(localSortPreferenceName, "Lex") - sortSubscriptionsByName() - updateTable() - } else if (sortBy == "Price") { - setStored(localSortPreferenceName, "Price") - sortSubscriptionsByPrice() - updateTable() - } - } -} - +}; // different sorters for subscriptions array let sortSubscriptionsByPrice = () => { - subscriptions.sort((a, b) => { - if (a.indkk > b.indkk) { - return -1; - } - if (b.indkk > a.indkk) { - return 1; - } - return 0; - }) -} + subscriptions.sort((a, b) => { + if (a.indkk > b.indkk) { + return -1; + } + if (b.indkk > a.indkk) { + return 1; + } + return 0; + }); +}; let sortSubscriptionsByName = () => { - subscriptions.sort((a, b) => { - if (a.title > b.title) { - return 1; - } - if (b.title > a.title) { - return -1; - } - return 0; - }) -} - - + subscriptions.sort((a, b) => { + if (a.title > b.title) { + return 1; + } + if (b.title > a.title) { + return -1; + } + return 0; + }); +}; // Gets more information on a subscription, and displays it in a popup let getInfoOfSubscription = (btn) => { - let currentSub = getSubscriptionFromButton(btn) - Swal.fire( - currentSub.title, - `${currentSub.amount}${currentSub.currency} over ${currentSub.months} months, which equates to
+ let currentSub = getSubscriptionFromButton(btn); + // @ts-ignore + Swal.fire(currentSub.title, `${currentSub.price}${currentSub.currency} over ${currentSub.months} months, which equates to
${currentSub.monthly}${currentSub.currency} monthly, or about ${currentSub.indkk} DKK
` + - // Display notes only if there are some. - (currentSub.notes === undefined ? "" : ( - ` + // Display notes only if there are some. + (currentSub.notes === undefined ? "" : (` Notes:
- `)), - 'question' - ) -} - + `)), 'question'); +}; // Since title is unique, this finds the title of the row from which a button was pressed, and returns that subscription from the subscription array let getSubscriptionFromButton = (button) => { - let tr = button.parentNode.parentNode - let title = tr.firstChild.textContent - return subscriptions.filter(s => s.title == title)[0] -} - -// Moves a row by some direction (+x, -x) -let moveRow = (btn, direction) => { - - // Find the subscriptions current index - let currentSub = getSubscriptionFromButton(btn) - let indexOfCurrentRow = subscriptions.indexOf(currentSub) - - // If trying to move a subscription out of bounds - if (indexOfCurrentRow + direction < 0 || indexOfCurrentRow + direction >= subscriptions.length) { - Swal.fire({ - icon: 'error', - title: 'Oops...', - text: 'Cant move this object ' + (direction == -1 ? "up" : "down"), - }) - } else { - // Move the subscription - subscriptions[indexOfCurrentRow] = subscriptions[indexOfCurrentRow + direction] - subscriptions[indexOfCurrentRow + direction] = currentSub - currentSub.changed = true - updateTable() - - // Store that the user has a custom order - setStored(localHasChangedOrder, true) - } -} - - + let tr = button.parentNode.parentNode; + let title = tr.firstChild.textContent; + return subscriptions.filter(s => s.title == title)[0]; +}; let askToDeleteRow = (btn) => { - Swal.fire({ - title: 'Are you sure?', - text: "You won't be able to revert this!", - icon: 'warning', - showCancelButton: true, - confirmButtonColor: '#3085d6', - cancelButtonColor: '#d33', - confirmButtonText: 'Yes, delete it!' - }).then((result) => { - if (result.value) { - deleteSubscription(getSubscriptionFromButton(btn)) - Swal.fire( - 'Deleted!', - 'Your file has been deleted.', - 'success' - ) - } - }) -} - + // @ts-ignore + Swal.fire({ + title: 'Are you sure?', + text: "You won't be able to revert this!", + icon: 'warning', + showCancelButton: true, + confirmButtonColor: '#3085d6', + cancelButtonColor: '#d33', + confirmButtonText: 'Yes, delete it!' + }).then((result) => { + if (result.value) { + deleteSubscription(getSubscriptionFromButton(btn)); + // @ts-ignore + Swal.fire('Deleted!', 'Your file has been deleted.', 'success'); + } + }); +}; let deleteSubscription = (subscription) => { - subscriptions = subscriptions.filter(s => s.title != subscription.title) - updateTable() -} - + const index = subscriptions.map(s => s.title).indexOf(subscription.title); + subscriptions.splice(index, 1); + updateTable(); +}; // Make subscriptions and currency ready to be stored as JSON let getSaveInfo = () => { - return {subscriptions: subscriptions, keys: [...currencies.keys()], values: [...currencies.values()]} -} - + return { version: 1, subscriptions: subscriptions, keys: [...currencies.keys()], values: [...currencies.values()] }; +}; // Parse a save object, and use it as the current info. -let loadSaveInfo = (loadedSave) => { - subscriptions = loadedSave.subscriptions - - currencies.clear() - for (let i = 0; i < loadedSave.keys.length; i++) { - currencies.set(loadedSave.keys[i], loadedSave.values[i]) - } -} - +let loadSaveInfo = (loadedSave, oldLoadedSave) => { + let actualSaveToUse = loadedSave; + if (oldLoadedSave !== null) { + // Old data + console.log("loading legacy data..."); + const oldData = oldLoadedSave; + let newData = { keys: [], subscriptions: [], values: [], version: 0 }; + newData.keys = oldData.keys; + newData.version = 1; + newData.values = oldData.values; + for (let sub of oldLoadedSave.subscriptions) { + const newSub = createNewSubscription(sub.title, sub.amount, sub.currency, sub.months, sub.notes); + newData.subscriptions.push(newSub); + } + actualSaveToUse = newData; + } + subscriptions = actualSaveToUse.subscriptions; + currencies.clear(); + for (let i = 0; i < actualSaveToUse.keys.length; i++) { + currencies.set(actualSaveToUse.keys[i], actualSaveToUse.values[i]); + } +}; // Save a local JSON version of the current state. let saveToFile = () => { - var dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(getSaveInfo())); - var downloadAnchorNode = document.createElement('a'); - downloadAnchorNode.setAttribute("href", dataStr); - downloadAnchorNode.setAttribute("download", "save" + ".json"); - document.body.appendChild(downloadAnchorNode); // required for firefox - downloadAnchorNode.click(); - downloadAnchorNode.remove(); -} - + var dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(getSaveInfo())); + var downloadAnchorNode = document.createElement('a'); + downloadAnchorNode.setAttribute("href", dataStr); + downloadAnchorNode.setAttribute("download", "save" + ".json"); + document.body.appendChild(downloadAnchorNode); // required for firefox + downloadAnchorNode.click(); + downloadAnchorNode.remove(); +}; // Load a local JSON version of some state. let load = () => { - var input, file, fr; - - if (typeof window.FileReader !== 'function') { - alert("The file API isn't supported on this browser yet."); - return; - } - - input = document.getElementById('fileinput'); - if (!input) { - alert("Um, couldn't find the fileinput element."); - } else if (!input.files) { - alert("This browser doesn't seem to support the `files` property of file inputs."); - } else if (!input.files[0]) { - alert("Please select a file before clicking 'Load'"); - } else { - file = input.files[0]; - fr = new FileReader(); - fr.onload = receivedText; - fr.readAsText(file); - } - - function receivedText(e) { - let lines = e.target.result; - loadedSave = JSON.parse(lines); - loadSaveInfo(loadedSave) - - updateTable() - } -} - + var input, file, fr; + if (typeof window.FileReader !== 'function') { + alert("The file API isn't supported on this browser yet."); + return; + } + input = document.getElementById('fileinput'); + if (!input) { + alert("Um, couldn't find the fileinput element."); + } + else if (!input.files) { + alert("This browser doesn't seem to support the `files` property of file inputs."); + } + else if (!input.files[0]) { + alert("Please select a file before clicking 'Load'"); + } + else { + file = input.files[0]; + fr = new FileReader(); + fr.onload = receivedText; + fr.readAsText(file); + } + function receivedText(e) { + let lines = e.target.result; + let loadedSave = JSON.parse(lines); + if (loadedSave.version == null) { + loadSaveInfo(null, loadedSave); + } + else { + loadSaveInfo(loadedSave, null); + } + updateTable(); + } +}; // Retrieve the key value pair vname, vvalue function getStored(vname) { - if (typeof (Storage) !== "undefined") { - return localStorage.getItem(vname) - } else { - return getCookie(vname) - } + if (typeof (Storage) !== "undefined") { + return localStorage.getItem(vname); + } + else { + return getCookie(vname); + } } - // Store the key value pair vname, vvalue function setStored(vname, vvalue) { - if (typeof (Storage) !== "undefined") { - return localStorage.setItem(vname, vvalue) - } else { - return setCookie(vname, vvalue) - } + if (typeof (Storage) !== "undefined") { + return localStorage.setItem(vname, vvalue); + } + else { + return setCookie(vname, vvalue, 1000); + } } - - // Fallback if browser doesn't support HTML5 localstorage function setCookie(cname, cvalue, exdays) { - var d = new Date(); - d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000)); - var expires = "expires=" + d.toUTCString(); - document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/"; + var d = new Date(); + d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000)); + var expires = "expires=" + d.toUTCString(); + document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/"; } - function getCookie(cname) { - var name = cname + "="; - var decodedCookie = decodeURIComponent(document.cookie); - var ca = decodedCookie.split(';'); - for (var i = 0; i < ca.length; i++) { - var c = ca[i]; - while (c.charAt(0) == ' ') { - c = c.substring(1); + var name = cname + "="; + var decodedCookie = decodeURIComponent(document.cookie); + var ca = decodedCookie.split(';'); + for (var i = 0; i < ca.length; i++) { + var c = ca[i]; + while (c.charAt(0) == ' ') { + c = c.substring(1); + } + if (c.indexOf(name) == 0) { + return c.substring(name.length, c.length); + } } - if (c.indexOf(name) == 0) { - return c.substring(name.length, c.length); - } - } - return ""; + return ""; } - - // Try to load data from local storage. If this fails, there's probably not any data to retrieve. try { - loadSaveInfo(JSON.parse(getStored(localDataName))) - - // Find out if we want to sort the array of the users data. - if (!getStored(localHasChangedOrder)){ - let userSortPref = getStored(localSortPreferenceName) - if (userSortPref === undefined){ - sortSubscriptionsByName() + let data = JSON.parse(getStored(localDataName)); + if (data.version == null) { + loadSaveInfo(null, data); } - else{ - if (userSortPref == "Price"){ - sortSubscriptionsByPrice() - } - else{ - sortSubscriptionsByName() - } + else { + loadSaveInfo(data, null); } - } - updateTable() -} catch (e) { - console.log("Didn't find any locally stored data") - console.log(e) + // Find out if we want to sort the array of the users data. + let userSortPref = getStored(localSortPreferenceName); + if (userSortPref === undefined) { + sortSubscriptionsByName(); + } + else { + if (userSortPref == "Price") { + sortSubscriptionsByPrice(); + } + else { + sortSubscriptionsByName(); + } + } + updateTable(); } +catch (e) { + console.log("Didn't find any locally stored data"); + console.log(e); +} +//# sourceMappingURL=main.js.map \ No newline at end of file diff --git a/js/main.js.map b/js/main.js.map new file mode 100644 index 0000000..1d7c961 --- /dev/null +++ b/js/main.js.map @@ -0,0 +1 @@ +{"version":3,"file":"main.js","sourceRoot":"","sources":["main.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/js/main.ts b/js/main.ts new file mode 100644 index 0000000..a0a9acd --- /dev/null +++ b/js/main.ts @@ -0,0 +1,640 @@ +interface subscription{ + title: String, + price: number, + currency: String, + months: number, + notes: String, + monthly: number, + indkk: number, + percentOfWhole: number, + changed: boolean +} + +interface oldSubscription{ + title: String, + amount: number, + currency: String, + months: number, + notes: String, + monthly: number, + indkk: number, + percentOfWhole: number, + changed: boolean +} + +interface oldSave{ + subscriptions: oldSubscription[], + keys: string[], //Currencies symbols + values: number[] // Currencies values +} + +interface save{ + version: number, + subscriptions: subscription[], + keys: String[], //Currencies symbols + values: number[] // Currencies values +} + +// Setup +let subscriptions: subscription[] = []; + +const currencies:Map = new Map() +currencies.set("$", 6.78) +currencies.set("€", 7.47) +currencies.set("DKK", 1) + +// The name of the data in localStorage +const localDataName = "subscriptionInformation" +let localSortPreferenceName = "sortPreference" + +// Initial scroll distance to bottom of the page. +let scrollDistance = getDocHeight() + +// Function that top/bottom button calls. Scrolls to top if user has scrolled 50% down, bottom otherwise. +let scrollToTopOrBottom = () => { + window.scrollTo({ + top: scrollDistance, + left: 0, + behavior: 'smooth' + }) +} + +// Gets the height of the page. +function getDocHeight() { + let D = document; + return Math.max( + D.body.scrollHeight, D.documentElement.scrollHeight, + D.body.offsetHeight, D.documentElement.offsetHeight, + D.body.clientHeight, D.documentElement.clientHeight + ) +} + +// Keep track of the window height. +let winheight = window.innerHeight || (document.documentElement || document.body).clientHeight; +window.onresize = () => { + winheight = window.innerHeight || (document.documentElement || document.body).clientHeight; +} + +// Returns true if valid subscription, false if not. +let isValidNewSubscription = (subscription): Boolean => { + + // Title of subscription must be unique. + if (subscriptions.map((sub) => sub.title).includes(subscription.title)) { + alert("Subscription name not unique") + return false + } + + return isValidSubscription(subscription); +} + +let isValidSubscription = (subscription: subscription): boolean => { + if (isNaN(subscription.price)) { + alert("Amount is not a number") + return false + } + if (!([...currencies.keys()].includes(subscription.currency))) { + alert("Currency is not a valid currency") + return false + } + if (isNaN(subscription.months)) { + alert("Months are not a number") + return false + } + + return true +} + +let createNewSubscription = (title, price, currency, months, notes): subscription => { + const newSub: subscription = { + changed: false, + currency: currency, + indkk: 0, + monthly: 0, + months: months, + notes: notes, + percentOfWhole: null, + price: price, + title: title + } + + newSub.monthly = newSub.price / newSub.months; + newSub.indkk = newSub.monthly * currencies.get(newSub.currency); + + return newSub; +} + +// Create a new subscription +let newSubscription = () => { + // Open the popup + // @ts-ignore + Swal.fire({ + title: 'Insert subscription information', + html: + '
' + + ' ' + + '' + + ' ' + + '' + + ' ' + + '' + + ' ' + + '' + + ' ' + + '', + focusConfirm: false, + preConfirm: () => { + // Create a new temporary subscription + + + const title = (document.getElementById('swal-input1')).value; + const price = parseFloat((document.getElementById('swal-input2')).value) + const currency = (document.getElementById('swal-input3')).value + const months = parseInt((document.getElementById('swal-input4')).value) + const notes = (document.getElementById('swal-input5')).value + + const newSub = createNewSubscription(title, price, currency, months, notes) + + // Do some form validation + let valid = isValidNewSubscription(newSub) + + // Update the table if form was validated successfully + if (valid){ + subscriptions.push(newSub) + updateTable() + } + } + }) +} + +// Edit a subscription +let editSubscription = (btn) => { + // Find out which subscription we're editing + let currentSubscription = getSubscriptionFromButton(btn) + + // Open the popup, and fill in the current subscription information in the form + // @ts-ignore + Swal.fire({ + title: 'Insert subscription information', + html: + '
' + + ' ' + + '' + + ' ' + + '' + + ' ' + + '' + + ' ' + + '' + + ' ' + + '', + focusConfirm: false, + cancelButtonColor: '#d33', + showCancelButton: true, + preConfirm: () => { + // Create a new temporary subscription + const title = (document.getElementById('swal-input1')).value; + const price = parseFloat((document.getElementById('swal-input2')).value) + const currency = (document.getElementById('swal-input3')).value + const months = parseInt((document.getElementById('swal-input4')).value) + const notes = (document.getElementById('swal-input5')).value + + const editedSub = createNewSubscription(title, price, currency, months, notes) + + let valid: boolean = isValidSubscription(editedSub) + console.log(valid) + + if (valid){ + console.log(editedSub) + // Update the table if form was validated successfully + deleteSubscription(currentSubscription) + subscriptions.push(editedSub) + updateTable() + } + } + }) +} + +// Add a new currency. Called by the add currency button +let addCurrency = () => { + // Fire popup + // @ts-ignore + Swal.fire({ + title: 'Insert currency information', + html: + '' + + '', + focusConfirm: false, + preConfirm: () => { + // Register the new possible currency + let newCurrency = { + symbol: (document.getElementById('swal-input1')).value, + rate: parseFloat((document.getElementById('swal-input2')).value), + } + + // Check that the rate is a number + if (isNaN(newCurrency.rate)) { + alert("DKK exchange rate is now a number") + return + } + + // Update with the new currency, and refresh the table + currencies.set(newCurrency.symbol, newCurrency.rate) + updateTable() + } + }) +} + +// Delete all subscriptions. +let Reset = () => { + // @ts-ignore + Swal.fire({ + title: 'Are you sure?', + text: "You won't be able to revert this!", + icon: 'warning', + showCancelButton: true, + confirmButtonColor: '#3085d6', + cancelButtonColor: '#d33', + confirmButtonText: 'Yes, delete it!' + }).then((result) => { + if (result.value) { + subscriptions.length = 0; + updateTable() + // @ts-ignore + Swal.fire( + 'Reset!', + 'Your subscriptions have been reset.', + 'success' + ) + } + }) +} + + +// Method for updating the table with new information. +let updateTable = () => { + // Create a new replacement tbody + let newTbody = document.createElement('tbody'); + newTbody.classList.add("tbody") + + // Sort by users preference + + // Add rows from data in memory. + populateTable(newTbody) + + // Replace the old tbody with the new cleared one + let oldTbody = document.querySelector('.tbody') + oldTbody.parentNode.replaceChild(newTbody, oldTbody) + + // Save the new information locally. + setStored(localDataName, JSON.stringify(getSaveInfo())) +} + +// Populate a tbody with subscriptions from the subscriptions array. +let populateTable = (tbody) => { + // Store the total cost of all subscriptions + let total = subscriptions.map((s) => s.monthly).reduce((acc, sub) => acc + sub) + + // Update tablefoot with total monthly cost and amount of subscriptions. + document.querySelector('#total').innerHTML = total.toFixed(2); + document.querySelector('#amountOfSubscriptions').innerHTML = subscriptions.length.toString(); + + // Begin to create table rows + for (let i = 0; i < subscriptions.length; i++) { + // Select a subscription on index i, and create a new empty row + let elem = subscriptions[i] + let tr = document.createElement('tr'); + + // Calculate the subscriptions percentage-wise cost of the monthly total. + elem.percentOfWhole = elem.indkk / total * 100; + + // If this element has been changed (ie. created, moved), mark it for the user. + if (elem.changed) { + tr.classList.add("is-selected"); + elem.changed = false + } + + // Create the row. + tr.innerHTML = '
' + + '' + + '' + + '' + + '' + + '' + + '' + + ''; + + // Append the new row + tbody.appendChild(tr); + } +} + +// Create a pie-chart with the cost of each subscription +let PieChart = () => { + // Get data ready for CanvasJS. + let data = [] + for (let subscription of subscriptions) { + console.log(subscription) + data.push({y: subscription.percentOfWhole, label: subscription.title}) + } + + // Create the chart with CanvasJS. + // @ts-ignore + let chart = new CanvasJS.Chart("chartContainer", { + animationEnabled: true, + title: { + text: "Monthly Expenses" + }, + data: [{ + type: "pie", + startAngle: 240, + yValueFormatString: "##0.00\"%\"", + indexLabel: "{label} {y}", + dataPoints: data + }] + }); + + // Lastly, render it. + chart.render() +} + +// Ask the user what to sort by, and then sorts the array. +let Sort = async () => { + + // The input options for SWAL2 + const inputOptions = new Promise((resolve) => { + resolve({ + 'Lex': 'Lexi.', + 'Price': 'Price' + }) + }) + + // Fire the popup + // @ts-ignore + const {value: sortBy} = await Swal.fire({ + title: 'Sort by what', + input: 'radio', + inputOptions: inputOptions, + inputValidator: (value) => { + if (!value) { + return 'You need to choose something!' + } + } + }) + + // Sort the array, and save the sorting preference + if (sortBy) { + if (sortBy == "Lex") { + setStored(localSortPreferenceName, "Lex") + sortSubscriptionsByName() + updateTable() + } else if (sortBy == "Price") { + setStored(localSortPreferenceName, "Price") + sortSubscriptionsByPrice() + updateTable() + } + } +} + +// different sorters for subscriptions array +let sortSubscriptionsByPrice = () => { + subscriptions.sort((a, b) => { + if (a.indkk > b.indkk) { + return -1; + } + if (b.indkk > a.indkk) { + return 1; + } + return 0; + }) +} +let sortSubscriptionsByName = () => { + subscriptions.sort((a, b) => { + if (a.title > b.title) { + return 1; + } + if (b.title > a.title) { + return -1; + } + return 0; + }) +} + + +// Gets more information on a subscription, and displays it in a popup +let getInfoOfSubscription = (btn) => { + let currentSub = getSubscriptionFromButton(btn) + // @ts-ignore + Swal.fire( + currentSub.title, + `${currentSub.price}${currentSub.currency} over ${currentSub.months} months, which equates to
+ ${currentSub.monthly}${currentSub.currency} monthly, or about ${currentSub.indkk} DKK
` + + // Display notes only if there are some. + (currentSub.notes === undefined ? "" : ( + ` + Notes:
+
+ +
+ `)), + 'question' + ) +} + +// Since title is unique, this finds the title of the row from which a button was pressed, and returns that subscription from the subscription array +let getSubscriptionFromButton = (button) => { + let tr = button.parentNode.parentNode + let title = tr.firstChild.textContent + return subscriptions.filter(s => s.title == title)[0] +} + +let askToDeleteRow = (btn) => { + // @ts-ignore + Swal.fire({ + title: 'Are you sure?', + text: "You won't be able to revert this!", + icon: 'warning', + showCancelButton: true, + confirmButtonColor: '#3085d6', + cancelButtonColor: '#d33', + confirmButtonText: 'Yes, delete it!' + }).then((result) => { + if (result.value) { + deleteSubscription(getSubscriptionFromButton(btn)) + // @ts-ignore + Swal.fire( + 'Deleted!', + 'Your file has been deleted.', + 'success' + ) + } + }) +} + +let deleteSubscription = (subscription) => { + const index = subscriptions.map(s => s.title).indexOf(subscription.title); + subscriptions.splice(index, 1); + updateTable() +} + +// Make subscriptions and currency ready to be stored as JSON +let getSaveInfo = (): save => { + return {version: 1, subscriptions: subscriptions, keys: [...currencies.keys()], values: [...currencies.values()]} +} + +// Parse a save object, and use it as the current info. +let loadSaveInfo = (loadedSave: save, oldLoadedSave: oldSave) => { + + let actualSaveToUse = loadedSave; + + if (oldLoadedSave !== null){ + // Old data + + console.log("loading legacy data...") + + const oldData: oldSave = oldLoadedSave; + + let newData: save = {keys: [], subscriptions: [], values: [], version: 0}; + newData.keys = oldData.keys; + newData.version = 1; + newData.values = oldData.values; + + for (let sub of oldLoadedSave.subscriptions){ + const newSub: subscription = createNewSubscription(sub.title, sub.amount, sub.currency, sub.months, sub.notes); + newData.subscriptions.push(newSub); + } + + actualSaveToUse = newData; + } + + + subscriptions = actualSaveToUse.subscriptions + + currencies.clear() + for (let i = 0; i < actualSaveToUse.keys.length; i++) { + currencies.set(actualSaveToUse.keys[i], actualSaveToUse.values[i]) + } +} + +// Save a local JSON version of the current state. +let saveToFile = () => { + var dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(getSaveInfo())); + var downloadAnchorNode = document.createElement('a'); + downloadAnchorNode.setAttribute("href", dataStr); + downloadAnchorNode.setAttribute("download", "save" + ".json"); + document.body.appendChild(downloadAnchorNode); // required for firefox + downloadAnchorNode.click(); + downloadAnchorNode.remove(); +} + +// Load a local JSON version of some state. +let load = () => { + var input, file, fr; + + if (typeof window.FileReader !== 'function') { + alert("The file API isn't supported on this browser yet."); + return; + } + + input = document.getElementById('fileinput'); + if (!input) { + alert("Um, couldn't find the fileinput element."); + } else if (!input.files) { + alert("This browser doesn't seem to support the `files` property of file inputs."); + } else if (!input.files[0]) { + alert("Please select a file before clicking 'Load'"); + } else { + file = input.files[0]; + fr = new FileReader(); + fr.onload = receivedText; + fr.readAsText(file); + } + + function receivedText(e) { + let lines = e.target.result; + let loadedSave = JSON.parse(lines); + + if (loadedSave.version == null){ + loadSaveInfo(null, loadedSave) + } + else{ + loadSaveInfo(loadedSave, null) + + } + + updateTable() + } +} + +// Retrieve the key value pair vname, vvalue +function getStored(vname) { + if (typeof (Storage) !== "undefined") { + return localStorage.getItem(vname) + } else { + return getCookie(vname) + } +} + +// Store the key value pair vname, vvalue +function setStored(vname, vvalue) { + if (typeof (Storage) !== "undefined") { + return localStorage.setItem(vname, vvalue) + } else { + return setCookie(vname, vvalue, 1000) + } +} + + +// Fallback if browser doesn't support HTML5 localstorage +function setCookie(cname, cvalue, exdays) { + var d = new Date(); + d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000)); + var expires = "expires=" + d.toUTCString(); + document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/"; +} + +function getCookie(cname) { + var name = cname + "="; + var decodedCookie = decodeURIComponent(document.cookie); + var ca = decodedCookie.split(';'); + for (var i = 0; i < ca.length; i++) { + var c = ca[i]; + while (c.charAt(0) == ' ') { + c = c.substring(1); + } + if (c.indexOf(name) == 0) { + return c.substring(name.length, c.length); + } + } + return ""; +} + + +// Try to load data from local storage. If this fails, there's probably not any data to retrieve. +try { + let data = JSON.parse(getStored(localDataName)); + + if (data.version == null){ + loadSaveInfo(null, data); + } + else{ + loadSaveInfo(data, null) + } + + // Find out if we want to sort the array of the users data. + let userSortPref = getStored(localSortPreferenceName) + if (userSortPref === undefined){ + sortSubscriptionsByName() + } + else{ + if (userSortPref == "Price"){ + sortSubscriptionsByPrice() + } + else{ + sortSubscriptionsByName() + } + } + updateTable() +} catch (e) { + console.log("Didn't find any locally stored data") + console.log(e) +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..3fd5769 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es2018", + "sourceMap": true + }, + "exclude": [ + "node_modules" + ] +}
' + elem.title + '' + elem.monthly.toFixed(2) + '' + elem.currency + '' + elem.indkk.toFixed(2) + '' + elem.percentOfWhole.toFixed(2) + '%' + '' + '' + '' + '' + `` + '' + elem.title + '' + elem.monthly.toFixed(2) + '' + elem.currency + '' + elem.indkk.toFixed(2) + '' + elem.percentOfWhole.toFixed(2) + '%' + '' + '' + '' + '' + `` + `` + `` + ' ' + elem.title + '' + elem.monthly.toFixed(2) + '' + elem.currency + '' + elem.indkk.toFixed(2) + '' + elem.percentOfWhole.toFixed(2) + '%' + '' + '' + '' + '' + `` + '