Compare commits

...

11 Commits
0.0.9 ... 1.1.0

Author SHA1 Message Date
Nathan Parikh
b34ab0f32c Windows 1.1.0 build 2017-09-16 00:21:29 -05:00
Nathan Parikh
276a52f87c 1.1.0 - Windows build. Also removed old version from repo 2017-09-16 00:01:21 -05:00
Nathan Parikh
7f0829f8ad version 1.1.0 - code only, no build yet 2017-09-15 23:57:03 -05:00
Nathan Parikh
386deb7a10 added pin to top functionality 2017-09-15 23:15:29 -05:00
Nathan Parikh
0f1b2b3d86 more portfolio updates 2017-09-13 10:03:34 -05:00
Nathan Parikh
6da0f8f5bd Added icons for main and portfolio screens. More portfolio updates. Portfolio coin order now matches main screen order. 2017-09-12 23:32:55 -05:00
Nathan Parikh
1207f6a09d Added working doughnut chart to portfolio 2017-09-12 00:22:21 -05:00
Nathan Parikh
0f92fb17f5 Added working portfolio 2017-09-11 23:26:20 -05:00
Nathan Parikh
3a8dbd63f0 Separated markup, logic, and styling. Also fixed issue #3 2017-09-11 16:17:34 -05:00
Nathan Parikh
8684427f76 Finalized macOS 1.0.0 build 2017-09-10 22:39:36 -05:00
Nathan Parikh
afb1836e1d Adding Windows 1.0.0 package 2017-09-08 23:13:24 -05:00
16 changed files with 1350 additions and 711 deletions

View File

@@ -36,10 +36,6 @@ Crypto Price Widget is an open source side project. To support development and k
No, Crypto Price Widget is completely client-side and doesn't hold any keys. All code is open source.
**How will you make money?**
I won't, but donations are welcome :)
**When can I have a Linux version?**
Linux coming soon!

408
css/app.css Normal file
View File

@@ -0,0 +1,408 @@
body {
background: rgba(0, 0, 0, 0.95);
font-family: 'Inconsolata', monospace;
color: #fff;
margin-top: 5px;
}
ul {
margin: 0;
padding: 0;
}
.titlebar {
-webkit-user-select: none;
-webkit-app-region: drag;
opacity: 0;
}
.titlebar:hover {
opacity: 1;
}
.titlebar .controls {
float: right;
line-height: 0;
}
button {
-webkit-app-region: no-drag;
background: none;
border: none;
outline: none;
}
#close-btn,
#min-btn {
height: 12px;
width: 12px;
background: none;
border: 2px solid #000;
-moz-border-radius: 50px;
-webkit-border-radius: 50px;
border-radius: 50px;
padding: 0;
}
#close-btn {
border-color: #ff2626;
}
#close-btn:hover {
background: #ff2626;
}
#min-btn {
border-color: #ffbd45;
}
#min-btn:hover {
background: #ffbd45;
}
.tabs button {
padding: 0;
}
.tabs button,
.tabs button.active {
display: inline-block;
}
.tabs button img {
width: 19px;
opacity: 0.5;
}
.tabs button:hover img,
.tabs button.active img {
opacity: 1;
}
#portfolio-btn, #main-btn {
color: #fff;
}
.panel {
display: none;
}
ul {
list-style-type: none;
}
.coin-list {
margin: 0;
padding: 0;
font-size: 28px;
font-weight: 400;
}
#portfolio-list.coin-list {
font-size: 16px;
padding-top: 9px;
}
.coin-list li {
margin: 0px 0px 15px 0px;
padding: 0px 0px 15px 0px;
border-bottom: 1px solid #252525;
}
#portfolio-list.coin-list li {
margin-bottom: 10px;
padding-bottom: 0px;
}
.coin-list li:last-child {
border-bottom: none;
}
.coin-list li span {
}
.coin-list li span.draggable {
width: 100%;
}
.coin-list input[type="number"] {
border: none;
padding: 5px;
background: rgba(0, 0, 0, 0);
color: #fff;
max-width: 85px;
display: inline-block;
outline: none;
}
.coin-list .block {
display: inline-block;
vertical-align: top;
}
.coin-list .block label {
font-size: 12px;
display: block;
/*background: rgba(255, 255, 255, 0.1);*/
padding: 1px 2px 2px;
}
.coin-list .block .quantity-value {
color: #b2ff93;
}
.coin-list li .sym {
border: 1px solid #252525;
border-left-width: 2px;
padding: 0px 5px 0px;
font-family: 'Heebo', sans-serif;
font-size: 12px;
}
.coin-list li .sym:hover {
cursor: -webkit-grab;
}
/*Symbol Colors*/
.coin-list li#coin-BTC .sym {
border-left-color: #F9A847;
}
.coin-list li#coin-XRP .sym {
border-left-color: #0997D2;
}
.coin-list li#coin-LTC .sym {
border-left-color: #F1F1F1;
}
.coin-list li#coin-NEO .sym,
.coin-list li#coin-GAS .sym {
border-left-color: #9CD115;
}
.coin-list li#coin-OMG .sym {
border-left-color: #1A53F0;
}
.coin-list li#coin-BCH .sym {
border-left-color: #F7931A;
}
.coin-list li#coin-DASH .sym {
border-left-color: #0475B6;
}
.coin-list li#coin-XMR .sym {
border-left-color: #FF6600;
}
.coin-list li#coin-ETC .sym {
border-left-color: #689274;
}
.coin-list li#coin-ZEC .sym {
border-left-color: #EFB948;
}
.coin-list li#coin-GNT .sym {
border-left-color: #00AFBF;
}
.coin-list li#coin-BAT .sym {
border-left-color: #662F92;
}
.coin-list li#coin-FCT .sym {
border-left-color: #E3A77D;
}
.coin-list li#coin-ARK .sym {
border-left-color: #CB0101;
}
.coin-list li#coin-DOGE .sym {
border-left-color: #BBA034;
}
.coin-list li#coin-CVC .sym {
border-left-color: #41BB2E;
}
.coin-list li#coin-MCO .sym {
border-left-color: #82344C;
}
.coin-list li#coin-UBQ .sym {
border-left-color: #00EA90;
}
.coin-list li#coin-DNT .sym {
border-left-color: #7CF7FA;
}
.coin-list li .change {
padding: 2px 3px 2px;
font-size: 12px;
float: right;
margin: 10px 0px 0px;
background: #000;
}
.coin-list li .change.positive {
color: #b2ff93;
}
.coin-list li .change.negative {
color: #ff6765;
}
.active {
display: block;
}
.inactive {
display: none;
}
/*Settings Page*/
#settings h3:first-child {
margin-top: 5px;
}
#myInput {
font-family: 'Inconsolata', monospace;
border: none;
padding: 0px 0px 10px;
font-size: 14px;
background: rgba(0, 0, 0, 0);
color: #fff;
outline: none;
border-bottom: 1px solid #252525;
display: block;
width: 100%;
}
#saveCoins,
#saveQuantities {
background-color: #000000;
color: #fff;
border: 1px solid #252525;
padding: 5px 10px;
margin: -1px 0px 0px 0px;
font-family: 'Inconsolata', monospace;
}
#coinlist {
margin: 15px 0px 0px 0px;
padding: 0;
max-height: 218px;
overflow-y: scroll;
}
#coinlist li {
position: relative;
margin: 0px 0px 5px 0px;
}
/* Custom checkboxes inspired by https://codepen.io/sderoij/pen/VvJJwE */
.checkbox-wrapper {
position: relative;
display: block;
margin: 15px 0px 0px;
}
#coinlist label,
.checkbox-styled-label {
height: auto;
width: 100%;
z-index: 0;
display: inline-block;
position: absolute;
top: 0;
left: 0;
text-indent: 24px;
}
#coinlist label div,
.checkbox-styled-label div {
height: 12px;
width: 12px;
border: solid 2px rgba(255, 255, 255, 0.6);
margin: 0;
border-radius: 50%;
transform: rotate(45deg);
transition: all 0ms ease-in-out, border 0ms ease 0ms;
position: absolute;
top: 0;
}
#coinlist input:hover + label div,
.checkbox-styled:hover + label div {
border-color: rgba(138, 255, 131, 0.9);
}
#coinlist input,
.checkbox-styled {
height: auto;
width: 18px;
margin: 0;
opacity: 0;
z-index: 1;
position: relative;
cursor: pointer;
}
#coinlist input:checked + label > div,
.checkbox-styled:checked + label > div {
border-radius: 0;
border-top: 0;
border-left: 0;
border-color: rgba(138, 255, 131, 0.9);
height: 15px;
width: 12px;
margin-top: -4px;
margin-left: 0px;
transform: rotate(40deg);
transition: all 0ms ease-in-out;
}
.checkbox-styled:checked + label > div {
width: 8px;
margin-left: 4px;
}
#tips {
font-size: 12px;
}
#tips li {
}
.creds {
font-size: 8px;
color: #252525;
margin: 15px 0px 0px 0px;
}
.creds a {
text-decoration: none;
color: #252525;
}
/*Portfolio*/
#portfolio-total-value {
text-align: center;
font-size: 1.5em;
font-weight: 600;
margin: 15px 0px;
}
#portfolioChart {
max-height: 480px;
max-width: 100%;
}
/*Scrollbar*/
::-webkit-scrollbar-corner {
background-color: #000;
}
::-webkit-scrollbar {
background-color: rgba(0, 0, 0, 100);
}
::-webkit-scrollbar {
width: .5em;
height: .5em;
}
::-webkit-scrollbar-thumb:window-inactive,
::-webkit-scrollbar-thumb {
background: #252525;
-webkit-border-radius: 100px;
}
/*Select Boxes*/
.custom-select {
position: relative;
display: inline-block;
}
.custom-select select {
display: inline-block;
border: 1px solid #252525;
padding: 4px 3px 3px 5px;
margin: 0;
font: inherit;
outline: none;
line-height: 1.2;
background: #000000;
-webkit-appearance: none;
width: 145px;
color: #fff;
}
/* for Webkit's CSS-only solution */
@media screen and (-webkit-min-device-pixel-ratio:0) {
.custom-select select {
padding-right:30px;
}
}
/* Since we removed the default focus styles, we have to add our own */
.custom-select select:focus {
-webkit-box-shadow: 0 0 3px 1px #00afc1;
-moz-box-shadow: 0 0 3px 1px #00afc1;
box-shadow: 0 0 3px 1px #00afc1;
}
/* Select arrow styling */
.custom-select:after {
content: "▼";
position: absolute;
top: 0;
right: 0;
bottom: 0;
font-size: 60%;
line-height: 30px;
padding: 0 7px;
background: #252525;
color: white;
pointer-events: none;
}

