From 579edb71df9c13fb45e0ddd948afd026bc8c5a19 Mon Sep 17 00:00:00 2001 From: Sarath Damaraju Date: Sun, 23 Feb 2020 21:27:22 +0530 Subject: [PATCH] chore: removes unused files and implements mathjs --- app/calculator.js | 215 +++++++++++++ app/dom.js | 220 +++++++++++++ app/index.css | 1 + app/index.html | 5 +- app/script.js | 410 ------------------------- constants/operators-as-text.js | 16 + {utils => deprecated}/cacheRates.js | 0 {utils => deprecated}/coreCalc.js | 0 {utils => deprecated}/coreCalc.test.js | 0 {utils => deprecated}/coreConv.js | 0 {utils => deprecated}/coreConv.test.js | 0 {utils => deprecated}/defaultRates.js | 0 {lib => deprecated}/money.js | 0 index.js | 10 - lib/_calculator.js | 47 +++ package.json | 5 +- utils/copy-to-clipboard.js | 14 + utils/main.js | 107 ------- utils/replace-all.js | 12 + 19 files changed, 531 insertions(+), 531 deletions(-) create mode 100644 app/calculator.js create mode 100644 app/dom.js delete mode 100644 app/script.js create mode 100644 constants/operators-as-text.js rename {utils => deprecated}/cacheRates.js (100%) rename {utils => deprecated}/coreCalc.js (100%) rename {utils => deprecated}/coreCalc.test.js (100%) rename {utils => deprecated}/coreConv.js (100%) rename {utils => deprecated}/coreConv.test.js (100%) rename {utils => deprecated}/defaultRates.js (100%) rename {lib => deprecated}/money.js (100%) create mode 100644 lib/_calculator.js create mode 100644 utils/copy-to-clipboard.js delete mode 100644 utils/main.js create mode 100644 utils/replace-all.js diff --git a/app/calculator.js b/app/calculator.js new file mode 100644 index 0000000..a6ffe33 --- /dev/null +++ b/app/calculator.js @@ -0,0 +1,215 @@ +'use strict'; + +const main = require('../lib/_calculator'); +const copyToClipboard = require('../utils/copy-to-clipboard'); + +const inputContainer = document.querySelectorAll('.app__input')[0]; +const outputContainer = document.querySelectorAll('.app__output')[0]; +const totalContainer = document.querySelector('#app__total__output'); +let equationsCollected = []; + +/** + * To split the input based on newline and evaluates + * @param {Object} textbox + */ +function getSelection(textbox) { + let selectedText = null; + const { activeElement } = document; + + // All browsers (including IE9 and up), except IE before version 9 + if ( + window.getSelection && + activeElement && + (activeElement.tagName.toLowerCase() === 'textarea' || + (activeElement.tagName.toLowerCase() === 'input' && + activeElement.type.toLowerCase() === 'text')) && + activeElement === textbox + ) { + const startIndex = textbox.selectionStart; + const endIndex = textbox.selectionEnd; + + if (endIndex - startIndex > 0) { + const text = textbox.value; + selectedText = text.substring( + textbox.selectionStart, + textbox.selectionEnd + ); + } + } else if ( + document.selection && + document.selection.type === 'Text' && + document.selection.createRange + ) { + // All Internet Explorer + const range = document.selection.createRange(); + selectedText = range.text; + } + + return selectedText; +} + +function getInputSelection(el) { + let start = 0; + let end = 0; + let normalizedValue; + let range; + let textInputRange; + let len; + let endRange; + + if ( + typeof el.selectionStart === 'number' && + typeof el.selectionEnd === 'number' + ) { + start = el.selectionStart; + end = el.selectionEnd; + } else { + range = document.selection.createRange(); + + if (range && range.parentElement() === el) { + len = el.value.length; + normalizedValue = el.value.replace(/\r\n/g, '\n'); + + // Create a working TextRange that lives only in the input + textInputRange = el.createTextRange(); + textInputRange.moveToBookmark(range.getBookmark()); + + // Check if the start and end of the selection are at the very end + // of the input, since moveStart/moveEnd doesn't return what we want + // in those cases + endRange = el.createTextRange(); + endRange.collapse(false); + + if (textInputRange.compareEndPoints('StartToEnd', endRange) > -1) { + start = len; + end = len; + } else { + start = -textInputRange.moveStart('character', -len); + start += normalizedValue.slice(0, start).split('\n').length - 1; + + if ( + textInputRange.compareEndPoints('EndToEnd', endRange) > -1 + ) { + end = len; + } else { + end = -textInputRange.moveEnd('character', -len); + end += normalizedValue.slice(0, end).split('\n').length - 1; + } + } + } + } + + return { + start, + end + }; +} + +function replaceSelectedText(keyCode, secondKeyCode = 0, reverse = false) { + const selection = getInputSelection(inputContainer); + const val = inputContainer.value; + if (secondKeyCode) { + if (reverse) { + inputContainer.value = + val.slice(0, selection.start) + + keyCode + + val.slice(selection.start, selection.end) + + secondKeyCode + + val.slice(selection.end); + } else { + inputContainer.value = + val.slice(0, selection.start) + + secondKeyCode + + val.slice(selection.start, selection.end) + + keyCode + + val.slice(selection.end); + } + } else { + inputContainer.value = + val.slice(0, selection.start) + + keyCode + + val.slice(selection.start, selection.end) + + keyCode + + val.slice(selection.end); + } +} + +function getKeyByValue(object, value) { + return Object.keys(object).find(key => object[key] === value); +} + +/** + * This function passes the data and updates the result on the markup + * @param {Array} arr - gets the expression by line as an array + * @private + */ + +// FIXME : Output position for multiline input +function evaluate(arr) { + const output = arr.map(each => main(each)); + outputContainer.innerText = ''; + let displayTotal = 0; + output.forEach(value => { + const result = document.createElement('p'); + result.className = '__output'; + if ( + Number(parseFloat(value)) === parseFloat(value) && + parseFloat(value) % 1 !== 0 + ) { + value = parseFloat(value); + result.innerText += Number( + value.toFixed(window.localStorage.decimalPoint) + ); + } else { + result.innerText += value; + } + + result.addEventListener('click', function() { + copyToClipboard(this); + }); + + outputContainer.append(result); + displayTotal += value; + totalContainer.innerText = displayTotal; + }); +} + +// Adding eventListeners +inputContainer.addEventListener('keydown', e => { + const quotesObj = { + '"': true, + "'": true + }; + const bracketsObj = { + '(': 1, + ')': 2, + '[': 3, + ']': 4, + '{': 5, + '}': 6 + }; + + if (getSelection(inputContainer) && quotesObj[e.key]) { + e.preventDefault(); + replaceSelectedText(e.key); + } else if (getSelection(inputContainer) && bracketsObj[e.key]) { + e.preventDefault(); + if (bracketsObj[e.key] % 2) { + replaceSelectedText( + e.key, + getKeyByValue(bracketsObj, bracketsObj[e.key] + 1), + true + ); + } else { + replaceSelectedText( + e.key, + getKeyByValue(bracketsObj, bracketsObj[e.key] - 1) + ); + } + } +}); + +inputContainer.addEventListener('keyup', e => { + equationsCollected = e.target.value.split('\n'); + evaluate(equationsCollected); +}); diff --git a/app/dom.js b/app/dom.js new file mode 100644 index 0000000..75dd148 --- /dev/null +++ b/app/dom.js @@ -0,0 +1,220 @@ +// Contains the DOM manipulations other than the calculator part + +const { remote } = require('electron'); + +/** #1 + * THEME CHOOSER + * By default OS theme + * user theme has high precedence + * OS theme and user theme applied then user theme + * user theme and OS theme applied then user theme + **/ +if (process.platform === 'darwin') { + const { systemPreferences } = remote; + + const defaultTheme = () => { + if ( + window.localStorage.userTheme === undefined || + window.localStorage.userTheme === 'auto' + ) { + window.localStorage.osTheme = systemPreferences.isDarkMode() + ? 'dark' + : 'light'; + + if ('loadTheme' in window) { + window.loadTheme(); + } + } + }; + + const defaultPoint = () => { + if (window.localStorage.decimalPoint === undefined) { + window.localStorage.decimalPoint = 4; + } + }; + + systemPreferences.subscribeNotification( + 'AppleInterfaceThemeChangedNotification', + defaultTheme + ); + + defaultTheme(); + defaultPoint(); +} + +/** #2 + * This function adds the window controls to the application + * @private + */ + +/** @const {Object} */ +const appPopup = document.querySelectorAll('.modal')[0]; + +(function() { + const { BrowserWindow } = require('electron').remote; + + function init() { + document + .querySelector('#app--minimize') + .addEventListener('click', () => { + const window = BrowserWindow.getFocusedWindow(); + window.minimize(); + }); + + document.querySelector('#app--close').addEventListener('click', () => { + const window = BrowserWindow.getFocusedWindow(); + window.close(); + }); + + document + .querySelector('#app--settings') + .addEventListener('click', () => { + appPopup.style.display = 'block'; + }); + + document + .querySelector('#modal__popup--close') + .addEventListener('click', () => { + appPopup.style.display = 'none'; + }); + + document + .querySelector('#theme-switcher') + .addEventListener('change', e => { + const userTheme = e.target.value; + if (userTheme === 'auto') { + document.documentElement.setAttribute( + 'data-theme', + window.localStorage.osTheme || 'light' + ); + } else { + document.documentElement.setAttribute( + 'data-theme', + userTheme + ); + } + + window.localStorage.userTheme = userTheme; + }); + + document + .querySelector('#decimal-switcher') + .addEventListener('change', e => { + const decimalPoint = e.target.value; + window.localStorage.decimalPoint = decimalPoint; + }); + } + + document.onreadystatechange = () => { + if (document.readyState === 'complete') { + init(); + const userTheme = + window.localStorage.userTheme || + window.localStorage.osTheme || + 'light'; + + const decimalPoint = window.localStorage.decimalPoint || 4; + + if (userTheme === 'auto') { + document.documentElement.setAttribute( + 'data-theme', + window.localStorage.osTheme || 'light' + ); + } else { + document.documentElement.setAttribute('data-theme', userTheme); + } + + document.querySelector('#theme-switcher').value = userTheme; + document.querySelector('#decimal-switcher').value = decimalPoint; + } + }; +})(); + +/** #3 + * Handling Resizable columns + */ +const getResizeableElement = () => { + return document.querySelector('.app__input'); +}; + +const getSecondResizeableElement = () => { + return document.querySelector('.app__output'); +}; + +const getHandleElement = () => { + return document.querySelector('#handle'); +}; + +const minPaneSize = 100; +let maxPaneSize = document.body.clientWidth * 0.75; +const minSecondPanelSize = 25; +getResizeableElement().style.setProperty('--max-width', `${maxPaneSize}px`); +getResizeableElement().style.setProperty('--min-width', `${minPaneSize}px`); + +const setPaneWidth = width => { + getResizeableElement().style.setProperty( + '--resizeable-width', + `${width}px` + ); + const secondWidth = + minSecondPanelSize + + ((maxPaneSize - + parseFloat( + getComputedStyle(getResizeableElement()).getPropertyValue( + '--resizeable-width' + ) + )) / + maxPaneSize) * + 100; + if (secondWidth >= minSecondPanelSize) { + getSecondResizeableElement().style.setProperty( + '--resizeable-width', + `${secondWidth}%` + ); + } +}; + +const getPaneWidth = () => { + const pxWidth = getComputedStyle(getResizeableElement()).getPropertyValue( + '--resizeable-width' + ); + return parseInt(pxWidth, 10); +}; + +const startDragging = event => { + event.preventDefault(); + const host = getResizeableElement(); + const startingPaneWidth = getPaneWidth(); + const xOffset = event.pageX; + + const mouseDragHandler = moveEvent => { + moveEvent.preventDefault(); + maxPaneSize = document.body.clientWidth * 0.75; + getResizeableElement().style.setProperty( + '--max-width', + `${maxPaneSize}px` + ); + + const primaryButtonPressed = moveEvent.buttons === 1; + if (!primaryButtonPressed) { + setPaneWidth( + Math.min(Math.max(getPaneWidth(), minPaneSize), maxPaneSize) + ); + document.body.removeEventListener('pointermove', mouseDragHandler); + return; + } + + const paneOriginAdjustment = 'left' === 'right' ? 1 : -1; + setPaneWidth( + (xOffset - moveEvent.pageX) * paneOriginAdjustment + + startingPaneWidth + ); + }; + + const remove = document.body.addEventListener( + 'pointermove', + mouseDragHandler + ); +}; + +getHandleElement().addEventListener('mousedown', startDragging); diff --git a/app/index.css b/app/index.css index 3034331..8be7b60 100644 --- a/app/index.css +++ b/app/index.css @@ -216,6 +216,7 @@ label::after { #app__total__output { margin: 0px 10px; + font-size: 0.75 rem; } /* Dark Mode */ diff --git a/app/index.html b/app/index.html index 4286ad8..568027f 100644 --- a/app/index.html +++ b/app/index.html @@ -61,7 +61,7 @@

More

- + @@ -92,7 +92,8 @@

Caligator

- + + diff --git a/app/script.js b/app/script.js deleted file mode 100644 index cd06b77..0000000 --- a/app/script.js +++ /dev/null @@ -1,410 +0,0 @@ -'use strict'; - -const { remote } = require('electron'); -const main = require('../utils/main'); - -// By default OS theme -// user theme has high precedence -// os theme and user theme applied then USer theme -// user theme and OS theme applied then User theme -if (process.platform === 'darwin') { - const { systemPreferences } = remote; - - const defaultTheme = () => { - if ( - window.localStorage.userTheme === undefined || - window.localStorage.userTheme === 'auto' - ) { - window.localStorage.osTheme = systemPreferences.isDarkMode() - ? 'dark' - : 'light'; - - if ('loadTheme' in window) { - window.loadTheme(); - } - } - }; - - const defaultPoint = () => { - if (window.localStorage.decimalPoint === undefined) { - window.localStorage.decimalPoint = 4; - } - }; - - systemPreferences.subscribeNotification( - 'AppleInterfaceThemeChangedNotification', - defaultTheme - ); - - defaultTheme(); - defaultPoint(); -} - -// Main - -/** @type {String} */ -const inputContainer = document.querySelectorAll('.app__input')[0]; - -/** @type {Object} */ -const outputContainer = document.querySelectorAll('.app__output')[0]; - -/** @type {Object} */ -const totalContainer = document.querySelector('#app__total__output'); - -/** @type {Array} */ -let equationsCollected = []; - -/** - * @event - * This function splits the input based on newline and evaluates - */ -function getSelection(textbox) { - let selectedText = null; - let activeElement = document.activeElement; - - // all browsers (including IE9 and up), except IE before version 9 - if ( - window.getSelection && - activeElement && - (activeElement.tagName.toLowerCase() == 'textarea' || - (activeElement.tagName.toLowerCase() == 'input' && - activeElement.type.toLowerCase() == 'text')) && - activeElement === textbox - ) { - let startIndex = textbox.selectionStart; - let endIndex = textbox.selectionEnd; - - if (endIndex - startIndex > 0) { - let text = textbox.value; - selectedText = text.substring( - textbox.selectionStart, - textbox.selectionEnd - ); - } - } else if ( - document.selection && - document.selection.type == 'Text' && - document.selection.createRange - ) { - // All Internet Explorer - let range = document.selection.createRange(); - selectedText = range.text; - } - - return selectedText; -} - -function getInputSelection(el) { - let start = 0, end = 0, normalizedValue, range, - textInputRange, len, endRange; - - if (typeof el.selectionStart == "number" && typeof el.selectionEnd == "number") { - start = el.selectionStart; - end = el.selectionEnd; - } else { - range = document.selection.createRange(); - - if (range && range.parentElement() == el) { - len = el.value.length; - normalizedValue = el.value.replace(/\r\n/g, "\n"); - - // Create a working TextRange that lives only in the input - textInputRange = el.createTextRange(); - textInputRange.moveToBookmark(range.getBookmark()); - - // Check if the start and end of the selection are at the very end - // of the input, since moveStart/moveEnd doesn't return what we want - // in those cases - endRange = el.createTextRange(); - endRange.collapse(false); - - if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) { - start = end = len; - } else { - start = -textInputRange.moveStart("character", -len); - start += normalizedValue.slice(0, start).split("\n").length - 1; - - if (textInputRange.compareEndPoints("EndToEnd", endRange) > -1) { - end = len; - } else { - end = -textInputRange.moveEnd("character", -len); - end += normalizedValue.slice(0, end).split("\n").length - 1; - } - } - } - } - - return { - start: start, - end: end - }; -} - -function replaceSelectedText(keyCode, secondKeyCode = 0, reverse = false) { - let selection = getInputSelection(inputContainer), val = inputContainer.value; - if(secondKeyCode){ - if(reverse){ - inputContainer.value = val.slice(0, selection.start) + keyCode + val.slice(selection.start,selection.end) + secondKeyCode + val.slice(selection.end); - } - else{ - inputContainer.value = val.slice(0, selection.start) + secondKeyCode + val.slice(selection.start,selection.end) + keyCode + val.slice(selection.end); - } - } - else{ - inputContainer.value = val.slice(0, selection.start) + keyCode + val.slice(selection.start,selection.end) + keyCode + val.slice(selection.end); - } -} - -function getKeyByValue(object, value) { - return Object.keys(object).find(key => object[key] === value); -} - -inputContainer.addEventListener('keydown', e => { - const quotesObj = { - "\"":true, - "\'":true - } - const bracketsObj = { - "(":1, - ")":2, - "[":3, - "]":4, - "{":5, - "}":6 - }; - - if(getSelection(inputContainer) && quotesObj[e.key]){ - e.preventDefault(); - replaceSelectedText(e.key) - } - else if(getSelection(inputContainer) && bracketsObj[e.key]){ - e.preventDefault(); - if(bracketsObj[e.key] % 2){ - replaceSelectedText(e.key, getKeyByValue(bracketsObj, bracketsObj[e.key]+1), true) - } - else{ - replaceSelectedText(e.key, getKeyByValue(bracketsObj, bracketsObj[e.key]-1)) - } - } -}) - -inputContainer.addEventListener('keyup', e => { - equationsCollected = e.target.value.split('\n'); - evaluate(equationsCollected); -}); - -/** - * This function passes the data and updates the result on the markup - * @param {Array} arr - gets the expression by line as an array - * @private - */ - -// FIXME : Output position for multiline input -function evaluate(arr) { - const output = arr.map(each => main(each)); - outputContainer.innerText = ''; - let displayTotal = 0; - output.forEach(value => { - const result = document.createElement('p'); - result.className = '__output'; - if ( - Number(parseFloat(value)) === parseFloat(value) && - parseFloat(value) % 1 !== 0 - ) { - value = parseFloat(value); - result.innerText += +value.toFixed( - window.localStorage.decimalPoint - ); - } else { - result.innerText += value; - } - result.addEventListener('click', function() { - copyClicked(this); - }); - - outputContainer.append(result); - displayTotal += value; - totalContainer.innerText = displayTotal; - }); -} - -// Controls - -/** @const {Object} */ -const appPopup = document.querySelectorAll('.modal')[0]; - -/** - * This function adds the window controls to the application - * @private - */ -(function() { - const { BrowserWindow } = require('electron').remote; - - function init() { - document - .querySelector('#app--minimize') - .addEventListener('click', () => { - const window = BrowserWindow.getFocusedWindow(); - window.minimize(); - }); - - document.querySelector('#app--close').addEventListener('click', () => { - const window = BrowserWindow.getFocusedWindow(); - window.close(); - }); - - document - .querySelector('#app--settings') - .addEventListener('click', () => { - appPopup.style.display = 'block'; - }); - - document - .querySelector('#modal__popup--close') - .addEventListener('click', () => { - appPopup.style.display = 'none'; - }); - - document - .querySelector('#theme-switcher') - .addEventListener('change', e => { - const userTheme = e.target.value; - if (userTheme === 'auto') { - document.documentElement.setAttribute( - 'data-theme', - window.localStorage.osTheme || 'light' - ); - } else { - document.documentElement.setAttribute( - 'data-theme', - userTheme - ); - } - - window.localStorage.userTheme = userTheme; - }); - - document - .querySelector('#decimal-switcher') - .addEventListener('change', e => { - const decimalPoint = e.target.value; - window.localStorage.decimalPoint = decimalPoint; - }); - } - - document.onreadystatechange = () => { - if (document.readyState === 'complete') { - init(); - const userTheme = - window.localStorage.userTheme || - window.localStorage.osTheme || - 'light'; - - const decimalPoint = window.localStorage.decimalPoint || 4; - - if (userTheme === 'auto') { - document.documentElement.setAttribute( - 'data-theme', - window.localStorage.osTheme || 'light' - ); - } else { - document.documentElement.setAttribute('data-theme', userTheme); - } - - document.querySelector('#theme-switcher').value = userTheme; - document.querySelector('#decimal-switcher').value = decimalPoint; - } - }; -})(); - -// Function to Copy to clipboard, on clicking an output element. -function copyClicked(p_output_element) { - const el = document.createElement('textarea'); - el.value = p_output_element.innerText; - document.body.appendChild(el); - el.select(); - document.execCommand('copy'); - document.body.removeChild(el); -} - -const getResizeableElement = () => { - return document.querySelector('.app__input'); -}; -const getSecondResizeableElement = () => { - return document.querySelector('.app__output'); -}; -const getHandleElement = () => { - return document.getElementById('handle'); -}; -const minPaneSize = 100; -let maxPaneSize = document.body.clientWidth * 0.75; -const minSecondPanelSize = 25; -getResizeableElement().style.setProperty('--max-width', `${maxPaneSize}px`); -getResizeableElement().style.setProperty('--min-width', `${minPaneSize}px`); - -const setPaneWidth = width => { - getResizeableElement().style.setProperty( - '--resizeable-width', - `${width}px` - ); - let secondWidth = - minSecondPanelSize + - ((maxPaneSize - - parseFloat( - getComputedStyle(getResizeableElement()).getPropertyValue( - '--resizeable-width' - ) - )) / - maxPaneSize) * - 100; - if (secondWidth >= minSecondPanelSize) { - getSecondResizeableElement().style.setProperty( - '--resizeable-width', - `${secondWidth}%` - ); - } -}; - -const getPaneWidth = () => { - const pxWidth = getComputedStyle(getResizeableElement()).getPropertyValue( - '--resizeable-width' - ); - return parseInt(pxWidth, 10); -}; - -const startDragging = event => { - event.preventDefault(); - const host = getResizeableElement(); - const startingPaneWidth = getPaneWidth(); - const xOffset = event.pageX; - - const mouseDragHandler = moveEvent => { - moveEvent.preventDefault(); - maxPaneSize = document.body.clientWidth * 0.75; - getResizeableElement().style.setProperty( - '--max-width', - `${maxPaneSize}px` - ); - - const primaryButtonPressed = moveEvent.buttons === 1; - if (!primaryButtonPressed) { - setPaneWidth( - Math.min(Math.max(getPaneWidth(), minPaneSize), maxPaneSize) - ); - document.body.removeEventListener('pointermove', mouseDragHandler); - return; - } - - const paneOriginAdjustment = 'left' === 'right' ? 1 : -1; - setPaneWidth( - (xOffset - moveEvent.pageX) * paneOriginAdjustment + - startingPaneWidth - ); - }; - const remove = document.body.addEventListener( - 'pointermove', - mouseDragHandler - ); -}; - -getHandleElement().addEventListener('mousedown', startDragging); diff --git a/constants/operators-as-text.js b/constants/operators-as-text.js new file mode 100644 index 0000000..062a183 --- /dev/null +++ b/constants/operators-as-text.js @@ -0,0 +1,16 @@ +const operatorsAsText = { + plus: '+', + 'added to': '+', + adds: '+', + with: '+', + minus: '-', + subtract: '-', + less: '-', + 'divided by': '/', + by: '/', + 'multiplied by': '*', + into: '*', + cross: '*' +}; + +module.exports = operatorsAsText; diff --git a/utils/cacheRates.js b/deprecated/cacheRates.js similarity index 100% rename from utils/cacheRates.js rename to deprecated/cacheRates.js diff --git a/utils/coreCalc.js b/deprecated/coreCalc.js similarity index 100% rename from utils/coreCalc.js rename to deprecated/coreCalc.js diff --git a/utils/coreCalc.test.js b/deprecated/coreCalc.test.js similarity index 100% rename from utils/coreCalc.test.js rename to deprecated/coreCalc.test.js diff --git a/utils/coreConv.js b/deprecated/coreConv.js similarity index 100% rename from utils/coreConv.js rename to deprecated/coreConv.js diff --git a/utils/coreConv.test.js b/deprecated/coreConv.test.js similarity index 100% rename from utils/coreConv.test.js rename to deprecated/coreConv.test.js diff --git a/utils/defaultRates.js b/deprecated/defaultRates.js similarity index 100% rename from utils/defaultRates.js rename to deprecated/defaultRates.js diff --git a/lib/money.js b/deprecated/money.js similarity index 100% rename from lib/money.js rename to deprecated/money.js diff --git a/index.js b/index.js index 944f6df..edef79a 100644 --- a/index.js +++ b/index.js @@ -1,14 +1,11 @@ 'use strict'; const path = require('path'); const { app, BrowserWindow, Menu } = require('electron'); -/// const {autoUpdater} = require('electron-updater'); const { is } = require('electron-util'); const unhandled = require('electron-unhandled'); const debug = require('electron-debug'); const contextMenu = require('electron-context-menu'); -const config = require('./store'); const menu = require('./menu'); -const cacheRates = require('./utils/cacheRates'); unhandled(); debug(); @@ -43,8 +40,6 @@ const createMainWindow = async () => { win.on('ready-to-show', async () => { win.show(); - - await cacheRates(config); }); win.on('closed', () => { @@ -89,9 +84,4 @@ app.on('activate', async () => { await app.whenReady(); Menu.setApplicationMenu(menu); mainWindow = await createMainWindow(); - - // const favoriteCalculator = config.get('favoriteCalculator'); - // mainWindow.webContents.executeJavaScript( - // `document.querySelector('header p').textContent = 'Your favorite calculator is ${favoriteCalculator}'` - // ); })(); diff --git a/lib/_calculator.js b/lib/_calculator.js new file mode 100644 index 0000000..8d54966 --- /dev/null +++ b/lib/_calculator.js @@ -0,0 +1,47 @@ +'use strict'; + +const mathJs = require('mathjs'); +const operatorsAsText = require('../constants/operators-as-text'); +const replaceAll = require('../utils/replace-all'); + +/** @const {object} */ +const commentRegExp = new RegExp(/^(\s*)#+(.*)/, 'm'); + +/** + * This is main function which parses and sends the values to the core modules + * @param {string} exp - provides user input, that can be an equation or conversion. But not both, yet. + * @returns {number} + */ +const evaluate = exp => { + if (exp) { + exp = exp.trim(); + + // 1. Check for comments + if (commentRegExp.test(exp)) return ''; + + // 2. Replace the text alternatives for arithmetic operators + Object.keys(operatorsAsText).forEach(operator => { + if (exp.includes(operator)) { + exp = replaceAll(exp, operator, operatorsAsText[operator]); + } + }); + + return mathJs.evaluate(exp); + } + + return ''; +}; + +const main = exp => { + try { + if (typeof evaluate(exp) !== 'function') { + return evaluate(exp); + } + + return ''; + } catch (error) { + return ''; + } +}; + +module.exports = main; diff --git a/package.json b/package.json index 5281152..285ac64 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ }, "scripts": { "postinstall": "electron-builder install-app-deps", - "lint": "xo", + "lint": "xo --fix", "test": "xo && npm run lint", "start": "electron .", "pack": "electron-builder --dir", @@ -50,7 +50,8 @@ "envs": [ "node", "browser" - ] + ], + "ignore": ["deprecated/**.js"] }, "np": { "publish": false, diff --git a/utils/copy-to-clipboard.js b/utils/copy-to-clipboard.js new file mode 100644 index 0000000..2043d70 --- /dev/null +++ b/utils/copy-to-clipboard.js @@ -0,0 +1,14 @@ +/** + * Function to Copy to clipboard, on clicking an output element. + * @param {*} selected dom element to be copied + */ +function copyToClipboard(selected) { + const el = document.createElement('textarea'); + el.value = selected.innerText; + document.body.append(el); + el.select(); + document.execCommand('copy'); + document.body.removeChild(el); +} + +module.exports = copyToClipboard; diff --git a/utils/main.js b/utils/main.js deleted file mode 100644 index 6096b91..0000000 --- a/utils/main.js +++ /dev/null @@ -1,107 +0,0 @@ -'use strict'; - -const mathJs = require('mathjs'); -const coreConv = require('./coreConv'); - -/** @const {Object} */ -const textForOperators = { - plus: '+', - 'added to': '+', - adds: '+', - with: '+', - minus: '-', - subtract: '-', - less: '-', - 'divided by': '/', - by: '/', - 'multiplied by': '*', - into: '*', - cross: '*' -}; - -/** @const {string} */ -const currencyUnits = Object.keys(coreConv.currencyUnits).join('|'); - -/** @const {object} */ -const commentRegExp = new RegExp(/^(\s*)#+(.*)/, 'm'); - -/** - * This function generates a RegExp for the given units - * @example generate(km|cm|in) - * @param {string} units - list of the conversion units - * @private - * @returns {object} - */ -const generateRegExpForUnits = units => - new RegExp( - `^(\\d+\\.?\\d*?\\s*)(${units})\\s*(to|TO)\\s*(${units})\\s*$`, - 'm' - ); - -/** @const {object} */ -const currencyRegExp = generateRegExpForUnits(currencyUnits); - -/** - * This function filters the given value with - * filter conditions : null, undefined, empty or to - * return false if it meets any of the above conditions - * @param {*} v - value which is filtered for null, undefined, empty or to. - * @returns {boolean} - result after filtering - * @private - * @returns {object} - */ -const filterValues = v => - v !== null && v !== undefined && v !== '' && v !== 'to' && v !== 'TO'; - -/** - * This function parses the given expression with the provided regExp and passes the values to the core modules - * @param {string} inp - each - * @param {object} type - regExp type - * @param {string} unit - conversion for 'l', 'c', 'w', 't', 'r', 'p' for length, currency and weight. check coreConv.convert(mode) - * @returns {number} - */ -const parseExp = (inp, type, unit) => { - inp = inp.split(type).filter(filterValues); - const result = coreConv.convert(unit, ...inp); - return result; -}; - -/** - * This is main function which parses and sends the values to the core modules - * @param {string} exp - provides user input, that can be an equation or conversion. But not both, yet. - * @returns {number} - */ - -// TODO: refactor -const evaluate = exp => { - exp = exp.trim(); - - // Ignores if starts with # - if (commentRegExp.test(exp)) return ''; - - // Replaces the text alternatives for operators - Object.keys(textForOperators).forEach(operator => { - const operatorRegExp = new RegExp(`\\d+\s*${operator}\\s*`, 'm'); - exp = exp.replace(operatorRegExp, textForOperators[operator]); - }); - - if (currencyRegExp.test(exp.toUpperCase())) { - return parseExp(exp.toUpperCase(), currencyRegExp, 'c'); - } - - return mathJs.evaluate(exp); -}; - -const main = exp => { - try { - return evaluate(exp) - ? typeof evaluate(exp) !== 'function' // To filter function printing - ? evaluate(exp) - : '' - : ''; - } catch (error) { - return ''; - } -}; - -module.exports = main; diff --git a/utils/replace-all.js b/utils/replace-all.js new file mode 100644 index 0000000..f60142d --- /dev/null +++ b/utils/replace-all.js @@ -0,0 +1,12 @@ +/** + * To find and replace the any occurrence of a given string with the replacement + * @param {string} str string to be searched and replaced + * @param {string} find string to be replaced + * @param {string} replacement replacement string + * @returns {string} replaced string or original string + */ +const replaceAll = (str, find, replacement) => { + return str && find && replacement ? str.split(find).join(replacement) : str; +}; + +module.exports = replaceAll;