Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa54efcec6 | ||
|
|
53bd6af536 | ||
|
|
073a483bf2 | ||
|
|
af39424d7d | ||
|
|
4b795fb375 | ||
|
|
5112fc8e8d | ||
|
|
1f381d4c25 | ||
|
|
ac6d6861bf | ||
|
|
649a26299c | ||
|
|
c0eb800259 | ||
|
|
e4bc240bf2 | ||
|
|
03fe34fb08 | ||
|
|
d7f9c9d2d4 | ||
|
|
b34ab0f32c | ||
|
|
276a52f87c | ||
|
|
7f0829f8ad | ||
|
|
386deb7a10 | ||
|
|
0f1b2b3d86 | ||
|
|
6da0f8f5bd | ||
|
|
1207f6a09d | ||
|
|
0f92fb17f5 | ||
|
|
3a8dbd63f0 | ||
|
|
8684427f76 | ||
|
|
afb1836e1d |
5
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
node_modules
|
node_modules
|
||||||
|
release-builds
|
||||||
out/
|
out/
|
||||||
|
*.code-workspace
|
||||||
16
README.md
@ -23,12 +23,14 @@ Crypto Price Widget is an open source project created by [Nathan Parikh](https:/
|
|||||||
|
|
||||||
## Donate
|
## Donate
|
||||||
|
|
||||||
Crypto Price Widget is an open source side project. To support development and keep the project running, you can donate using Bitcoin, Ethereum, Litecoin, or Doge:
|
Crypto Price Widget is an open source side project. To support development and keep the project running, you can donate using one of the below options:
|
||||||
|
|
||||||
- Bitcoin: `17iENfaJkEpxGXW7mgdFh9hGMZV65R2zVL`
|
- PayPal: https://www.paypal.me/nathanp
|
||||||
- Ethereum: `0x68b99868700b33A248de4A62a038a9e3b03DCA21`
|
- CashApp: https://cash.app/$ndzynes
|
||||||
- Litecoin: `La8eCVjzLq8zrJV3LgyU6WtnyQnjs76LFY`
|
- Bitcoin: `bc1qkzrkkhmufjuyslh92mfne5yfe6trhf2u258wl4`
|
||||||
- Doge: `DFHBdwUbcvGezfgHHbWmH8eLWjAjUhFSZ2`
|
- Ethereum: `0x0606405c03F381EF187C413438E7efE705ec64AA`
|
||||||
|
- Litecoin: `LNBBr1iutwMCTfGcPps2Qhg66vSkAbZWhE`
|
||||||
|
- Doge: `DG1twgxAJa4Tj42e4aoHVnh654Ro8ftDGi`
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|
||||||
@ -36,10 +38,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.
|
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?**
|
**When can I have a Linux version?**
|
||||||
|
|
||||||
Linux coming soon!
|
Linux coming soon!
|
||||||
|
|||||||
1
coinlist.json
Normal file
1579
css/animate.css
vendored
Normal file
505
css/app.css
Normal file
@ -0,0 +1,505 @@
|
|||||||
|
/*Fonts*/
|
||||||
|
@font-face {
|
||||||
|
font-family: 'heeboregular';
|
||||||
|
src: url('../fonts/heebo-regular-webfont.woff2') format('woff2'),
|
||||||
|
url('../fonts/heebo-regular-webfont.woff') format('woff');
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'heebothin';
|
||||||
|
src: url('../fonts/heebo-thin-webfont.woff2') format('woff2'),
|
||||||
|
url('../fonts/heebo-thin-webfont.woff') format('woff');
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'inconsolataregular';
|
||||||
|
src: url('../fonts/inconsolata-regular-webfont.woff2') format('woff2'),
|
||||||
|
url('../fonts/inconsolata-regular-webfont.woff') format('woff');
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
body,
|
||||||
|
button,
|
||||||
|
#myInput,
|
||||||
|
#saveCoins,
|
||||||
|
#saveQuantities {
|
||||||
|
font-family: 'inconsolataregular', monospace;
|
||||||
|
}
|
||||||
|
.coin-list li .sym {
|
||||||
|
font-family: 'heeboregular', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: rgba(0, 0, 0, 0.95);
|
||||||
|
color: #fff;
|
||||||
|
margin-top: 35px;
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.titlebar {
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-webkit-app-region: drag;
|
||||||
|
opacity: 0;
|
||||||
|
position: fixed;
|
||||||
|
width: 94%;
|
||||||
|
z-index: 100;
|
||||||
|
background: rgba(0, 0, 0, 0.95);
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
padding: 8px 3% 3px;
|
||||||
|
}
|
||||||
|
.titlebar:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.titlebar .controls {
|
||||||
|
float: right;
|
||||||
|
line-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
-webkit-app-region: no-drag;
|
||||||
|
background-color: #000000;
|
||||||
|
color: #fff;
|
||||||
|
border: 1px solid #252525;
|
||||||
|
padding: 5px 10px;
|
||||||
|
margin: -1px 0px 0px 0px;
|
||||||
|
}
|
||||||
|
header button {
|
||||||
|
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;
|
||||||
|
-webkit-transition: all 150ms ease;
|
||||||
|
-moz-transition: all 150ms ease;
|
||||||
|
-ms-transition: all 150ms ease;
|
||||||
|
-o-transition: all 150ms ease;
|
||||||
|
transition: all 150ms ease;
|
||||||
|
}
|
||||||
|
#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;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
.tabs button img {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
opacity: 0.5;
|
||||||
|
-webkit-transition: all 150ms ease;
|
||||||
|
-moz-transition: all 150ms ease;
|
||||||
|
-ms-transition: all 150ms ease;
|
||||||
|
-o-transition: all 150ms ease;
|
||||||
|
transition: all 150ms ease;
|
||||||
|
}
|
||||||
|
.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-size: 12px;
|
||||||
|
}
|
||||||
|
.coin-list li .sym:hover {
|
||||||
|
cursor: -webkit-grab;
|
||||||
|
border-top-color: #4c4c4c;
|
||||||
|
border-right-color: #4c4c4c;
|
||||||
|
border-bottom-color: #4c4c4c;
|
||||||
|
-webkit-transition: all 250ms ease;
|
||||||
|
-moz-transition: all 250ms ease;
|
||||||
|
-ms-transition: all 250ms ease;
|
||||||
|
-o-transition: all 250ms ease;
|
||||||
|
transition: all 250ms ease;
|
||||||
|
}
|
||||||
|
/*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: 14px;
|
||||||
|
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 {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
#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: 18px;
|
||||||
|
z-index: 0;
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
text-indent: 24px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
#coinlist label {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
#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;
|
||||||
|
-webkit-transition: all 250ms ease;
|
||||||
|
-moz-transition: all 250ms ease;
|
||||||
|
-ms-transition: all 250ms ease;
|
||||||
|
-o-transition: all 250ms ease;
|
||||||
|
transition: all 250ms ease;
|
||||||
|
}
|
||||||
|
#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-color: #252525;
|
||||||
|
-webkit-border-radius: 100px;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background-color: #4c4c4c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*Offline*/
|
||||||
|
.error {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.error h2,
|
||||||
|
.error h4 {
|
||||||
|
font-family: Arial,Helvetica,sans-serif;
|
||||||
|
}
|
||||||
|
.error button.refresh {
|
||||||
|
cursor: pointer;
|
||||||
|
background: #41BB2E;
|
||||||
|
border: none;
|
||||||
|
color: #000;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
108
css/offline-language-english.css
Normal 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.";
|
||||||
|
}
|
||||||
75
css/offline-theme-dark-indicator.css
Normal 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
fonts/heebo-regular-webfont.woff
Normal file
BIN
fonts/heebo-regular-webfont.woff2
Normal file
BIN
fonts/heebo-thin-webfont.woff
Normal file
BIN
fonts/heebo-thin-webfont.woff2
Normal file
BIN
fonts/inconsolata-regular-webfont.woff
Normal file
BIN
fonts/inconsolata-regular-webfont.woff2
Normal file
BIN
images/appbar.alert.png
Normal file
|
After Width: | Height: | Size: 982 B |
BIN
images/appbar.pie.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
images/appbar.settings.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
images/appbar.speakerphone.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
images/appbar.stock.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
images/icons8-Home-64.png
Normal file
|
After Width: | Height: | Size: 600 B |
BIN
images/icons8-Rebalance Portfolio-100.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
images/offline_doge.jpg
Normal file
|
After Width: | Height: | Size: 31 KiB |
754
index.html
@ -2,348 +2,8 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Latest Crypto Prices</title>
|
<title>Crypto Price Widget</title>
|
||||||
<link href="https://fonts.googleapis.com/css?family=Heebo:100,400" rel="stylesheet">
|
<link href="css/app.css" 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>
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@ -351,16 +11,20 @@
|
|||||||
<div class="controls">
|
<div class="controls">
|
||||||
<button id="min-btn"></button>
|
<button id="min-btn"></button>
|
||||||
<button id="close-btn"></button>
|
<button id="close-btn"></button>
|
||||||
</div>
|
</div><!-- .controls -->
|
||||||
<button id="settings-btn" onclick="toggleSettings()"><img src="images/icons8-Settings.png"></button>
|
<div class="tabs">
|
||||||
|
<button id="main-btn" href="#main"><img src="images/appbar.stock.png"></button>
|
||||||
|
<button id="portfolio-btn" href="#portfolio"><img src="images/appbar.pie.png"></button>
|
||||||
|
<button id="settings-btn" href="#settings"><img src="images/appbar.settings.png"></button>
|
||||||
|
</div><!-- .tabs -->
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div id="main" class="panel active">
|
<div id="main" class="panel active">
|
||||||
<ul id="prices">
|
<ul id="prices" class="coin-list">
|
||||||
</ul>
|
</ul>
|
||||||
</div><!-- #main -->
|
</div><!-- #main -->
|
||||||
|
|
||||||
<div id="settings" class="panel inactive">
|
<div id="settings" class="panel">
|
||||||
<h3>Choose Your Coins</h3>
|
<h3>Choose Your Coins</h3>
|
||||||
|
|
||||||
<div id="coinsearch">
|
<div id="coinsearch">
|
||||||
@ -370,7 +34,6 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div><!-- #coinsearch -->
|
</div><!-- #coinsearch -->
|
||||||
|
|
||||||
<!-- need to figure out how to save currency selection on close/open -->
|
|
||||||
<h3>Choose Your Base Currency</h3>
|
<h3>Choose Your Base Currency</h3>
|
||||||
<label class="custom-select">
|
<label class="custom-select">
|
||||||
<select id="base" onchange="setBase()">
|
<select id="base" onchange="setBase()">
|
||||||
@ -397,6 +60,14 @@
|
|||||||
<option value="ZAR">ZAR</option>
|
<option value="ZAR">ZAR</option>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</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>
|
<h3>Tip Jar</h3>
|
||||||
<ul id="tips">
|
<ul id="tips">
|
||||||
@ -406,375 +77,38 @@
|
|||||||
<li>DOGE: DFHBdwUbcvGezfgHHbWmH8eLWjAjUhFSZ2</li>
|
<li>DOGE: DFHBdwUbcvGezfgHHbWmH8eLWjAjUhFSZ2</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="creds">
|
<h4>Build Info</h4>
|
||||||
<div>Icons made by <a href="https://www.flaticon.com/authors/madebyoliver" title="Madebyoliver">Madebyoliver</a> from <a href="https://www.flaticon.com/" title="Flaticon">www.flaticon.com</a> is licensed by <a href="http://creativecommons.org/licenses/by/3.0/" title="Creative Commons BY 3.0" target="_blank">CC 3.0 BY</a></div>
|
node <script>document.write(process.versions.node)</script>,
|
||||||
</div>
|
Chrome <script>document.write(process.versions.chrome)</script>,
|
||||||
|
and Electron <script>document.write(process.versions.electron)</script>.
|
||||||
|
|
||||||
</div><!-- #settings -->
|
</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>
|
||||||
|
|
||||||
|
<!-- show what % each coin is of portfolio -->
|
||||||
|
|
||||||
|
<!-- Enter avg. purchase price for each coin -->
|
||||||
|
<!-- See % gain/loss -->
|
||||||
|
</div><!-- #portfolio -->
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// You can also require other files to run in this process
|
// You can also require other files to run in this process
|
||||||
require('./renderer.js')
|
//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>
|
||||||
|
<script src="js/app_common.js"></script>
|
||||||
<script src="js/html.sortable.min.js"></script>
|
<script src="js/html.sortable.min.js"></script>
|
||||||
</html>
|
</html>
|
||||||
10
js/Chart.min.js
vendored
Normal file
549
js/app_common.js
Normal file
@ -0,0 +1,549 @@
|
|||||||
|
/******************
|
||||||
|
* APP FUNCTIONALITY
|
||||||
|
******************/
|
||||||
|
//access electron from here
|
||||||
|
const remote = require("electron").remote;
|
||||||
|
//user settings
|
||||||
|
const settings = require("electron-settings");
|
||||||
|
|
||||||
|
//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");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Base Currency */
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ul = document.getElementById("prices"); // Get the list where we will place coins
|
||||||
|
const portfolio_ul = document.getElementById("portfolio-list");
|
||||||
|
var 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 clearData() {
|
||||||
|
ul.innerHTML = "";
|
||||||
|
clearTimeout(appRefresh);
|
||||||
|
}
|
||||||
|
|
||||||
|
function initData() {
|
||||||
|
//need to redeclare the url variable here to grab the latest user coins, etc.
|
||||||
|
var url =
|
||||||
|
"https://min-api.cryptocompare.com/data/pricemultifull?fsyms=" +
|
||||||
|
settings.get("user.coins") +
|
||||||
|
"&tsyms=" +
|
||||||
|
base +
|
||||||
|
"&extraParams=crypto-price-widget";
|
||||||
|
fetch(url)
|
||||||
|
.then(
|
||||||
|
function (response) {
|
||||||
|
// Examine the response
|
||||||
|
response.json().then(function (data) {
|
||||||
|
//console.log(url);
|
||||||
|
let pricesDISPLAY = data.DISPLAY; // display for everything except coin symbol
|
||||||
|
let pricesRAW = data.RAW; // raw to get "BTC" instead of bitcoin symbol
|
||||||
|
|
||||||
|
var i = 0;
|
||||||
|
for (let key of Object.keys(pricesDISPLAY)) {
|
||||||
|
let coin = pricesDISPLAY[key];
|
||||||
|
//console.log(coin);
|
||||||
|
let li = createNode("li"),
|
||||||
|
span = createNode("span");
|
||||||
|
sym = createNode("span");
|
||||||
|
li.setAttribute("class", "price");
|
||||||
|
li.setAttribute("id", "coin-" + [key]);
|
||||||
|
|
||||||
|
span.setAttribute("class", "draggable");
|
||||||
|
|
||||||
|
//when adding a new coin, default sortorder to 999
|
||||||
|
if (settings.get(li.id + ".order") == null) {
|
||||||
|
settings.set(li.id + ".order", 999);
|
||||||
|
li.setAttribute("sortorder", 999);
|
||||||
|
} else {
|
||||||
|
li.setAttribute("sortorder", settings.get(li.id + ".order"));
|
||||||
|
}
|
||||||
|
|
||||||
|
append(li, span);
|
||||||
|
append(ul, li);
|
||||||
|
i++;
|
||||||
|
} //for
|
||||||
|
|
||||||
|
//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
|
||||||
|
|
||||||
|
//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);
|
||||||
|
}
|
||||||
|
|
||||||
|
sortChildren(document.getElementById("prices"), function (li) {
|
||||||
|
return +li.getAttribute("sortorder");
|
||||||
|
});
|
||||||
|
sortChildren(document.getElementById("portfolio-list"), function (
|
||||||
|
li
|
||||||
|
) {
|
||||||
|
return +li.getAttribute("sortorder");
|
||||||
|
});
|
||||||
|
}); //response.json
|
||||||
|
updateData();
|
||||||
|
} //function(response)
|
||||||
|
) //.then
|
||||||
|
.catch(function (err) {
|
||||||
|
console.log("Unable to connect!");
|
||||||
|
var mainDiv = document.getElementById("main");
|
||||||
|
var errorDiv = document.createElement("div");
|
||||||
|
errorDiv.className = "error";
|
||||||
|
errorDiv.innerHTML =
|
||||||
|
'<h2>Uh-oh! Looks like you're offline.</h2>\
|
||||||
|
<img src="images/offline_doge.jpg" />\
|
||||||
|
<h4>Reconnect, then reload the app.</h4>\
|
||||||
|
<button type="button" class="refresh" onClick="location.reload(false);" >Reload</button>';
|
||||||
|
document.getElementById("main").appendChild(errorDiv);
|
||||||
|
}); //catch
|
||||||
|
} //initData
|
||||||
|
|
||||||
|
function updateData() {
|
||||||
|
//need to redeclare the url variable here to grab the latest user coins, etc.
|
||||||
|
var url =
|
||||||
|
"https://min-api.cryptocompare.com/data/pricemultifull?fsyms=" +
|
||||||
|
settings.get("user.coins") +
|
||||||
|
"&tsyms=" +
|
||||||
|
base +
|
||||||
|
"&extraParams=crypto-price-widget";
|
||||||
|
/*
|
||||||
|
** What data needs to be grabbed/changed?
|
||||||
|
** Base currency
|
||||||
|
** Coin price
|
||||||
|
** % change
|
||||||
|
** Portfolio - Coin price affects current value / total
|
||||||
|
*/
|
||||||
|
//console.log(settings.get('user.coins'));
|
||||||
|
fetch(url).then(
|
||||||
|
function (response) {
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
|
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>";
|
||||||
|
|
||||||
|
//Price Alert Test - PRO Feature
|
||||||
|
/*
|
||||||
|
* Choose crypto
|
||||||
|
* Choose price
|
||||||
|
* Choose equals, greater than, or less than price
|
||||||
|
* Alert set to "on"
|
||||||
|
* Alert when matches conditions
|
||||||
|
* When click on notification, alert set to "off"
|
||||||
|
* Use electron settings, localStorage, or sessionStorage?
|
||||||
|
* Should this be included in the updateData or separate?
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
var alerted = localStorage.getItem('alerted') || '';
|
||||||
|
if(coinSymbol.includes("BTC") && coinRAW[base].PRICE >= "5723" && alerted != 'yes') {
|
||||||
|
let notif = new window.Notification('Price Alert', {
|
||||||
|
body: "BTC has gone above 5790!"
|
||||||
|
});
|
||||||
|
notif.onclick = () => {
|
||||||
|
//so it doesn't keep notifying us every 3 seconds.
|
||||||
|
localStorage.setItem('alerted','yes');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// % 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;
|
||||||
|
if (quantityNumber != null) {
|
||||||
|
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 if (quantityValue != null) {
|
||||||
|
//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,");
|
||||||
|
}
|
||||||
|
} //for
|
||||||
|
}); //response.json().then
|
||||||
|
} //function(response)
|
||||||
|
); //then
|
||||||
|
appRefresh = setTimeout(function () {
|
||||||
|
updateData();
|
||||||
|
}, 5000); // run this once every 5 seconds
|
||||||
|
} //updateData()
|
||||||
|
|
||||||
|
// Let's do this thing!
|
||||||
|
initData();
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
settings.set("user.coins", selchb);
|
||||||
|
//clear and reload
|
||||||
|
clearData();
|
||||||
|
initData();
|
||||||
|
};
|
||||||
|
|
||||||
|
/***********
|
||||||
|
* 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";
|
||||||
|
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);
|
||||||
|
};
|
||||||
|
|
||||||
|
/***********
|
||||||
|
* SETTINGS
|
||||||
|
***********/
|
||||||
|
// Settings - list of coins
|
||||||
|
function loadJSON(callback) {
|
||||||
|
//Stored local version of https://www.cryptocompare.com/api/data/coinlist/ for performance
|
||||||
|
var file = "./coinlist.json";
|
||||||
|
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.id = actual_JSON.Data[key].Name;
|
||||||
|
label.htmlFor = 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
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***********
|
||||||
|
* 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
74
main.js
@ -1,45 +1,47 @@
|
|||||||
const electron = require('electron')
|
const electron = require("electron");
|
||||||
// Module to control application life.
|
// app control, application life.BrowserWindow creates native browser window.
|
||||||
const app = electron.app
|
const { app, BrowserWindow } = require("electron");
|
||||||
// Module to create native browser window.
|
|
||||||
const BrowserWindow = electron.BrowserWindow
|
|
||||||
//Store Window size and position
|
//Store Window size and position
|
||||||
const windowStateKeeper = require('electron-window-state');
|
const windowStateKeeper = require("electron-window-state");
|
||||||
|
|
||||||
|
const path = require("path");
|
||||||
|
const url = require("url");
|
||||||
|
|
||||||
const path = require('path')
|
const settings = require("electron-settings");
|
||||||
const url = require('url')
|
|
||||||
|
|
||||||
const settings = require('electron-settings');
|
|
||||||
|
|
||||||
// Keep a global reference of the window object, if you don't, the window will
|
// Keep a global reference of the window object, if you don't, the window will
|
||||||
// be closed automatically when the JavaScript object is garbage collected.
|
// be closed automatically when the JavaScript object is garbage collected.
|
||||||
let mainWindow
|
let mainWindow = null;
|
||||||
|
|
||||||
function createWindow () {
|
function createWindow() {
|
||||||
// Load the previous state with fallback to defaults
|
// Load the previous state with fallback to defaults
|
||||||
let mainWindowState = windowStateKeeper({
|
let mainWindowState = windowStateKeeper({
|
||||||
defaultWidth: 320,
|
defaultWidth: 320,
|
||||||
defaultHeight: 240
|
defaultHeight: 240,
|
||||||
});
|
});
|
||||||
// Create the browser window.
|
// Create the browser window.
|
||||||
mainWindow = new electron.BrowserWindow({
|
mainWindow = new BrowserWindow({
|
||||||
title: app.getName(),
|
title: app.getName(),
|
||||||
|
alwaysOnTop: false,
|
||||||
//show: false,
|
//show: false,
|
||||||
x: mainWindowState.x,
|
x: mainWindowState.x,
|
||||||
y: mainWindowState.y,
|
y: mainWindowState.y,
|
||||||
width: mainWindowState.width,
|
width: mainWindowState.width,
|
||||||
height: mainWindowState.height,
|
height: mainWindowState.height,
|
||||||
maxWidth: 360,
|
maxWidth: 960,
|
||||||
minWidth: 240,
|
minWidth: 290,
|
||||||
minHeight: 100,
|
minHeight: 100,
|
||||||
maximizable: false,
|
maximizable: false,
|
||||||
fullscreenable: false,
|
fullscreenable: false,
|
||||||
frame: false,
|
frame: false,
|
||||||
titleBarStyle: 'customButtonsOnHover',
|
titleBarStyle: "customButtonsOnHover",
|
||||||
autoHideMenuBar: true,
|
autoHideMenuBar: true,
|
||||||
transparent: true,
|
transparent: true,
|
||||||
icon: 'images/icon.png'
|
icon: path.join(__dirname, "images/icon.png"),
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Let us register listeners on the window, so we can update the state
|
// Let us register listeners on the window, so we can update the state
|
||||||
@ -48,45 +50,47 @@ function createWindow () {
|
|||||||
mainWindowState.manage(mainWindow);
|
mainWindowState.manage(mainWindow);
|
||||||
|
|
||||||
// and load the index.html of the app.
|
// and load the index.html of the app.
|
||||||
mainWindow.loadURL(url.format({
|
mainWindow.loadURL(
|
||||||
pathname: path.join(__dirname, 'index.html'),
|
url.format({
|
||||||
protocol: 'file:',
|
pathname: path.join(__dirname, "index.html"),
|
||||||
slashes: true
|
protocol: "file:",
|
||||||
}))
|
slashes: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
// Open the DevTools.
|
// Open the DevTools.
|
||||||
// mainWindow.webContents.openDevTools()
|
// mainWindow.webContents.openDevTools()
|
||||||
|
|
||||||
// Emitted when the window is closed.
|
// Emitted when the window is closed.
|
||||||
mainWindow.on('closed', function () {
|
mainWindow.on("closed", function () {
|
||||||
// Dereference the window object, usually you would store windows
|
// Dereference the window object, usually you would store windows
|
||||||
// in an array if your app supports multi windows, this is the time
|
// in an array if your app supports multi windows, this is the time
|
||||||
// when you should delete the corresponding element.
|
// when you should delete the corresponding element.
|
||||||
mainWindow = null
|
mainWindow = null;
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method will be called when Electron has finished
|
// This method will be called when Electron has finished
|
||||||
// initialization and is ready to create browser windows.
|
// initialization and is ready to create browser windows.
|
||||||
// Some APIs can only be used after this event occurs.
|
// Some APIs can only be used after this event occurs.
|
||||||
app.on('ready', createWindow)
|
app.on("ready", createWindow);
|
||||||
|
|
||||||
// Quit when all windows are closed.
|
// Quit when all windows are closed.
|
||||||
app.on('window-all-closed', function () {
|
app.on("window-all-closed", function () {
|
||||||
// On OS X it is common for applications and their menu bar
|
// On OS X it is common for applications and their menu bar
|
||||||
// to stay active until the user quits explicitly with Cmd + Q
|
// to stay active until the user quits explicitly with Cmd + Q
|
||||||
if (process.platform !== 'darwin') {
|
if (process.platform !== "darwin") {
|
||||||
app.quit()
|
app.quit();
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
app.on('activate', function () {
|
app.on("activate", function () {
|
||||||
// On OS X it's common to re-create a window in the app when the
|
// On OS X it's common to re-create a window in the app when the
|
||||||
// dock icon is clicked and there are no other windows open.
|
// dock icon is clicked and there are no other windows open.
|
||||||
if (mainWindow === null) {
|
if (mainWindow === null) {
|
||||||
createWindow()
|
createWindow();
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
// In this file you can include the rest of your app's specific main process
|
// In this file you can include the rest of your app's specific main process
|
||||||
// code. You can also put them in separate files and require them here.
|
// code. You can also put them in separate files and require them here.
|
||||||
|
|||||||
10677
package-lock.json
generated
23
package.json
@ -1,14 +1,14 @@
|
|||||||
{
|
{
|
||||||
"name": "crypto-price-widget",
|
"name": "crypto-price-widget",
|
||||||
"productName": "Crypto Price Widget",
|
"productName": "Crypto Price Widget",
|
||||||
"version": "1.0.0",
|
"version": "1.4.0",
|
||||||
"description": "A cross-platform app for tracking Crypto prices",
|
"description": "A cross-platform app for tracking Crypto prices",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "electron .",
|
"start": "electron .",
|
||||||
"build": "node build.js",
|
"build": "node build.js",
|
||||||
"package-mac": "electron-packager . --overwrite --platform=darwin --arch=x64 --icon=images/icon.icns --prune=true --out=release-builds",
|
"package-mac": "electron-packager . --overwrite --platform=darwin --arch=x64 --icon=images/icon.icns --prune=true --out=release-builds",
|
||||||
"package-win": "electron-packager . --overwrite --asar=true --platform=win32 --arch=ia32 --icon=images/icon_win.ico --prune=true --out=release-builds --version-string.CompanyName=CE --version-string.FileDescription=CE --version-string.ProductName=\"Crypto Price Widget\"",
|
"package-win": "electron-packager . --overwrite --platform=win32 --arch=x64 --icon=images/icon_win.ico --prune=true --out=release-builds --version-string.CompanyName=CE --version-string.FileDescription=CE --version-string.ProductName=\"Crypto Price Widget\"",
|
||||||
"package-linux": "electron-packager . --overwrite --platform=linux --arch=x64 --icon=images/icon.png --prune=true --out=release-builds"
|
"package-linux": "electron-packager . --overwrite --platform=linux --arch=x64 --icon=images/icon.png --prune=true --out=release-builds"
|
||||||
},
|
},
|
||||||
"repository": "https://github.com/nathanp/crypto-price-widget",
|
"repository": "https://github.com/nathanp/crypto-price-widget",
|
||||||
@ -22,18 +22,19 @@
|
|||||||
"author": "Nathan Parikh",
|
"author": "Nathan Parikh",
|
||||||
"license": "CC0-1.0",
|
"license": "CC0-1.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"electron": "~1.6.2",
|
"electron": "^9.0.4",
|
||||||
"electron-packager": "^8.7.2"
|
"electron-packager": "^14.2.1",
|
||||||
|
"electron-winstaller": "^4.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ava": "^0.15.2",
|
"ava": "^3.8.2",
|
||||||
"cryptocurrencies": "^1.0.0",
|
"cryptocurrencies": "^7.0.0",
|
||||||
"electron-settings": "^3.1.1",
|
"electron-settings": "^3.2.0",
|
||||||
"electron-window-state": "^4.1.1",
|
"electron-window-state": "^5.0.3",
|
||||||
"html5sortable": "^0.6.1",
|
"html5sortable": "^0.9.17",
|
||||||
"isomorphic-fetch": "^2.2.1",
|
"isomorphic-fetch": "^2.2.1",
|
||||||
"lodash.sortby": "^4.7.0",
|
"lodash.sortby": "^4.7.0",
|
||||||
"sortablejs": "^1.6.0",
|
"sortablejs": "^1.10.2",
|
||||||
"xo": "^0.16.0"
|
"xo": "^0.32.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,9 @@
|
|||||||
// This file is required by the index.html file and will
|
// This file is required by the index.html file and will
|
||||||
// be executed in the renderer process for that window.
|
// be executed in the renderer process for that window.
|
||||||
// All of the Node.js APIs are available in this process.
|
// All of the Node.js APIs are available in this process.
|
||||||
|
// Do this from the renderer process
|
||||||
|
/*
|
||||||
|
var notif = new window.Notification('Download Complete', {
|
||||||
|
body: "yolo"
|
||||||
|
})
|
||||||
|
*/
|
||||||