View File

@@ -0,0 +1,108 @@
/* line 6, ../sass/_content.sass */
.offline-ui .offline-ui-retry:before {
content: "Reconnect";
}
/* line 11, ../sass/_content.sass */
.offline-ui.offline-ui-up .offline-ui-content:before {
content: "Your computer is connected to the internet.";
}
@media (max-width: 1024px) {
/* line 11, ../sass/_content.sass */
.offline-ui.offline-ui-up .offline-ui-content:before {
content: "Your device is connected to the internet.";
}
}
@media (max-width: 568px) {
/* line 11, ../sass/_content.sass */
.offline-ui.offline-ui-up .offline-ui-content:before {
content: "Your device is connected.";
}
}
/* line 22, ../sass/_content.sass */
.offline-ui.offline-ui-down .offline-ui-content:before {
content: "Your computer lost its internet connection.";
}
@media (max-width: 1024px) {
/* line 22, ../sass/_content.sass */
.offline-ui.offline-ui-down .offline-ui-content:before {
content: "Your device lost its internet connection.";
}
}
@media (max-width: 568px) {
/* line 22, ../sass/_content.sass */
.offline-ui.offline-ui-down .offline-ui-content:before {
content: "Your device isn't connected.";
}
}
/* line 33, ../sass/_content.sass */
.offline-ui.offline-ui-down.offline-ui-connecting .offline-ui-content:before, .offline-ui.offline-ui-down.offline-ui-connecting-2s .offline-ui-content:before {
content: "Attempting to reconnect...";
}
/* line 42, ../sass/_content.sass */
.offline-ui.offline-ui-down.offline-ui-waiting .offline-ui-content[data-retry-in-unit="second"]:before {
content: "Connection lost. Reconnecting in " attr(data-retry-in-value) " seconds...";
}
@media (max-width: 568px) {
/* line 42, ../sass/_content.sass */
.offline-ui.offline-ui-down.offline-ui-waiting .offline-ui-content[data-retry-in-unit="second"]:before {
content: "Reconnecting in " attr(data-retry-in-value) "s...";
}
}
/* line 50, ../sass/_content.sass */
.offline-ui.offline-ui-down.offline-ui-waiting .offline-ui-content[data-retry-in-unit="second"][data-retry-in-value="1"]:before {
content: "Connection lost. Reconnecting in " attr(data-retry-in-value) " second...";
}
@media (max-width: 568px) {
/* line 50, ../sass/_content.sass */
.offline-ui.offline-ui-down.offline-ui-waiting .offline-ui-content[data-retry-in-unit="second"][data-retry-in-value="1"]:before {
content: "Reconnecting in " attr(data-retry-in-value) "s...";
}
}
/* line 58, ../sass/_content.sass */
.offline-ui.offline-ui-down.offline-ui-waiting .offline-ui-content[data-retry-in-unit="minute"]:before {
content: "Connection lost. Reconnecting in " attr(data-retry-in-value) " minutes...";
}
@media (max-width: 568px) {
/* line 58, ../sass/_content.sass */
.offline-ui.offline-ui-down.offline-ui-waiting .offline-ui-content[data-retry-in-unit="minute"]:before {
content: "Reconnecting in " attr(data-retry-in-value) "m...";
}
}
/* line 66, ../sass/_content.sass */
.offline-ui.offline-ui-down.offline-ui-waiting .offline-ui-content[data-retry-in-unit="minute"][data-retry-in-value="1"]:before {
content: "Connection lost. Reconnecting in " attr(data-retry-in-value) " minute...";
}
@media (max-width: 568px) {
/* line 66, ../sass/_content.sass */
.offline-ui.offline-ui-down.offline-ui-waiting .offline-ui-content[data-retry-in-unit="minute"][data-retry-in-value="1"]:before {
content: "Reconnecting in " attr(data-retry-in-value) "m...";
}
}
/* line 74, ../sass/_content.sass */
.offline-ui.offline-ui-down.offline-ui-waiting .offline-ui-content[data-retry-in-unit="hour"]:before {
content: "Connection lost. Reconnecting in " attr(data-retry-in-value) " hours...";
}
@media (max-width: 568px) {
/* line 74, ../sass/_content.sass */
.offline-ui.offline-ui-down.offline-ui-waiting .offline-ui-content[data-retry-in-unit="hour"]:before {
content: "Reconnecting in " attr(data-retry-in-value) "h...";
}
}
/* line 82, ../sass/_content.sass */
.offline-ui.offline-ui-down.offline-ui-waiting .offline-ui-content[data-retry-in-unit="hour"][data-retry-in-value="1"]:before {
content: "Connection lost. Reconnecting in " attr(data-retry-in-value) " hour...";
}
@media (max-width: 568px) {
/* line 82, ../sass/_content.sass */
.offline-ui.offline-ui-down.offline-ui-waiting .offline-ui-content[data-retry-in-unit="hour"][data-retry-in-value="1"]:before {
content: "Reconnecting in " attr(data-retry-in-value) "h...";
}
}
/* line 90, ../sass/_content.sass */
.offline-ui.offline-ui-down.offline-ui-reconnect-failed-2s.offline-ui-waiting .offline-ui-retry {
display: none;
}
/* line 93, ../sass/_content.sass */
.offline-ui.offline-ui-down.offline-ui-reconnect-failed-2s .offline-ui-content:before {
content: "Connection attempt failed.";
}

View File

@@ -0,0 +1,75 @@
/* line 3, ../sass/_offline-theme-base-indicator.sass */
.offline-ui, .offline-ui *, .offline-ui:before, .offline-ui:after, .offline-ui *:before, .offline-ui *:after {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
/* line 6, ../sass/_offline-theme-base-indicator.sass */
.offline-ui {
display: none;
position: fixed;
background: white;
z-index: 2000;
display: inline-block;
}
/* line 13, ../sass/_offline-theme-base-indicator.sass */
.offline-ui .offline-ui-retry {
display: none;
}
/* line 16, ../sass/_offline-theme-base-indicator.sass */
.offline-ui.offline-ui-up {
display: block;
}
/* line 19, ../sass/_offline-theme-base-indicator.sass */
.offline-ui.offline-ui-down {
display: block;
}
/* line 8, ../sass/offline-theme-dark-indicator.sass */
.offline-ui {
-webkit-box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15);
-moz-box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15);
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15);
-webkit-border-radius: 4px 4px 0 0;
-moz-border-radius: 4px 4px 0 0;
-ms-border-radius: 4px 4px 0 0;
-o-border-radius: 4px 4px 0 0;
border-radius: 4px 4px 0 0;
font-family: "Helvetica Neue", sans-serif;
font-weight: 300;
padding: 1em;
background: black;
color: #cccccc;
bottom: 0;
left: 20px;
}
/* line 19, ../sass/offline-theme-dark-indicator.sass */
.offline-ui .offline-ui-content {
padding-left: 1.5em;
}
/* line 22, ../sass/offline-theme-dark-indicator.sass */
.offline-ui .offline-ui-content:after {
-webkit-border-radius: 50%;
-moz-border-radius: 50%;
-ms-border-radius: 50%;
-o-border-radius: 50%;
border-radius: 50%;
content: " ";
display: block;
position: absolute;
top: 0;
bottom: 0;
left: 1em;
margin: auto;
height: 0.8em;
width: 0.8em;
}
/* line 36, ../sass/offline-theme-dark-indicator.sass */
.offline-ui.offline-ui-up .offline-ui-content:after {
background: #80d580;
}
/* line 41, ../sass/offline-theme-dark-indicator.sass */
.offline-ui.offline-ui-down .offline-ui-content:after {
background: #e24949;
}

BIN
images/icons8-Home-64.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 600 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -5,345 +5,7 @@
<title>Latest Crypto Prices</title>
<link href="https://fonts.googleapis.com/css?family=Heebo:100,400" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Inconsolata" rel="stylesheet">
<style type="text/css">
/*@import url('https://rsms.me/interface/interface.css');*/
body {
background: rgba(0, 0, 0, 0.95);
font-family: 'Inconsolata', monospace;
color: #fff;
margin-top: 5px;
}
ul {
margin: 0;
padding: 0;
}
.titlebar {
-webkit-user-select: none;
-webkit-app-region: drag;
opacity: 0;
}
.titlebar:hover {
opacity: 1;
}
.titlebar .controls {
float: right;
line-height: 0;
}
button {
-webkit-app-region: no-drag;
background: none;
border: none;
outline: none;
}
#close-btn,
#min-btn {
height: 12px;
width: 12px;
background: none;
border: 2px solid #000;
-moz-border-radius: 50px;
-webkit-border-radius: 50px;
border-radius: 50px;
padding: 0;
}
#close-btn {
border-color: #ff2626;
}
#close-btn:hover {
background: #ff2626;
}
#min-btn {
border-color: #ffbd45;
}
#min-btn:hover {
background: #ffbd45;
}
#settings-btn {
padding: 0;
}
#settings-btn img {
width: 24px;
opacity: 0.5;
}
#settings-btn:hover img {
opacity: 1;
}
ul {
list-style-type: none;
}
#prices {
margin: 0;
padding: 0;
font-size: 28px;
font-weight: 400;
}
#prices li {
margin: 0px 0px 15px 0px;
padding: 0px 0px 15px 0px;
border-bottom: 1px solid #252525;
}
#prices li:last-child {
border-bottom: none;
}
#prices li span {
}
#prices li span.draggable {
width: 100%;
}
#prices li .sym {
border: 1px solid #252525;
border-left-width: 2px;
padding: 0px 5px 0px;
font-family: 'Heebo', sans-serif;
font-size: 12px;
}
#prices li .sym:hover {
cursor: -webkit-grab;
}
/*Symbol Colors*/
#prices li#coin-BTC .sym {
border-left-color: #F9A847;
}
#prices li#coin-XRP .sym {
border-left-color: #0997D2;
}
#prices li#coin-LTC .sym {
border-left-color: #F1F1F1;
}
#prices li#coin-NEO .sym,
#prices li#coin-GAS .sym {
border-left-color: #9CD115;
}
#prices li#coin-OMG .sym {
border-left-color: #1A53F0;
}
#prices li#coin-BCH .sym {
border-left-color: #F7931A;
}
#prices li#coin-DASH .sym {
border-left-color: #0475B6;
}
#prices li#coin-XMR .sym {
border-left-color: #FF6600;
}
#prices li#coin-ETC .sym {
border-left-color: #689274;
}
#prices li#coin-ZEC .sym {
border-left-color: #EFB948;
}
#prices li#coin-GNT .sym {
border-left-color: #00AFBF;
}
#prices li#coin-BAT .sym {
border-left-color: #662F92;
}
#prices li#coin-FCT .sym {
border-left-color: #E3A77D;
}
#prices li#coin-ARK .sym {
border-left-color: #CB0101;
}
#prices li#coin-DOGE .sym {
border-left-color: #BBA034;
}
#prices li#coin-CVC .sym {
border-left-color: #41BB2E;
}
#prices li#coin-MCO .sym {
border-left-color: #82344C;
}
#prices li#coin-UBQ .sym {
border-left-color: #00EA90;
}
#prices li#coin-DNT .sym {
border-left-color: #7CF7FA;
}
#prices li .change {
padding: 2px 3px 2px;
font-size: 12px;
float: right;
margin: 10px 0px 0px;
background: #000;
}
#prices li .change.positive {
color: #b2ff93;
}
#prices li .change.negative {
color: #ff6765;
}
.active {
display: block;
}
.inactive {
display: none;
}
/*Settings Page*/
#settings h3:first-child {
margin-top: 5px;
}
#myInput {
font-family: 'Inconsolata', monospace;
border: none;
padding: 0px 0px 10px;
font-size: 14px;
background: rgba(0, 0, 0, 0);
color: #fff;
outline: none;
border-bottom: 1px solid #252525;
display: block;
width: 100%;
}
#saveCoins {
background-color: #000000;
color: #fff;
border: 1px solid #252525;
padding: 5px 10px;
margin: -1px 0px 0px 0px;
font-family: 'Inconsolata', monospace;
}
#coinlist {
margin: 15px 0px 0px 0px;
padding: 0;
max-height: 218px;
overflow-y: scroll;
}
#coinlist li {
position: relative;
margin: 0px 0px 5px 0px;
}
/* Custom checkboxes inspired by https://codepen.io/sderoij/pen/VvJJwE */
#coinlist label {
height: auto;
width: 100%;
z-index: 0;
display: inline-block;
position: absolute;
top: 0;
left: 0;
text-indent: 24px;
}
#coinlist label div {
height: 12px;
width: 12px;
border: solid 2px rgba(255, 255, 255, 0.6);
margin: 0;
border-radius: 50%;
transform: rotate(45deg);
transition: all 0ms ease-in-out, border 0ms ease 0ms;
position: absolute;
top: 0;
}
#coinlist input:hover + label div {
border-color: rgba(138, 255, 131, 0.9);
}
#coinlist input {
height: auto;
width: 18px;
margin: 0;
opacity: 0;
z-index: 1;
position: relative;
cursor: pointer;
}
#coinlist input:checked + label > div {
border-radius: 0;
border-top: 0;
border-left: 0;
border-color: rgba(138, 255, 131, 0.9);
height: 15px;
width: 12px;
margin-top: -4px;
margin-left: 0px;
transform: rotate(40deg);
transition: all 0ms ease-in-out;
}
#tips {
font-size: 12px;
}
#tips li {
}
.creds {
font-size: 8px;
color: #252525;
margin: 15px 0px 0px 0px;
}
.creds a {
text-decoration: none;
color: #252525;
}
/*Scrollbar*/
::-webkit-scrollbar-corner {
background-color: #000;
}
::-webkit-scrollbar {
background-color: rgba(0, 0, 0, 100);
}
::-webkit-scrollbar {
width: .5em;
height: .5em;
}
::-webkit-scrollbar-thumb:window-inactive,
::-webkit-scrollbar-thumb {
background: #252525;
-webkit-border-radius: 100px;
}
/*Select Boxes*/
.custom-select {
position: relative;
display: inline-block;
}
.custom-select select {
display: inline-block;
border: 1px solid #252525;
padding: 4px 3px 3px 5px;
margin: 0;
font: inherit;
outline: none;
line-height: 1.2;
background: #000000;
-webkit-appearance: none;
width: 145px;
color: #fff;
}
/* for Webkit's CSS-only solution */
@media screen and (-webkit-min-device-pixel-ratio:0) {
.custom-select select {
padding-right:30px;
}
}
/* Since we removed the default focus styles, we have to add our own */
.custom-select select:focus {
-webkit-box-shadow: 0 0 3px 1px #00afc1;
-moz-box-shadow: 0 0 3px 1px #00afc1;
box-shadow: 0 0 3px 1px #00afc1;
}
/* Select arrow styling */
.custom-select:after {
content: "▼";
position: absolute;
top: 0;
right: 0;
bottom: 0;
font-size: 60%;
line-height: 30px;
padding: 0 7px;
background: #252525;
color: white;
pointer-events: none;
}
</style>
<link href="css/app.css" rel="stylesheet">
</head>
<body>
@@ -351,16 +13,20 @@
<div class="controls">
<button id="min-btn"></button>
<button id="close-btn"></button>
</div>
<button id="settings-btn" onclick="toggleSettings()"><img src="images/icons8-Settings.png"></button>
</div><!-- .controls -->
<div class="tabs">
<button id="main-btn" href="#main"><img src="images/icons8-Home-64.png"></button>
<button id="portfolio-btn" href="#portfolio"><img src="images/icons8-Rebalance Portfolio-100.png"></button>
<button id="settings-btn" href="#settings"><img src="images/icons8-Settings.png"></button>
</div><!-- .tabs -->
</header>
<div id="main" class="panel active">
<ul id="prices">
<ul id="prices" class="coin-list">
</ul>
</div><!-- #main -->
<div id="settings" class="panel inactive">
<div id="settings" class="panel">
<h3>Choose Your Coins</h3>
<div id="coinsearch">
@@ -370,7 +36,6 @@
</ul>
</div><!-- #coinsearch -->
<!-- need to figure out how to save currency selection on close/open -->
<h3>Choose Your Base Currency</h3>
<label class="custom-select">
<select id="base" onchange="setBase()">
@@ -397,6 +62,15 @@
<option value="ZAR">ZAR</option>
</select>
</label>
<div class="checkbox-wrapper">
<input id="pin-to-top" class="checkbox-styled" type="checkbox" name="pin-to-top">
<label class="checkbox-styled-label">
Window always on top?
<div></div>
</label>
</div>
<h3>Tip Jar</h3>
<ul id="tips">
@@ -411,370 +85,36 @@
</div>
</div><!-- #settings -->
<div id="portfolio" class="panel">
<div class="chart">
</div>
<ul id="portfolio-list" class="coin-list">
</ul>
<button type="button" id="saveQuantities">Save</button>
<div id="portfolio-total-value">Total Value: <span class="value"></span></div>
<div class="chart-container" style="position: relative; height:40vh; width:94vw">
<canvas id="portfolioChart" width="360" height="360"></canvas>
</div>
<!-- show what % each coin is of portfolio -->
<!-- Enter avg. purchase price for each coin -->
<!-- See % gain/loss -->
</div><!-- #portfolio -->
</body>
<script>
// You can also require other files to run in this process
require('./renderer.js')
</script>
<script>
/******************
* APP FUNCTIONALITY
******************/
//user settings
const settings = require('electron-settings');
settings.set('developer', {
first: 'Nathan',
last: 'Parikh'
});
//default coins
if(settings.has('user.coins')) {
//do nothing because coins already set
}
else {
settings.set('user', {
coins: 'BTC,ETH,LTC'
});
}
//default base currency
if(settings.has('user.currency')) {
//do nothing because currency already set
}
else {
settings.set('user.currency', 'USD');
}
(function() {
function loadJSON(callback) {
var file = 'https://www.cryptocompare.com/api/data/coinlist/';
var xobj = new XMLHttpRequest();
xobj.overrideMimeType("application/json");
xobj.open('GET', file, true);
xobj.onreadystatechange = function () {
if (xobj.readyState == 4 && xobj.status == "200") {
// Required use of an anonymous callback as .open will NOT return a value but simply returns undefined in asynchronous mode
callback(xobj.responseText);
}
};
xobj.send(null);
} //loadJSON
// Generate the list of all coins
loadJSON(function(response) {
// Parse JSON string into object
var myDiv = document.getElementById("coinlist");
var actual_JSON = JSON.parse(response);
//alert(settings.get('user.coins'));
//console.log(actual_JSON.Data);
//loop through data, get coin info, generate checkbox for each coin
Object.keys(actual_JSON.Data).forEach(function(key) {
//console.log(actual_JSON.Data[key].Name);
//console.log(actual_JSON.Data[key].CoinName);
var li = document.createElement("li");
var checkBox = document.createElement("input");
checkBox.className = "coinCode";
var label = document.createElement("label");
label.className = "coinName";
var div = document.createElement("div");
checkBox.type = "checkbox";
checkBox.value = actual_JSON.Data[key].Name;
checkBox.name = "cl[]";
//check the coins the user has already set
var str = String(settings.get('user.coins'));
var split_str = str.split(",");
if (split_str.indexOf(actual_JSON.Data[key].Name) !== -1) {
checkBox.checked = true;
}
myDiv.appendChild(li);
li.appendChild(checkBox);
li.appendChild(label);
label.appendChild(document.createTextNode(actual_JSON.Data[key].CoinName));
label.appendChild(document.createTextNode(' ('+actual_JSON.Data[key].Name+')'));
label.appendChild(div);
}); //forEach
}); //loadJSON
base = settings.get('user.currency'); // get the user's base currency
var currSel = document.getElementById('base'); //select the currency select box
currSel.value = settings.get('user.currency'); //select the option that corresponds to the user's currency
setBase = function() {
//selected base currency
var sel = document.getElementById('base');
var x = sel.selectedIndex;
var y = sel.options;
base = y[x].text;
settings.set('user.currency', base); //save the user's selection
updateData(); //immediately reflect the changed currency
};
})();
//Functions for creating/appending elements
function createNode(element) {
return document.createElement(element);
}
function append(parent, el) {
return parent.appendChild(el);
}
// Returns an array with values of the selected (checked) checkboxes in "frm"
function getSelectedChbox(frm) {
var selchbox = []; // array that will store the value of selected checkboxes
// gets all the input tags in frm, and their number
var inpfields = frm.getElementsByTagName('input');
var nr_inpfields = inpfields.length;
// traverse the inpfields elements, and adds the value of selected (checked) checkbox in selchbox
for(var i=0; i<nr_inpfields; i++) {
if(inpfields[i].type == 'checkbox' && inpfields[i].checked == true) selchbox.push(inpfields[i].value);
}
return selchbox;
}
/* Test this function */
//document.getElementById('firstname').innerHTML = settings.get('user.coins');
// Click on #saveCoins, save the coin selection to the user
document.getElementById('saveCoins').onclick = function(){
var coinForm = document.getElementById('coinlist');
var selchb = getSelectedChbox(coinForm); // gets the array returned by getSelectedChbox()
//alert(selchb);
settings.set('user', {
coins: selchb
});
var selectedCoins = settings.get('user.coins');
//document.getElementById('firstname').innerHTML = selectedCoins;
// just reloading the entire app because I have yet to figure out how to add/remove a coin from the primary list without a page reload
location.reload();
}
const ul = document.getElementById('prices'); // Get the list where we will place coins
const url = 'https://min-api.cryptocompare.com/data/pricemultifull?fsyms='+settings.get('user.coins') +'&tsyms='+base +'&extraParams=your_app_name';
function initData() {
fetch(url)
.then(
function(response) {
if (response.status !== 200) {
console.log('Looks like there was a problem. Status Code: ' +
response.status);
return;
}
// Examine the response
response.json().then(function(data) {
//console.log(data);
let prices = data.DISPLAY;
var i = 0;
for (let key of Object.keys(prices)) {
let coin = prices[key];
//console.log(coin);
let li = createNode('li'),
span = createNode('span');
sym = createNode('span');
li.setAttribute("class", "price");
li.setAttribute("id", "coin-"+[key]);
//alert("coin-"+[key])
//console.log(settings.get('coin.'+[key]+'.order'));
li.setAttribute("sortorder", settings.get(li.id+'.order'));
//alert(settings.get(li.id+'.order'));
append(li, span);
append(ul, li);
i++;
}
//console.log(data.RAW.BTC.USD.PRICE)
sortChildren(
document.getElementById('prices'),
function(li) { return +li.getAttribute('sortorder') }
);
//sort your coins
sortable('#prices', {
handle: 'span'
})[0].addEventListener('sortstop', function(e) {
// Declare variables
var ul, li, i;
ul = document.getElementById("prices");
li = ul.getElementsByTagName('li');
// Loop through all list items
for (i = 0; i < li.length; i++) {
li[i].setAttribute("sortorder", i);
var elementID = li[i].id;
//alert(elementID);
settings.set(elementID, { // coin-BTC
order: li[i].getAttribute('sortorder')
});
//alert(settings.get(elementID + '.order'));
}
//alert(settings.get('coin.'+e+'.order'));
/*
This event is triggered when the user stopped sorting and the DOM position has changed.
e.detail.item contains the current dragged element.
e.detail.index contains the new index of the dragged element (considering only list items)
e.detail.oldindex contains the old index of the dragged element (considering only list items)
e.detail.elementIndex contains the new index of the dragged element (considering all items within sortable)
e.detail.oldElementIndex contains the old index of the dragged element (considering all items within sortable)
e.detail.startparent contains the element that the dragged item comes from
e.detail.endparent contains the element that the dragged item was added to (new parent)
e.detail.newEndList contains all elements in the list the dragged item was dragged to
e.detail.newStartList contains all elements in the list the dragged item was dragged from
e.detail.oldStartList contains all elements in the list the dragged item was dragged from BEFORE it was dragged from it
*/
}); //sortable
}); //response.json
} //function(response)
) //.then
.catch(function(err) {
console.log('Fetch Error :-S', err);
});
updateData();
}
function updateData() {
const url = 'https://min-api.cryptocompare.com/data/pricemultifull?fsyms='+settings.get('user.coins') +'&tsyms='+base +'&extraParams=your_app_name';
fetch(url)
.then(
function(response) {
if (response.status !== 200) {
console.log('Looks like there was a problem. Status Code: ' +
response.status);
return;
}
// Examine the text in the response
response.json().then(function(data) {
let pricesDISPLAY = data.DISPLAY; // display for everything except coin symbol
let pricesRAW = data.RAW; // raw to get BTC instead of bitcoin symbol
for (let key of Object.keys(pricesRAW)) {
let coinDISPLAY = pricesDISPLAY[key];
let coinDISPLAYchange = coinDISPLAY[base].CHANGEPCT24HOUR;
let coinRAW = pricesRAW[key];
//console.log(coinDISPLAY);
let li = document.getElementById("coin-"+[key]),
span = document.querySelector("#coin-"+[key]+" span");
span.setAttribute("class", "draggable");
let coinSymbol = coinRAW[base].FROMSYMBOL;
let coinRate = coinDISPLAY[base].PRICE.replace(/ /g,''); //.replace(/ /g,'') removes space after $
//replace currencies that have no symbols with easier to read formats
if(coinRate.includes("AUD")) { coinRate = coinRate.replace("AUD", "A$"); }
if(coinRate.includes("CAD")) { coinRate = coinRate.replace("CAD", "C$"); }
if(coinRate.includes("HKD")) { coinRate = coinRate.replace("HKD", "HK$"); }
if(coinRate.includes("MXN")) { coinRate = coinRate.replace("MXN", "$"); }
if(coinRate.includes("NOK")) { coinRate = coinRate.replace("NOK", "kr"); }
if(coinRate.includes("NZD")) { coinRate = coinRate.replace("NZD", "NZ$"); }
if(coinRate.includes("SEK")) { coinRate = coinRate.replace("SEK", "kr"); }
if(coinRate.includes("SGD")) { coinRate = coinRate.replace("SGD", "S$"); }
if(coinRate.includes("TRY")) { coinRate = coinRate.replace("TRY", "₺"); }
if(coinRate.includes("ZAR")) { coinRate = coinRate.replace("ZAR", "R"); }
//console.log(span);
span.innerHTML = '<span class="sym">' + coinSymbol + '</span> ' + coinRate + '<span class="change">' + coinDISPLAYchange + '%</span>';
// % Change
let change = document.querySelector("#coin-"+[key]+" .change");
if(coinDISPLAYchange > 0) {
change.className += " positive";
change.classList.remove("negative");
}
else if(coinDISPLAYchange < 0) {
change.className += " negative";
change.classList.remove("postive");
}
else {
change.classList.remove("postive");
change.classList.remove("negative");
}
}
});
}
)
setTimeout(function(){updateData()}, 5000); // run this once every 5 seconds
}
// Let's do this thing!
initData();
/*******
* APP UI
********/
//Window controls
const remote = require('electron').remote;
document.getElementById("close-btn").addEventListener("click", function (e) {
var window = remote.getCurrentWindow();
window.close();
});
document.getElementById("min-btn").addEventListener("click", function (e) {
var window = remote.getCurrentWindow();
window.minimize();
});
//settings tab/icon
function toggleSettings() {
var divs = document.getElementsByClassName('panel'), i;
for (i = 0; i < divs.length; ++i) {
if(divs[i].classList.contains('inactive')) {
divs[i].classList.remove('inactive');
divs[i].classList.add('active');
}
else {
divs[i].classList.remove('active');
divs[i].classList.add('inactive');
}
}//for
}//toggleSettings
//Coin search filter
function myFunction() {
// Declare variables
var input, filter, ul, li, a, i;
input = document.getElementById('myInput');
filter = input.value.toUpperCase();
ul = document.getElementById("coinlist");
li = ul.getElementsByTagName('li');
// Loop through all list items, and hide those who don't match the search query
for (i = 0; i < li.length; i++) {
label = li[i].getElementsByTagName("label")[0];
checkbox = li[i].getElementsByTagName("input")[0].value;
if (label.innerHTML.toUpperCase().indexOf(filter) > -1) {
li[i].style.display = "";
} else {
li[i].style.display = "none";
}
} //for
} //myFunction
//sort by attribute
function sortChildren(wrap, f, isNum) {
var l = wrap.children.length,
arr = new Array(l);
for(var i=0; i<l; ++i)
arr[i] = [f(wrap.children[i]), wrap.children[i]];
arr.sort(isNum
? function(a,b){ return a[0]-b[0]; }
: function(a,b){ return a[0]<b[0] ? -1 : a[0]>b[0] ? 1 : 0; }
);
var par = wrap.parentNode,
ref = wrap.nextSibling;
par.removeChild(wrap);
for(var i=0; i<l; ++i) wrap.appendChild(arr[i][1]);
par.insertBefore(wrap, ref);
} //sortChildren
</script>
<script src="js/app_common.js"></script>
<script src="js/html.sortable.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.0/Chart.min.js"></script>
</html>

533
js/app_common.js Normal file
View File

@@ -0,0 +1,533 @@
/******************
* APP FUNCTIONALITY
******************/
//access electron from here
const remote = require('electron').remote;
//user settings
const settings = require('electron-settings');
settings.set('developer', {
first: 'Nathan',
last: 'Parikh'
});
//default coins
if(settings.has('user.coins')) {
//do nothing because coins already set
}
else {
settings.set('user', {
coins: 'BTC,ETH,LTC'
});
}
//default base currency
if(settings.has('user.currency')) {
//do nothing because currency already set
}
else {
settings.set('user.currency', 'USD');
}
(function() {
function loadJSON(callback) {
var file = 'https://www.cryptocompare.com/api/data/coinlist/';
var xobj = new XMLHttpRequest();
xobj.overrideMimeType("application/json");
xobj.open('GET', file, true);
xobj.onreadystatechange = function () {
if (xobj.readyState == 4 && xobj.status == "200") {
// Required use of an anonymous callback as .open will NOT return a value but simply returns undefined in asynchronous mode
callback(xobj.responseText);
}
};
xobj.send(null);
} //loadJSON
// Generate the list of all coins
loadJSON(function(response) {
// Parse JSON string into object
var myDiv = document.getElementById("coinlist");
var actual_JSON = JSON.parse(response);
//alert(settings.get('user.coins'));
//console.log(actual_JSON.Data);
//loop through data, get coin info, generate checkbox for each coin
Object.keys(actual_JSON.Data).forEach(function(key) {
//console.log(actual_JSON.Data[key].Name);
//console.log(actual_JSON.Data[key].CoinName);
var li = document.createElement("li");
var checkBox = document.createElement("input");
checkBox.className = "coinCode";
var label = document.createElement("label");
label.className = "coinName";
var div = document.createElement("div");
checkBox.type = "checkbox";
checkBox.value = actual_JSON.Data[key].Name;
checkBox.name = "cl[]";
//check the coins the user has already set
var str = String(settings.get('user.coins'));
var split_str = str.split(",");
if (split_str.indexOf(actual_JSON.Data[key].Name) !== -1) {
checkBox.checked = true;
}
myDiv.appendChild(li);
li.appendChild(checkBox);
li.appendChild(label);
label.appendChild(document.createTextNode(actual_JSON.Data[key].CoinName));
label.appendChild(document.createTextNode(' ('+actual_JSON.Data[key].Name+')'));
label.appendChild(div);
}); //forEach
}); //loadJSON
base = settings.get('user.currency'); // get the user's base currency
var currSel = document.getElementById('base'); //select the currency select box
currSel.value = settings.get('user.currency'); //select the option that corresponds to the user's currency
setBase = function() {
//selected base currency
var sel = document.getElementById('base');
var x = sel.selectedIndex;
var y = sel.options;
base = y[x].text;
settings.set('user.currency', base); //save the user's selection
updateData(); //immediately reflect the changed currency
};
})();
//Functions for creating/appending elements
function createNode(element) {
return document.createElement(element);
}
function append(parent, el) {
return parent.appendChild(el);
}
// Returns an array with values of the selected (checked) checkboxes in "frm"
function getSelectedChbox(frm) {
var selchbox = []; // array that will store the value of selected checkboxes
// gets all the input tags in frm, and their number
var inpfields = frm.getElementsByTagName('input');
var nr_inpfields = inpfields.length;
// traverse the inpfields elements, and adds the value of selected (checked) checkbox in selchbox
for(var i=0; i<nr_inpfields; i++) {
if(inpfields[i].type == 'checkbox' && inpfields[i].checked == true) selchbox.push(inpfields[i].value);
}
return selchbox;
}
const ul = document.getElementById('prices'); // Get the list where we will place coins
const portfolio_ul = document.getElementById('portfolio-list');;
const url = 'https://min-api.cryptocompare.com/data/pricemultifull?fsyms='+settings.get('user.coins') +'&tsyms='+base +'&extraParams=crypto-price-widget';
var pinCheck = document.getElementById("pin-to-top");
function initData() {
fetch(url)
.then(
function(response) {
if (response.status !== 200) {
console.log('Looks like there was a problem. Status Code: ' +
response.status);
return;
}
// Examine the response
response.json().then(function(data) {
//console.log(data);
let prices = data.DISPLAY;
var i = 0;
for (let key of Object.keys(prices)) {
let coin = prices[key];
//console.log(coin);
let li = createNode('li'),
span = createNode('span');
sym = createNode('span');
li.setAttribute("class", "price");
li.setAttribute("id", "coin-"+[key]);
//alert("coin-"+[key])
//console.log(settings.get('coin.'+[key]+'.order'));
li.setAttribute("sortorder", settings.get(li.id+'.order'));
//alert(settings.get(li.id+'.order'));
append(li, span);
append(ul, li);
i++;
} //for
//console.log(data.RAW.BTC.USD.PRICE)
sortChildren(
document.getElementById('prices'),
function(li) { return +li.getAttribute('sortorder') }
);
sortChildren(
document.getElementById('portfolio-list'),
function(li) { return +li.getAttribute('sortorder') }
);
//sort your coins
sortable('#prices', {
handle: 'span'
})[0].addEventListener('sortstop', function(e) {
// Declare variables
var ul, ulPortfolio, li, liPortfolio, i;
ul = document.getElementById("prices");
ulPortfolio = document.getElementById("portfolio-list");
li = ul.getElementsByTagName('li');
liPortfolio = ulPortfolio.getElementsByTagName('li');
// Loop through all list items
for (i = 0; i < li.length; i++) {
li[i].setAttribute("sortorder", i);
var elementID = li[i].id;
//alert(elementID);
settings.set(elementID, { // coin-BTC
order: li[i].getAttribute('sortorder')
});
//alert(settings.get(elementID + '.order'));
} //for
//alert(settings.get('coin.'+e+'.order'));
}); //sortable
}); //response.json
} //function(response)
) //.then
.catch(function(err) {
console.log('Fetch Error :-S', err);
});
updateData();
}
function updateData() {
//console.log(settings.get('user.coins'));
const url = 'https://min-api.cryptocompare.com/data/pricemultifull?fsyms='+settings.get('user.coins') +'&tsyms='+base +'&extraParams=crypto-price-widget';
fetch(url)
.then(
function(response) {
if (response.status !== 200) {
console.log('Looks like there was a problem. Status Code: ' +
response.status);
return;
}
// Examine the text in the response
response.json().then(function(data) {
let pricesDISPLAY = data.DISPLAY; // display for everything except coin symbol
let pricesRAW = data.RAW; // raw to get BTC instead of bitcoin symbol
let portfolioSum = 0;
// Chart labels
var chartLabels = [];
// Chart data
var chartData = [];
// Chart colors - need to match these colors to the coin in a future release
var chartColors = [
'#FF9F1C',
'#2EC4B6',
'#E71D36',
'#011627',
'#FDFFFC',
'#D31EE8',
'#0288D1',
'#5FC42D',
'#E64A19',
'#0097A7',
'#FBC02D',
'#00796B',
'#388E3C',
'#689F38',
'#303F9F',
'#C2185B',
'#FFA000',
'#D32F2F',
'#C2185B',
'#fff'
];
//randomize
function shuffle(array) {
var currentIndex = array.length, temporaryValue, randomIndex;
// While there remain elements to shuffle...
while (0 !== currentIndex) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
// And swap it with the current element.
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
}
for (let key of Object.keys(pricesRAW)) {
let coinDISPLAY = pricesDISPLAY[key];
let coinDISPLAYchange = coinDISPLAY[base].CHANGEPCT24HOUR;
let coinRAW = pricesRAW[key];
//console.log(coinDISPLAY);
let li = document.getElementById("coin-"+[key]),
span = document.querySelector("#coin-"+[key]+" span");
span.setAttribute("class", "draggable");
let coinSymbol = coinRAW[base].FROMSYMBOL;
let coinRate = coinDISPLAY[base].PRICE.replace(/ /g,''); //.replace(/ /g,'') removes space after $
//replace currencies that have no symbols with easier to read formats
if(coinRate.includes("AUD")) { coinRate = coinRate.replace("AUD", "A$"); }
if(coinRate.includes("CAD")) { coinRate = coinRate.replace("CAD", "C$"); }
if(coinRate.includes("HKD")) { coinRate = coinRate.replace("HKD", "HK$"); }
if(coinRate.includes("MXN")) { coinRate = coinRate.replace("MXN", "$"); }
if(coinRate.includes("NOK")) { coinRate = coinRate.replace("NOK", "kr"); }
if(coinRate.includes("NZD")) { coinRate = coinRate.replace("NZD", "NZ$"); }
if(coinRate.includes("SEK")) { coinRate = coinRate.replace("SEK", "kr"); }
if(coinRate.includes("SGD")) { coinRate = coinRate.replace("SGD", "S$"); }
if(coinRate.includes("TRY")) { coinRate = coinRate.replace("TRY", "₺"); }
if(coinRate.includes("ZAR")) { coinRate = coinRate.replace("ZAR", "R"); }
//console.log(span);
span.innerHTML = '<span class="sym">' + coinSymbol + '</span> ' + coinRate + '<span class="change">' + coinDISPLAYchange + '%</span>';
// % Change
let change = document.querySelector("#coin-"+[key]+" .change");
if(coinDISPLAYchange > 0) {
change.className += " positive";
change.classList.remove("negative");
}
else if(coinDISPLAYchange < 0) {
change.className += " negative";
change.classList.remove("postive");
}
else {
change.classList.remove("postive");
change.classList.remove("negative");
}
// Portfolio
let quantityValue = document.querySelector("#coin-"+[key]+" .quantity-value");
let quantityNumber = settings.get('quantity.'+[key]);
let regp = /[^0-9.-]+/g;
let quantityTotal = parseFloat(coinRate.replace(regp, '')) * parseFloat(quantityNumber.replace(regp, ''));
// sum of all total coin values
portfolioSum += quantityTotal;
// put sum into the markup
let portfolioTotalValue = document.querySelector("#portfolio-total-value .value");
// total value for each coin
if(coinRate.includes("Ƀ")) {
//because BTC has 8 decimal places
quantityValue.innerHTML = quantityTotal.toFixed(8);
portfolioTotalValue.innerHTML = portfolioSum.toFixed(8);
}
else {
//standard currency format
quantityValue.innerHTML = quantityTotal.toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g, '$1,');
portfolioTotalValue.innerHTML = portfolioSum.toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g, '$1,');
}
// Chart labels
chartLabels.push(key);
// Chart data
chartData.push(quantityTotal);
} //for
sortChildren(
document.getElementById('portfolio-list'),
function(li) { return +li.getAttribute('sortorder') }
);
var newChartLabels = chartLabels;
// Chart
var ctx = document.getElementById("portfolioChart");
var myChart = new Chart(ctx, {
type: 'doughnut',
data: {
datasets: [{
data: chartData,
backgroundColor: chartColors,
borderColor: '#000',
borderWidth: 0
}],
labels: newChartLabels
},
options: {
animation : false,
responsive: false,
maintainAspectRatio: true,
legend : {
position: 'bottom'
}
}
}); //myChart
//Pin to Top - settings check - immediately set checkbox and window to saved state
if(settings.get('user.pinToTop') == 'yes') {
pinCheck.checked = true;
remote.getCurrentWindow().setAlwaysOnTop(true);
} else {
pinCheck.checked = false;
remote.getCurrentWindow().setAlwaysOnTop(false);
}
}); //then
}
)
setTimeout(function(){updateData()}, 5000); // run this once every 5 seconds
}
// Let's do this thing!
initData();
/* Test this function */
//document.getElementById('firstname').innerHTML = settings.get('user.coins');
// Click on #saveCoins, save the coin selection to the user
document.getElementById('saveCoins').onclick = function(){
var coinForm = document.getElementById('coinlist');
var selchb = getSelectedChbox(coinForm); // gets the array returned by getSelectedChbox()
//alert(selchb);
settings.set('user.coins', selchb);
// just reloading the entire app because I have yet to figure out how to add/remove a coin from the primary list without a page reload
location.reload(false);
//alert(settings.get('user.currency'));
}
/***********
* PORTFOLIO
***********/
var portfolio_list_container = document.querySelector('#portfolio-list');
var portfolio_list = settings.get('user.coins');
//generate html from list of coins
for (let key of Object.keys(portfolio_list)) {
let coin = portfolio_list[key];
//console.log(coin);
let li = createNode('li'),
span = createNode('span');
sym = createNode('span');
li.setAttribute("id", "coin-"+[coin]);
li.setAttribute("sortorder", settings.get(li.id+'.order'));
append(li, span);
append(portfolio_ul, li);
if (settings.has('quantity.'+[coin])) {
inputValue = settings.get('quantity.'+[coin]);
}
else {
inputValue = '0';
inputValue = settings.set('quantity.'+[coin], '0');
}
span.innerHTML = '<span class="sym">' + coin + '</span> <span class="block quantity"><label for="quantity.' + coin +'">Quantity</label> <input type="number" name="quantity.' + coin +'" min="0" value="'+inputValue+'" step=".01"></span> <span class="block value"><label>Current Value</label><span class="quantity-value"></span></span>';
i++;
} //for
// save quantities
document.getElementById('saveQuantities').onclick = function(){
var items = portfolio_ul.getElementsByTagName("input");
for (var i = 0; i < items.length; ++i) {
// do something with items[i], which is a <li> element
inputName = items[i].getAttribute("name");
inputValue = items[i].value;
//console.log(inputValue);
settings.set(inputName, inputValue);
}
// just reloading the entire app because I have yet to figure out how to add/remove a coin from the primary list without a page reload
//location.reload(false);
}
/***********
* PIN TO TOP
*************/
pinCheck.onclick = function(event) {
var window = remote.getCurrentWindow();
var checkbox = event.target;
if(checkbox.checked) {
//Checkbox has been checked
window.setAlwaysOnTop(true); //immediately make the change to the window
settings.set('user.pinToTop', 'yes');
} else {
//Checkbox has been unchecked
window.setAlwaysOnTop(false);
settings.set('user.pinToTop', 'no');
}
};
/*******
* APP UI
********/
//Window controls
document.getElementById("close-btn").addEventListener("click", function (e) {
var window = remote.getCurrentWindow();
window.close();
});
document.getElementById("min-btn").addEventListener("click", function (e) {
var window = remote.getCurrentWindow();
window.minimize();
});
//Panel tabs
var tabLinks = document.querySelectorAll('.tabs button');
for (var i = 0; i < tabLinks.length; i++) {
tabLinks[i].onclick = function() {
var target = this.getAttribute('href').replace('#', '');
var sections = document.querySelectorAll('.panel');
for(var j=0; j < sections.length; j++) {
sections[j].style.display = 'none';
}
document.getElementById(target).style.display = 'block';
for(var k=0; k < tabLinks.length; k++) {
tabLinks[k].removeAttribute('class');
}
this.setAttribute('class', 'active');
return false;
}
};
//Coin search filter
function myFunction() {
// Declare variables
var input, filter, ul, li, a, i;
input = document.getElementById('myInput');
filter = input.value.toUpperCase();
ul = document.getElementById("coinlist");
li = ul.getElementsByTagName('li');
// Loop through all list items, and hide those who don't match the search query
for (i = 0; i < li.length; i++) {
label = li[i].getElementsByTagName("label")[0];
checkbox = li[i].getElementsByTagName("input")[0].value;
if (label.innerHTML.toUpperCase().indexOf(filter) > -1) {
li[i].style.display = "";
} else {
li[i].style.display = "none";
}
} //for
} //myFunction
//sort by attribute
function sortChildren(wrap, f, isNum) {
var l = wrap.children.length,
arr = new Array(l);
for(var i=0; i<l; ++i)
arr[i] = [f(wrap.children[i]), wrap.children[i]];
arr.sort(isNum
? function(a,b){ return a[0]-b[0]; }
: function(a,b){ return a[0]<b[0] ? -1 : a[0]>b[0] ? 1 : 0; }
);
var par = wrap.parentNode,
ref = wrap.nextSibling;
par.removeChild(wrap);
for(var i=0; i<l; ++i) wrap.appendChild(arr[i][1]);
par.insertBefore(wrap, ref);
} //sortChildren

2
js/offline.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -25,13 +25,14 @@ function createWindow () {
// Create the browser window.
mainWindow = new electron.BrowserWindow({
title: app.getName(),
alwaysOnTop: false,
//show: false,
x: mainWindowState.x,
y: mainWindowState.y,
width: mainWindowState.width,
height: mainWindowState.height,
maxWidth: 360,
minWidth: 240,
minWidth: 280,
minHeight: 100,
maximizable: false,
fullscreenable: false,

179
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "crypto-price-widget",
"version": "1.0.0",
"version": "1.0.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -41,6 +41,22 @@
}
}
},
"aggregate-error": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-1.0.0.tgz",
"integrity": "sha1-iINE2tAiCnLjr1CQYRf0h3GSX6w=",
"requires": {
"clean-stack": "1.3.0",
"indent-string": "3.2.0"
},
"dependencies": {
"indent-string": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz",
"integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok="
}
}
},
"ajv": {
"version": "4.11.8",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz",
@@ -1289,6 +1305,11 @@
"resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz",
"integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A=="
},
"clean-stack": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-1.3.0.tgz",
"integrity": "sha1-noIVAa6XmYbEax1m0tQy2y/UrjE="
},
"clean-yaml-object": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/clean-yaml-object/-/clean-yaml-object-0.1.0.tgz",
@@ -1683,6 +1704,23 @@
"resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.0.tgz",
"integrity": "sha1-HMPIOkkNZ/ldkeOfatHy4Ia2MEg="
},
"dns-packet": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.2.2.tgz",
"integrity": "sha512-kN+DjfGF7dJGUL7nWRktL9Z18t1rWP3aQlyZdY8XlpvU3Nc6GeFTQApftcjtWKxAZfiggZSGrCEoszNgvnpwDg==",
"requires": {
"ip": "1.1.5",
"safe-buffer": "5.1.1"
}
},
"dns-socket": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/dns-socket/-/dns-socket-1.6.2.tgz",
"integrity": "sha512-Ztbaf5fToBfm/4+sVEJi7mT2mJOLYYpI+TpgOhxwp5l28UwunTpHMccVhTe9L0F6pQ2cUF0ja9ukuTCtzYq2Ig==",
"requires": {
"dns-packet": "1.2.2"
}
},
"doctrine": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
@@ -1744,6 +1782,11 @@
}
}
},
"duplexer3": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
"integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI="
},
"eastasianwidth": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.1.1.tgz",
@@ -2629,6 +2672,11 @@
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
"integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4="
},
"get-stream": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
},
"getpass": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
@@ -2936,6 +2984,16 @@
"loose-envify": "1.3.1"
}
},
"ip": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
"integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo="
},
"ip-regex": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
"integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk="
},
"irregular-plurals": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-1.3.0.tgz",
@@ -3045,6 +3103,14 @@
"is-extglob": "1.0.0"
}
},
"is-ip": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-ip/-/is-ip-2.0.0.tgz",
"integrity": "sha1-aO6gfooKCpTC0IDdZ0xzGrKkYas=",
"requires": {
"ip-regex": "2.1.0"
}
},
"is-js-type": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-js-type/-/is-js-type-2.0.0.tgz",
@@ -3107,6 +3173,47 @@
"symbol-observable": "0.2.4"
}
},
"is-online": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-online/-/is-online-7.0.0.tgz",
"integrity": "sha1-fiQIwK4efje6jVC9sjcmDTK/2W4=",
"requires": {
"got": "6.7.1",
"p-any": "1.1.0",
"p-timeout": "1.2.0",
"public-ip": "2.3.5"
},
"dependencies": {
"got": {
"version": "6.7.1",
"resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz",
"integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=",
"requires": {
"create-error-class": "3.0.2",
"duplexer3": "0.1.4",
"get-stream": "3.0.0",
"is-redirect": "1.0.0",
"is-retry-allowed": "1.1.0",
"is-stream": "1.1.0",
"lowercase-keys": "1.0.0",
"safe-buffer": "5.1.1",
"timed-out": "4.0.1",
"unzip-response": "2.0.1",
"url-parse-lax": "1.0.0"
}
},
"timed-out": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz",
"integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8="
},
"unzip-response": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz",
"integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c="
}
}
},
"is-path-cwd": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz",
@@ -3909,6 +4016,19 @@
"os-tmpdir": "1.0.2"
}
},
"p-any": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/p-any/-/p-any-1.1.0.tgz",
"integrity": "sha512-Ef0tVa4CZ5pTAmKn+Cg3w8ABBXh+hHO1aV8281dKOoUHfX+3tjG2EaFcC+aZyagg9b4EYGsHEjz21DnEE8Og2g==",
"requires": {
"p-some": "2.0.0"
}
},
"p-finally": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
"integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
},
"p-limit": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.1.0.tgz",
@@ -3924,6 +4044,22 @@
"p-limit": "1.1.0"
}
},
"p-some": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/p-some/-/p-some-2.0.0.tgz",
"integrity": "sha512-CsRc5gwQNJgSh+pNaGUtgBWBSh9btl8jYLbIdeqLgOLAATZmDDX7xTS5V0mqJk5Dw0gz8FF6s8sAF4D0MvxLhw==",
"requires": {
"aggregate-error": "1.0.0"
}
},
"p-timeout": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.0.tgz",
"integrity": "sha1-mCD5lDTFgXhotPNICe5SkWYNW2w=",
"requires": {
"p-finally": "1.0.0"
}
},
"package-hash": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/package-hash/-/package-hash-1.2.0.tgz",
@@ -4278,6 +4414,47 @@
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
},
"public-ip": {
"version": "2.3.5",
"resolved": "https://registry.npmjs.org/public-ip/-/public-ip-2.3.5.tgz",
"integrity": "sha1-fXhHYliBV2unofpBCwl43yOb33U=",
"requires": {
"dns-socket": "1.6.2",
"got": "6.7.1",
"is-ip": "2.0.0",
"pify": "2.3.0"
},
"dependencies": {
"got": {
"version": "6.7.1",
"resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz",
"integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=",
"requires": {
"create-error-class": "3.0.2",
"duplexer3": "0.1.4",
"get-stream": "3.0.0",
"is-redirect": "1.0.0",
"is-retry-allowed": "1.1.0",
"is-stream": "1.1.0",
"lowercase-keys": "1.0.0",
"safe-buffer": "5.1.1",
"timed-out": "4.0.1",
"unzip-response": "2.0.1",
"url-parse-lax": "1.0.0"
}
},
"timed-out": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz",
"integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8="
},
"unzip-response": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz",
"integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c="
}
}
},
"punycode": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",

View File

@@ -1,7 +1,7 @@
{
"name": "crypto-price-widget",
"productName": "Crypto Price Widget",
"version": "1.0.0",
"version": "1.1.0",
"description": "A cross-platform app for tracking Crypto prices",
"main": "main.js",
"scripts": {

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,4 +1,3 @@
// This file is required by the index.html file and will
// be executed in the renderer process for that window.
// All of the Node.js APIs are available in this process.