Code refactoring

--performance improvements
This commit is contained in:
sairaj mote 2023-01-09 19:42:00 +05:30
parent 07b64748c1
commit 89841d3f49
9 changed files with 1869 additions and 5641 deletions

File diff suppressed because one or more lines are too long

View File

@ -9,33 +9,34 @@
font-size: clamp(1rem, 1.2vmax, 3rem);
}
html, body {
html,
body {
height: 100%;
scroll-behavior: smooth;
}
body {
--accent-color: #304FFE;
--accent-color: #304ffe;
--light-shade: rgba(var(--text-color), 0.06);
--text-color: 17, 17, 17;
--text-color-light: 100, 100, 100;
--foreground-color: 255, 255, 255;
--background-color: #F6f6f6;
--background-color: 243, 245, 250;
--error-color: red;
--green: #00843b;
color: rgba(var(--text-color), 1);
background: var(--background-color);
background: rgba(var(--background-color), 1);
display: flex;
flex-direction: column;
}
body[data-theme=dark] {
--accent-color: #2353FF;
--accent-color: #a6b9ff;
--green: #13ff5a;
--text-color: 240, 240, 240;
--text-color-light: 170, 170, 170;
--foreground-color: 20, 20, 20;
--background-color: #0a0a0a;
--foreground-color: 27, 28, 29;
--background-color: 21, 22, 22;
--error-color: rgb(255, 106, 106);
}
@ -43,6 +44,26 @@ main {
flex: 1;
}
sm-chips {
--gap: 0.3rem;
}
sm-chip {
position: relative;
font-size: 0.9rem;
--border-radius: 0.5rem;
--padding: 0.5rem 0.8rem;
--background: rgba(var(--text-color), 0.06);
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
font-weight: 500;
}
sm-chip[selected] {
--background: var(--accent-color);
color: rgba(var(--foreground-color), 1);
}
.full-bleed {
grid-column: 1/4;
}
@ -86,7 +107,8 @@ p:not(:last-of-type) {
}
img {
object-fit: cover;
-o-object-fit: cover;
object-fit: cover;
}
a {
@ -97,59 +119,80 @@ a:focus-visible {
box-shadow: 0 0 0 0.1rem rgba(var(--text-color), 1) inset;
}
.button,
button {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
position: relative;
display: inline-flex;
border: none;
background-color: transparent;
overflow: hidden;
-webkit-tap-highlight-color: transparent;
align-items: center;
background: none;
cursor: pointer;
outline: none;
color: inherit;
font-size: 0.9rem;
font-weight: 500;
border-radius: 0.2rem;
padding: 0.5rem 0.6rem;
-webkit-tap-highlight-color: transparent;
border: none;
white-space: nowrap;
padding: 0.8rem;
border-radius: 0.5rem;
justify-content: center;
color: inherit;
min-width: -webkit-max-content;
min-width: -moz-max-content;
min-width: max-content;
}
button:focus-visible {
outline: rgba(var(--text-color), 1) 0.1rem solid;
}
a.button:any-link {
position: relative;
display: inline-flex;
align-items: center;
background: none;
.button:not(:disabled),
button:not(:disabled) {
cursor: pointer;
outline: none;
font-weight: 500;
font-size: 0.8rem;
border-radius: 0.3rem;
padding: 0.5rem 0.6rem;
align-self: flex-start;
text-decoration: none;
color: rgba(var(--text-color), 0.7);
-webkit-tap-highlight-color: transparent;
background-color: rgba(var(--text-color), 0.06);
}
a.button:any-link .icon {
margin-right: 0.3rem;
height: 1.2rem;
}
a:any-link:focus-visible {
outline: rgba(var(--text-color), 1) 0.1rem solid;
}
.button {
background-color: rgba(var(--text-color), 0.06);
color: var(--accent-color);
background-color: var(--blue-accent-1);
}
.button .icon {
fill: var(--accent-color);
}
.button--primary, .button--danger {
color: rgba(var(--background-color), 1) !important;
}
.button--primary .icon, .button--danger .icon {
fill: rgba(var(--background-color), 1);
}
.button--primary {
width: 100%;
background-color: var(--accent-color);
}
.button--danger {
background-color: var(--danger-color);
}
.button--small {
padding: 0.4rem 0.6rem;
}
sm-button {
--border-radius: 0.3rem;
.cta {
text-transform: uppercase;
font-size: 0.8rem;
font-weight: 700;
letter-spacing: 0.05em;
padding: 0.8rem 1rem;
}
.icon {
width: 1.2rem;
height: 1.2rem;
fill: rgba(var(--text-color), 0.8);
flex-shrink: 0;
}
.icon-only {
padding: 0.5rem;
border-radius: 0.3rem;
}
button:disabled {
opacity: 0.5;
}
ul {
@ -169,7 +212,7 @@ ul {
pointer-events: none;
}
.hide-completely {
.hidden {
display: none !important;
}
@ -189,8 +232,6 @@ ul {
word-wrap: break-word;
-ms-word-break: break-all;
word-break: break-word;
-ms-hyphens: auto;
-moz-hyphens: auto;
-webkit-hyphens: auto;
hyphens: auto;
}
@ -308,10 +349,6 @@ ul {
fill: rgba(var(--text-color), 0.9);
}
.button__icon {
height: 1.2rem;
width: 1.2rem;
}
.button__icon--left {
margin-right: 0.5rem;
}
@ -433,7 +470,17 @@ ul {
stroke-dasharray: 202;
margin: 2rem 0;
stroke: rgba(var(--text-color), 1);
animation: stroke-anim 2s infinite alternate;
-webkit-animation: stroke-anim 2s infinite alternate;
animation: stroke-anim 2s infinite alternate;
}
@-webkit-keyframes stroke-anim {
0% {
stroke-dashoffset: 202;
}
100% {
stroke-dashoffset: 0;
}
}
@keyframes stroke-anim {
@ -462,7 +509,6 @@ ul {
--background: rgba(var(--text-color), 0.06);
}
.search-torrent .icon {
height: 1.2rem;
fill: rgba(var(--text-color), 0.7);
}
@ -574,8 +620,6 @@ ul {
.progress-loader,
.placeholder-loader {
height: 1.2rem;
width: 1.2rem;
fill: none;
padding: 0.1rem;
stroke-width: 12;
@ -673,15 +717,10 @@ ul {
}
#torrent_download_button {
align-self: flex-start;
color: white;
flex-shrink: 0;
padding: 0.7rem;
justify-content: center;
background-color: var(--accent-color);
width: auto;
padding: 1rem;
}
#torrent_download_button .icon {
fill: white;
margin-right: 0.5rem;
}
@ -693,7 +732,6 @@ ul {
border: solid var(--accent-color) thin;
}
#torrent_index_tx .icon {
height: 1.2rem;
margin-right: 0.5rem;
}
@ -736,7 +774,9 @@ sm-option {
cursor: pointer;
font-weight: 500;
font-size: 0.95rem;
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
border-radius: 0.3rem;
padding: 0.3rem 0.6rem;
transition: background-color 0.3s;
@ -753,7 +793,9 @@ sm-option {
}
.selected-filter {
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
cursor: pointer;
display: inline-flex;
align-items: center;
@ -763,12 +805,11 @@ sm-option {
border-radius: 0.3rem;
}
.selected-filter .icon {
height: 1.2rem;
width: 1.2rem;
}
#page_selector strip-option,
#search_page_selector strip-option {
#page_selector sm-chip,
#search_page_selector sm-chip {
--border-radius: 0.3rem;
--active-option-color: white;
--active-option-backgroud-color: var(--accent-color);
@ -815,11 +856,9 @@ sm-option {
.torrent-card .progress-indicator {
margin-top: 1rem;
}
#torrent_tags {
font-size: 0.8rem;
}
#torrent_download_button,
#loader_container {
width: 100%;
@ -830,36 +869,28 @@ sm-option {
.popup__header {
padding: 1.5rem 1.5rem 0 0.5rem;
}
.auto-grid-2 {
grid-template-columns: 1fr 1fr;
}
.page-layout {
grid-template-columns: 1fr 90vw 1fr;
}
#main_header {
padding: 1.5rem;
}
.page__title {
font-size: 3rem;
}
.torrent-card {
padding: 1.5rem;
}
.torrent-preview {
gap: 3rem;
}
#torrent_name {
font-size: 4rem;
max-width: 16ch;
}
.info-section {
grid-template-columns: 1fr 1fr;
}
@ -881,7 +912,6 @@ sm-option {
width: 0.5rem;
height: 0.5rem;
}
::-webkit-scrollbar-thumb {
background: rgba(var(--text-color), 0.3);
border-radius: 1rem;
@ -889,7 +919,6 @@ sm-option {
::-webkit-scrollbar-thumb:hover {
background: rgba(var(--text-color), 0.5);
}
.search-suggestion:hover {
background-color: rgba(var(--text-color), 0.1);
}

2
css/main.min.css vendored

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,8 @@
font-size: clamp(1rem, 1.2vmax, 3rem);
}
html, body {
html,
body {
height: 100%;
scroll-behavior: smooth;
}
@ -20,11 +21,11 @@ body {
--text-color: 17, 17, 17;
--text-color-light: 100, 100, 100;
--foreground-color: 255, 255, 255;
--background-color: #F6f6f6;
--background-color: 243, 245, 250;
--error-color: red;
--green: #00843b;
color: rgba(var(--text-color), 1);
background: var(--background-color);
background: rgba(var(--background-color), 1);
display: flex;
flex-direction: column;
}
@ -34,8 +35,8 @@ body[data-theme=dark] {
--green: #13ff5a;
--text-color: 240, 240, 240;
--text-color-light: 170, 170, 170;
--foreground-color: 20, 20, 20;
--background-color: #0a0a0a;
--foreground-color: 27, 28, 29;
--background-color: 21, 22, 22;
--error-color: rgb(255, 106, 106);
}
@ -82,7 +83,8 @@ p:not(:last-of-type) {
}
img {
object-fit: cover;
-o-object-fit: cover;
object-fit: cover;
}
a {
@ -93,59 +95,80 @@ a:focus-visible {
box-shadow: 0 0 0 0.1rem rgba(var(--text-color), 1) inset;
}
.button,
button {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
position: relative;
display: inline-flex;
border: none;
background-color: transparent;
overflow: hidden;
-webkit-tap-highlight-color: transparent;
align-items: center;
background: none;
cursor: pointer;
outline: none;
color: inherit;
font-size: 0.9rem;
font-weight: 500;
border-radius: 0.2rem;
padding: 0.5rem 0.6rem;
-webkit-tap-highlight-color: transparent;
border: none;
white-space: nowrap;
padding: 0.8rem;
border-radius: 0.5rem;
justify-content: center;
color: inherit;
min-width: -webkit-max-content;
min-width: -moz-max-content;
min-width: max-content;
}
button:focus-visible {
outline: rgba(var(--text-color), 1) 0.1rem solid;
}
a.button:any-link {
position: relative;
display: inline-flex;
align-items: center;
background: none;
.button:not(:disabled),
button:not(:disabled) {
cursor: pointer;
outline: none;
font-weight: 500;
font-size: 0.8rem;
border-radius: 0.3rem;
padding: 0.5rem 0.6rem;
align-self: flex-start;
text-decoration: none;
color: rgba(var(--text-color), 0.7);
-webkit-tap-highlight-color: transparent;
background-color: rgba(var(--text-color), 0.06);
}
a.button:any-link .icon {
margin-right: 0.3rem;
height: 1.2rem;
}
a:any-link:focus-visible {
outline: rgba(var(--text-color), 1) 0.1rem solid;
}
.button {
background-color: rgba(var(--text-color), 0.06);
color: var(--accent-color);
background-color: var(--blue-accent-1);
}
.button .icon {
fill: var(--accent-color);
}
.button--primary, .button--danger {
color: rgba(var(--background-color), 1) !important;
}
.button--primary .icon, .button--danger .icon {
fill: rgba(var(--background-color), 1);
}
.button--primary {
width: 100%;
background-color: var(--accent-color);
}
.button--danger {
background-color: var(--danger-color);
}
.button--small {
padding: 0.4rem 0.6rem;
}
sm-button {
--border-radius: 0.3rem;
.cta {
text-transform: uppercase;
font-size: 0.8rem;
font-weight: 700;
letter-spacing: 0.05em;
padding: 0.8rem 1rem;
}
.icon {
width: 1.2rem;
height: 1.2rem;
fill: rgba(var(--text-color), 0.8);
flex-shrink: 0;
}
.icon-only {
padding: 0.5rem;
border-radius: 0.3rem;
}
button:disabled {
opacity: 0.5;
}
sm-popup {
@ -169,7 +192,7 @@ ul {
pointer-events: none;
}
.hide-completely {
.hidden {
display: none !important;
}
@ -189,8 +212,6 @@ ul {
word-wrap: break-word;
-ms-word-break: break-all;
word-break: break-word;
-ms-hyphens: auto;
-moz-hyphens: auto;
-webkit-hyphens: auto;
hyphens: auto;
}
@ -454,7 +475,17 @@ tags-input {
fill: none;
stroke-dashoffset: 180;
stroke-dasharray: 180;
animation: load 3.6s linear infinite, spin 1s linear infinite;
-webkit-animation: load 3.6s linear infinite, spin 1s linear infinite;
animation: load 3.6s linear infinite, spin 1s linear infinite;
}
@-webkit-keyframes load {
50% {
stroke-dashoffset: 0;
}
100% {
stroke-dashoffset: -180;
}
}
@keyframes load {
@ -465,6 +496,11 @@ tags-input {
stroke-dashoffset: -180;
}
}
@-webkit-keyframes spin {
100% {
transform: rotate(360deg);
}
}
@keyframes spin {
100% {
transform: rotate(360deg);
@ -507,16 +543,18 @@ sm-select {
file-input {
font-size: 0.9rem;
--button-color: var(--background-color);
--button-color: rgba(var(--background-color), 1);
}
file-input .icon {
height: 1.2rem;
width: 1.2rem;
fill: var(--background-color);
fill: rgba(var(--background-color), 1);
}
summary {
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
cursor: pointer;
justify-self: center;
margin-bottom: 1.5rem;
@ -547,7 +585,6 @@ summary {
#main_header {
padding: 1.5rem 2rem;
}
#torrent_form {
width: 32rem;
}
@ -557,7 +594,6 @@ summary {
width: 0.5rem;
height: 0.5rem;
}
::-webkit-scrollbar-thumb {
background: rgba(var(--text-color), 0.3);
border-radius: 1rem;

File diff suppressed because one or more lines are too long

View File

@ -1,495 +1,514 @@
*{
padding: 0;
margin: 0;
box-sizing: border-box;
font-family: 'Inter', sans-serif;
* {
padding: 0;
margin: 0;
box-sizing: border-box;
font-family: "Inter", sans-serif;
}
:root{
font-size: clamp(1rem, 1.2vmax, 3rem);
:root {
font-size: clamp(1rem, 1.2vmax, 3rem);
}
html, body{
height: 100%;
scroll-behavior: smooth;
html,
body {
height: 100%;
scroll-behavior: smooth;
}
body {
--accent-color: #0eaf8f;
--light-shade: rgba(var(--text-color), 0.06);
--text-color: 17, 17, 17;
--text-color-light: 100, 100, 100;
--foreground-color: 255, 255, 255;
--background-color: #F6f6f6;
--error-color: red;
--green: #00843b;
color: rgba(var(--text-color), 1);
background: var(--background-color);
display: flex;
flex-direction: column;
--accent-color: #0eaf8f;
--light-shade: rgba(var(--text-color), 0.06);
--text-color: 17, 17, 17;
--text-color-light: 100, 100, 100;
--foreground-color: 255, 255, 255;
--background-color: 243, 245, 250;
--error-color: red;
--green: #00843b;
color: rgba(var(--text-color), 1);
background: rgba(var(--background-color), 1);
display: flex;
flex-direction: column;
}
body[data-theme='dark']{
--accent-color: #1abc9c;
--green: #13ff5a;
--text-color: 240, 240, 240;
--text-color-light: 170, 170, 170;
--foreground-color: 20, 20, 20;
--background-color: #0a0a0a;
--error-color: rgb(255, 106, 106);
body[data-theme="dark"] {
--accent-color: #1abc9c;
--green: #13ff5a;
--text-color: 240, 240, 240;
--text-color-light: 170, 170, 170;
--foreground-color: 27, 28, 29;
--background-color: 21, 22, 22;
--error-color: rgb(255, 106, 106);
}
.full-bleed{
grid-column: 1/4;
.full-bleed {
grid-column: 1/4;
}
.h1{
font-size: 2.5rem;
.h1 {
font-size: 2.5rem;
}
.h2{
font-size: 2rem;
.h2 {
font-size: 2rem;
}
.h3{
font-size: 1.4rem;
.h3 {
font-size: 1.4rem;
}
.h4{
font-size: 1rem;
.h4 {
font-size: 1rem;
}
.h5{
font-size: 0.8rem;
.h5 {
font-size: 0.8rem;
}
.uppercase{
text-transform: uppercase;
.uppercase {
text-transform: uppercase;
}
.capitalize{
text-transform: capitalize;
.capitalize {
text-transform: capitalize;
}
p {
font-size: 0.8;
max-width: 60ch;
line-height: 1.7;
color: rgba(var(--text-color), 0.8);
&:not(:last-of-type){
margin-bottom: 1rem;
}
font-size: 0.8;
max-width: 60ch;
line-height: 1.7;
color: rgba(var(--text-color), 0.8);
&:not(:last-of-type) {
margin-bottom: 1rem;
}
}
img{
object-fit: cover;
img {
object-fit: cover;
}
a{
color: inherit;
text-decoration: none;
&:focus-visible{
box-shadow: 0 0 0 0.1rem rgba(var(--text-color), 1) inset;
}
a {
color: inherit;
text-decoration: none;
&:focus-visible {
box-shadow: 0 0 0 0.1rem rgba(var(--text-color), 1) inset;
}
}
button{
position: relative;
display: inline-flex;
overflow: hidden;
align-items: center;
background: none;
.button,
button {
user-select: none;
position: relative;
display: inline-flex;
border: none;
background-color: transparent;
overflow: hidden;
-webkit-tap-highlight-color: transparent;
align-items: center;
font-size: 0.9rem;
font-weight: 500;
white-space: nowrap;
padding: 0.8rem;
border-radius: 0.5rem;
justify-content: center;
color: inherit;
min-width: max-content;
&:not(:disabled) {
cursor: pointer;
outline: none;
color: inherit;
font-size: 0.9rem;
font-weight: 500;
border-radius: 0.2rem;
padding: 0.5rem 0.6rem;
-webkit-tap-highlight-color: transparent;
border: none;
}
}
button:focus-visible{
outline: rgba(var(--text-color), 1) 0.1rem solid;
}
a.button:any-link{
position: relative;
display: inline-flex;
align-items: center;
background: none;
cursor: pointer;
outline: none;
font-weight: 500;
font-size: 0.8rem;
border-radius: 0.3rem;
padding: 0.5rem 0.6rem;
align-self: flex-start;
text-decoration: none;
color: rgba(var(--text-color), 0.7);
-webkit-tap-highlight-color: transparent;
background-color: rgba(var(--text-color), 0.06);
.icon{
margin-right: 0.3rem;
height: 1.2rem;
.button {
color: var(--accent-color);
background-color: var(--blue-accent-1);
.icon {
fill: var(--accent-color);
}
&--primary,
&--danger {
color: rgba(var(--background-color), 1) !important;
.icon {
fill: rgba(var(--background-color), 1);
}
}
a:any-link:focus-visible{
outline: rgba(var(--text-color), 1) 0.1rem solid;
}
.button{
background-color: rgba(var(--text-color), 0.06);
}
sm-button{
--border-radius: 0.3rem;
}
sm-popup{
--width: min(24rem, 100%);
}
ul{
list-style: none;
}
.flex{
display: flex;
}
.grid{
display: grid;
}
.hide{
opacity: 0;
pointer-events: none;
}
.hide-completely{
display: none !important;
}
.no-transformations{
transform: none !important;
}
.overflow-ellipsis{
}
&--primary {
width: 100%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.breakable{
overflow-wrap: break-word;
word-wrap: break-word;
-ms-word-break: break-all;
word-break: break-word;
-ms-hyphens: auto;
-moz-hyphens: auto;
-webkit-hyphens: auto;
hyphens: auto;
}
.flex{
display: flex;
}
.grid{
display: grid;
}
.flow-column{
grid-auto-flow: column;
}
.gap-0-5{
gap: 0.5rem;
}
.gap-1{
gap: 1rem;
}
.gap-1-5{
gap: 1.5rem;
}
.gap-2{
gap: 2rem;
}
.gap-3{
gap: 3rem;
}
.text-align-right{
text-align: right;
}
.align-start{
align-items: flex-start;
}
.align-center{
align-items: center;
}
.text-center{
text-align: center;
}
.justify-start{
justify-content: start;
}
.justify-center{
justify-content: center;
}
.justify-right{
margin-left: auto;
}
.align-self-center{
align-self: center;
}
.justify-self-center{
justify-self: center;
}
.justify-self-start{
justify-self: start;
}
.justify-self-end{
justify-self: end;
}
.direction-column{
flex-direction: column;
}
.space-between{
justify-content: space-between;
}
.w-100{
width: 100%;
}
.ripple{
position: absolute;
border-radius: 50%;
transform: scale(0);
background: rgba(var(--text-color), 0.16);
pointer-events: none;
}
.interact{
position: relative;
overflow: hidden;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
}
.observe-empty-state:empty{
display: none;
}
.observe-empty-state:not(:empty) ~ .empty-state{
display: none;
}
.icon{
width: 1.5rem;
height: 1.5rem;
fill: rgba(var(--text-color), 0.9);
}
.button__icon{
height: 1.2rem;
width: 1.2rem;
&--left{
margin-right: 0.5rem;
}
&--right{
margin-left: 0.5rem;
}
}
.page-layout{
position: relative;
display: grid;
grid-template-columns: 1rem minmax(0, 1fr) 1rem;
& > * {
grid-column: 2/3;
}
}
.popup__header{
display: grid;
gap: 0.5rem;
width: 100%;
padding: 0 1.5rem 0 0.5rem;
align-items: center;
grid-template-columns: auto 1fr;
}
.popup__header__close{
padding: 0.5rem;
cursor: pointer;
}
.button--primary{
color: white;
font-weight: 500;
padding: 0.5rem 1.2rem;
background-color: var(--accent-color);
}
&--danger {
background-color: var(--danger-color);
}
&--small {
padding: 0.4rem 0.6rem;
}
}
.cta {
text-transform: uppercase;
font-size: 0.8rem;
font-weight: 700;
letter-spacing: 0.05em;
padding: 0.8rem 1rem;
}
.icon {
width: 1.2rem;
height: 1.2rem;
fill: rgba(var(--text-color), 0.8);
flex-shrink: 0;
}
.icon-only {
padding: 0.5rem;
border-radius: 0.3rem;
}
button:disabled {
opacity: 0.5;
}
sm-popup {
--width: min(24rem, 100%);
}
ul {
list-style: none;
}
.flex {
display: flex;
}
.grid {
display: grid;
}
.hide {
opacity: 0;
pointer-events: none;
}
.hidden {
display: none !important;
}
.no-transformations {
transform: none !important;
}
.overflow-ellipsis {
width: 100%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.breakable {
overflow-wrap: break-word;
word-wrap: break-word;
-ms-word-break: break-all;
word-break: break-word;
-ms-hyphens: auto;
-moz-hyphens: auto;
-webkit-hyphens: auto;
hyphens: auto;
}
.flex {
display: flex;
}
.grid {
display: grid;
}
.flow-column {
grid-auto-flow: column;
}
.gap-0-5 {
gap: 0.5rem;
}
.gap-1 {
gap: 1rem;
}
.gap-1-5 {
gap: 1.5rem;
}
.gap-2 {
gap: 2rem;
}
.gap-3 {
gap: 3rem;
}
.text-align-right {
text-align: right;
}
.align-start {
align-items: flex-start;
}
.align-center {
align-items: center;
}
.text-center {
text-align: center;
}
.justify-start {
justify-content: start;
}
.justify-center {
justify-content: center;
}
.justify-right {
margin-left: auto;
}
.align-self-center {
align-self: center;
}
.justify-self-center {
justify-self: center;
}
.justify-self-start {
justify-self: start;
}
.justify-self-end {
justify-self: end;
}
.direction-column {
flex-direction: column;
}
.space-between {
justify-content: space-between;
}
.w-100 {
width: 100%;
}
.ripple {
position: absolute;
border-radius: 50%;
transform: scale(0);
background: rgba(var(--text-color), 0.16);
pointer-events: none;
}
.interact {
position: relative;
overflow: hidden;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
}
.observe-empty-state:empty {
display: none;
}
.observe-empty-state:not(:empty) ~ .empty-state {
display: none;
}
.icon {
width: 1.5rem;
height: 1.5rem;
fill: rgba(var(--text-color), 0.9);
}
.button__icon {
height: 1.2rem;
width: 1.2rem;
&--left {
margin-right: 0.5rem;
}
&--right {
margin-left: 0.5rem;
}
}
.page-layout {
position: relative;
display: grid;
grid-template-columns: 1rem minmax(0, 1fr) 1rem;
& > * {
grid-column: 2/3;
}
}
.popup__header {
display: grid;
gap: 0.5rem;
width: 100%;
padding: 0 1.5rem 0 0.5rem;
align-items: center;
grid-template-columns: auto 1fr;
}
.popup__header__close {
padding: 0.5rem;
cursor: pointer;
}
.button--primary {
color: white;
font-weight: 500;
padding: 0.5rem 1.2rem;
background-color: var(--accent-color);
}
#confirmation_popup,
#prompt_popup {
flex-direction: column;
h4 {
font-weight: 500;
margin-bottom: 0.5rem;
}
sm-button{
margin: 0;
}
.flex {
padding: 0;
margin-top: 1rem;
sm-button:first-of-type {
margin-right: 0.6rem;
margin-left: auto;
}
flex-direction: column;
h4 {
font-weight: 500;
margin-bottom: 0.5rem;
}
sm-button {
margin: 0;
}
.flex {
padding: 0;
margin-top: 1rem;
sm-button:first-of-type {
margin-right: 0.6rem;
margin-left: auto;
}
}
}
#main_header{
position: relative;
display: grid;
padding: 1rem;
align-items: center;
margin-bottom: 1rem;
grid-template-columns: 1fr auto auto;
a.button{
font-size: 0.9rem;
font-weight: 500;
border: solid thin var(--accent-color);
}
}
#main_header__logo{
height: 1.8rem;
width: 1.8rem;
}
.theme-switcher{
position: relative;
justify-self: flex-end;
width: 1.5rem;
height: 1.5rem;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
.icon{
position: absolute;
transition: transform 0.6s;
}
}
.theme-switcher__checkbox{
display: none;
&:checked ~ .moon-icon{
transform: scale(0) rotate(90deg);
}
&:not(:checked) ~ .sun-icon{
transform: scale(0) rotate(-90deg);
}
}
.fieldset{
display: grid;
gap: 1rem;
border: none;
padding: 1rem;
border-radius: 0.5rem;
background-color: rgba(var(--text-color), 0.06);
}
tags-input{
#main_header {
position: relative;
display: grid;
padding: 1rem;
align-items: center;
margin-bottom: 1rem;
grid-template-columns: 1fr auto auto;
a.button {
font-size: 0.9rem;
font-weight: 500;
border: solid thin var(--accent-color);
}
}
.torrent-upload-section{
display: grid;
place-items: center;
flex: 1;
padding: 0 0 2rem 0;
#main_header__logo {
height: 1.8rem;
width: 1.8rem;
}
#torrent_form{
display: grid;
padding: 0 1rem;
margin-bottom: 3rem;
.theme-switcher {
position: relative;
justify-self: flex-end;
width: 1.5rem;
height: 1.5rem;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
.icon {
position: absolute;
transition: transform 0.6s;
}
}
.theme-switcher__checkbox {
display: none;
&:checked ~ .moon-icon {
transform: scale(0) rotate(90deg);
}
&:not(:checked) ~ .sun-icon {
transform: scale(0) rotate(-90deg);
}
}
.fieldset {
display: grid;
gap: 1rem;
border: none;
padding: 1rem;
border-radius: 0.5rem;
background-color: rgba(var(--text-color), 0.06);
}
tags-input {
font-size: 0.9rem;
}
.torrent-upload-section {
display: grid;
place-items: center;
flex: 1;
padding: 0 0 2rem 0;
}
#torrent_form {
display: grid;
padding: 0 1rem;
margin-bottom: 3rem;
}
.loader {
height: 1.6rem;
width: 1.6rem;
stroke-width: 8;
overflow: visible;
stroke: var(--accent-color);
fill: none;
stroke-dashoffset: 180;
stroke-dasharray: 180;
animation: load 3.6s linear infinite, spin 1s linear infinite;
height: 1.6rem;
width: 1.6rem;
stroke-width: 8;
overflow: visible;
stroke: var(--accent-color);
fill: none;
stroke-dashoffset: 180;
stroke-dasharray: 180;
animation: load 3.6s linear infinite, spin 1s linear infinite;
}
@keyframes load {
50% {
stroke-dashoffset: 0;
}
100%{
stroke-dashoffset: -180;
}
50% {
stroke-dashoffset: 0;
}
100% {
stroke-dashoffset: -180;
}
}
@keyframes spin {
100% {
transform: rotate(360deg);
}
100% {
transform: rotate(360deg);
}
}
.progress-bar{
position: relative;
display: flex;
width: 100%;
align-items: center;
height: 0.5rem;
border-radius: 2rem;
.progress-bar {
position: relative;
display: flex;
width: 100%;
align-items: center;
height: 0.5rem;
border-radius: 2rem;
}
.progress{
position: absolute;
left: 0;
height: 100%;
border-radius: 2rem;
transition: width 0.3s;
background-color: var(--accent-color);
.progress {
position: absolute;
left: 0;
height: 100%;
border-radius: 2rem;
transition: width 0.3s;
background-color: var(--accent-color);
}
#overlay_content,
#overlay_content > *{
display: grid;
gap: 1.5rem;
justify-content: center;
justify-items: center;
text-align: center;
#overlay_content > * {
display: grid;
gap: 1.5rem;
justify-content: center;
justify-items: center;
text-align: center;
}
#upload_torrent_button{
width: 100%;
#upload_torrent_button {
width: 100%;
}
sm-select{
--max-height: 40vh;
sm-select {
--max-height: 40vh;
}
file-input{
font-size: 0.9rem;
--button-color: var(--background-color);
.icon{
height: 1.2rem;
width: 1.2rem;
fill: var(--background-color);
}
file-input {
font-size: 0.9rem;
--button-color: rgba(var(--background-color), 1);
.icon {
height: 1.2rem;
width: 1.2rem;
fill: rgba(var(--background-color), 1);
}
}
summary{
user-select: none;
cursor: pointer;
justify-self: center;
margin-bottom: 1.5rem;
summary {
user-select: none;
cursor: pointer;
justify-self: center;
margin-bottom: 1.5rem;
}
.page-footer{
display: grid;
gap: 1.5rem;
padding: 2rem 1.5rem;
text-align: center;
justify-items: center;
background-color: rgba(var(--text-color), 0.06);
.icon{
height: 1.6rem;
width: 1.6rem;
margin-left: -0.4rem;
margin-right: 0.1rem;
}
h5{
font-weight: 400;
}
h4{
font-weight: 600;
}
.page-footer {
display: grid;
gap: 1.5rem;
padding: 2rem 1.5rem;
text-align: center;
justify-items: center;
background-color: rgba(var(--text-color), 0.06);
.icon {
height: 1.6rem;
width: 1.6rem;
margin-left: -0.4rem;
margin-right: 0.1rem;
}
h5 {
font-weight: 400;
}
h4 {
font-weight: 600;
}
}
@media screen and (min-width: 640px) {
#main_header{
padding: 1.5rem 2rem;
}
#torrent_form{
width: 32rem;
}
#main_header {
padding: 1.5rem 2rem;
}
#torrent_form {
width: 32rem;
}
}
@media (any-hover: hover){
::-webkit-scrollbar{
width: 0.5rem;
height: 0.5rem;
@media (any-hover: hover) {
::-webkit-scrollbar {
width: 0.5rem;
height: 0.5rem;
}
::-webkit-scrollbar-thumb {
background: rgba(var(--text-color), 0.3);
border-radius: 1rem;
&:hover {
background: rgba(var(--text-color), 0.5);
}
::-webkit-scrollbar-thumb{
background: rgba(var(--text-color), 0.3);
border-radius: 1rem;
&:hover{
background: rgba(var(--text-color), 0.5);
}
}
}
}
}

View File

@ -15,21 +15,23 @@
<body data-theme="">
<sm-notifications id="notification_drawer"></sm-notifications>
<audio id="notification_sound">
<source src="https://rmservices.duckdns.org/files/notification-sound.mp3" type="audio/mpeg">
<source src="https://rmservices.duckdns.org/files/notification-sound.ogg" type="audio/ogg">
</audio>
<sm-popup id="confirmation">
<sm-popup id="confirmation_popup">
<h4 id="confirm_title"></h4>
<p id="confirm_message"></p>
<div class="flex align-center">
<sm-button variant="no-outline" class="cancel-btn">Cancel</sm-button>
<sm-button variant="no-outline" class="submit-btn">OK</button>
<div class="flex align-center gap-0-5 margin-left-auto">
<button class="button cancel-button">Cancel</button>
<button class="button button--primary confirm-button">OK</button>
</div>
</sm-popup>
<sm-popup id="filter_popup">
<header class="popup__header" slot="header">
<button class="popup__header__close" onclick="getRef('filter_popup').hide()">
<svg class="icon close-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z"/></svg>
<svg class="icon close-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24"
height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z" />
</svg>
</button>
<h3>Filter</h3>
</header>
@ -43,7 +45,7 @@
<button onclick="addFilter()" class="button button--primary">Fliter</button>
</div>
</form>
</section>
</section>
</sm-popup>
<header id="main_header">
<div class="flex align-center">
@ -51,22 +53,10 @@
<path
d="M20.46,21.32C20,19.78,18.6,18.59,15.3,17a12.67,12.67,0,0,1-2.64-1.56,4.27,4.27,0,0,1-.79-1,2.6,2.6,0,0,1,0-1.41c.24-.68.49-1,2.43-2.85a7.18,7.18,0,0,0,2.09-2.92,4.25,4.25,0,0,0,0-1.77,6.52,6.52,0,0,0-2.85-3.11c-.56-.36-.81-.4-.81-.15a2.33,2.33,0,0,1-.18.45L12.4,3l-.53-.36c-.28-.21-.64-.41-.77-.49s-.46-.11-.46,0a6.21,6.21,0,0,1-.37.83s-.08,0-.17-.08c-1.15-.83-1.64-1-1.64-.73A7.33,7.33,0,0,1,7.7,3.65C6.48,5.68,5.24,6.7,4,6.7c-.56,0-.54,0-.37.64s.2.58.68.43a3.37,3.37,0,0,0,1.09-.54.86.86,0,0,1,.3-.17,1.34,1.34,0,0,1,.13.39.79.79,0,0,0,.17.4A3.5,3.5,0,0,0,7.37,7.3L7.8,7l.09.34c.12.45.19.51.62.39a4.25,4.25,0,0,0,2.17-1.54l.38-.45,0,.39A5.92,5.92,0,0,1,8.89,9.54L7.67,10.71c-2,1.93-1.89,3.51.37,5a27.41,27.41,0,0,0,2.89,1.51c.17.07.62.32,1,.54C14,19,15,20.23,15,21.48a2,2,0,0,0,0,.49h0c0,.05,0,.05.56-.1a1.89,1.89,0,0,0,.53-.21,2.41,2.41,0,0,0-.34-1.15,7.05,7.05,0,0,0-1.68-1.77,21.91,21.91,0,0,0-3.2-1.83A9.53,9.53,0,0,1,8.16,15.2a2.18,2.18,0,0,1-.74-1.55C7.42,12.79,7.86,12,9,11c1.77-1.64,2.45-2.45,2.92-3.55a2.28,2.28,0,0,0,.26-1.26A2,2,0,0,0,12,5.06l-.2-.45L12,4.3l.28-.49.09-.18L12.6,4a3.69,3.69,0,0,1,.61,1.76A3.47,3.47,0,0,1,12.94,7l-.09.25s-.21.37-.41.69A17.78,17.78,0,0,1,9.91,10.6c-1.07,1-1.43,1.62-1.47,2.47a2.05,2.05,0,0,0,.7,1.73,10.47,10.47,0,0,0,3.28,2.08c2.28,1.13,3.26,1.81,4,2.73a2.94,2.94,0,0,1,.74,1.75,1.26,1.26,0,0,0,.09.57.48.48,0,0,0,.26,0l.51-.13.29-.08,0-.28c-.13-1-1-2-2.47-3a25.52,25.52,0,0,0-3.26-1.77,8.59,8.59,0,0,1-2.23-1.43,2.09,2.09,0,0,1-.5-2.62c.26-.53.5-.83,2.35-2.6,1.51-1.45,2.15-2.58,2.15-3.79A3.67,3.67,0,0,0,13,3.48a3,3,0,0,1-.4-.42A1.85,1.85,0,0,1,13,2.33a6.74,6.74,0,0,1,1.83,1.73,2.62,2.62,0,0,1,.47,1.68,3,3,0,0,1-.55,1.84c-.45.78-.79,1.14-2.67,2.93a5.56,5.56,0,0,0-1.3,1.64,1.77,1.77,0,0,0-.21,1,1.76,1.76,0,0,0,.19.92,6.28,6.28,0,0,0,2.9,2.34,21.6,21.6,0,0,1,3.66,2c1.35,1,2,2,2,3a1.06,1.06,0,0,0,.05.47,2.83,2.83,0,0,0,1-.24C20.56,21.68,20.56,21.66,20.46,21.32ZM7.29,6.4h0a2.23,2.23,0,0,1-.9.28L6,6.72l.43-.53a15.22,15.22,0,0,0,1.89-3,3.52,3.52,0,0,1,.38-.67c.07-.08.49.2,1,.64l.39.35L9.66,4A6.7,6.7,0,0,1,7.29,6.4Zm3.58-1.11A5.8,5.8,0,0,1,9.25,6.51h0a3.3,3.3,0,0,1-.74.17l-.35,0,.39-.49a15.64,15.64,0,0,0,1.32-2,4.63,4.63,0,0,1,.28-.49c.06-.08.33.26.57.77l.28.57Zm1-1.4a1.63,1.63,0,0,1-.28.4A6.63,6.63,0,0,1,11,3.72l-.53-.56.12-.29c.2-.49.24-.51.64-.19a5.57,5.57,0,0,1,.85.78A2.78,2.78,0,0,1,11.87,3.89Z" />
</svg>
<a href="./index.html" class="header__company-name">RanchiMall</a>
<a href="#/home" class="header__company-name">RanchiMall</a>
</div>
<a href="#how_it_works" class="button justify-right">How it works?</a>
<label class="theme-switcher" title="Change theme">
<input id="theme_switcher" class="theme-switcher__checkbox" type="checkbox">
<svg class="icon moon-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M10 6a8 8 0 0 0 11.955 6.956C21.474 18.03 17.2 22 12 22 6.477 22 2 17.523 2 12c0-5.2 3.97-9.474 9.044-9.955A7.963 7.963 0 0 0 10 6zm-6 6a8 8 0 0 0 8 8 8.006 8.006 0 0 0 6.957-4.045c-.316.03-.636.045-.957.045-5.523 0-10-4.477-10-10 0-.321.015-.64.045-.957A8.006 8.006 0 0 0 4 12zm14.164-9.709L19 2.5v1l-.836.209a2 2 0 0 0-1.455 1.455L16.5 6h-1l-.209-.836a2 2 0 0 0-1.455-1.455L13 3.5v-1l.836-.209A2 2 0 0 0 15.29.836L15.5 0h1l.209.836a2 2 0 0 0 1.455 1.455zm5 5L24 7.5v1l-.836.209a2 2 0 0 0-1.455 1.455L21.5 11h-1l-.209-.836a2 2 0 0 0-1.455-1.455L18 8.5v-1l.836-.209a2 2 0 0 0 1.455-1.455L20.5 5h1l.209.836a2 2 0 0 0 1.455 1.455z" />
</svg>
<svg class="icon sun-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M12 18a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-2a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM11 1h2v3h-2V1zm0 19h2v3h-2v-3zM3.515 4.929l1.414-1.414L7.05 5.636 5.636 7.05 3.515 4.93zM16.95 18.364l1.414-1.414 2.121 2.121-1.414 1.414-2.121-2.121zm2.121-14.85l1.414 1.415-2.121 2.121-1.414-1.414 2.121-2.121zM5.636 16.95l1.414 1.414-2.121 2.121-1.414-1.414 2.121-2.121zM23 11v2h-3v-2h3zM4 11v2H1v-2h3z" />
</svg>
</label>
<a href="#/how_it_works" class="button justify-right">How it works?</a>
<theme-toggle></theme-toggle>
</header>
<main>
<section id="loading_page" class="page">
@ -79,7 +69,7 @@
<h4>FLO Torrent</h4>
<p>Getting torrents from FLO blockchain</p>
</section>
<section id="homepage" class="page hide-completely">
<section id="home" class="page hidden">
<section id="search_section" class="page-layout">
<svg class="app-icon icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<path
@ -103,7 +93,7 @@
<section class="page-layout">
<div class="flex align-center space-between">
<h4>Recent</h4>
<a class="button" href="#browse">
<a class="button" href="#/browse">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24"
width="24px">
<path d="M0 0h24v24H0z" fill="none" />
@ -116,7 +106,7 @@
<section id="torrent_container" class="torrent-container"></section>
</section>
</section>
<section id="torrent" class="page hide-completely page-layout">
<section id="torrent" class="page hidden page-layout">
<section class="torrent-preview">
<div id="torrent_type_icon" class="torrent-type-icon"></div>
<div class="torrent-preview__info-section">
@ -125,16 +115,21 @@
<p id="torrent_description"></p>
<h5 id="torrent_uploader"></h5>
<a id="torrent_index_tx" target="_blank" class="flex align-center">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M10 6v2H5v11h11v-5h2v6a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h6zm11-3v8h-2V6.413l-7.793 7.794-1.414-1.414L17.585 5H13V3h8z"/></svg>
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M10 6v2H5v11h11v-5h2v6a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h6zm11-3v8h-2V6.413l-7.793 7.794-1.414-1.414L17.585 5H13V3h8z" />
</svg>
See the index entry in blockchain
</a>
<div id="download_container">
<button id="torrent_download_button"
<button id="torrent_download_button" class="button button--primary"
onclick="downloadTorrent(currentTorrent.filename, currentTorrent.startTx, currentTorrent.chunks, currentTorrent.id, true)">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24"
height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M16 2l5 5v14.008a.993.993 0 0 1-.993.992H3.993A1 1 0 0 1 3 21.008V2.992C3 2.444 3.445 2 3.993 2H16zm-3 10V8h-2v4H8l4 4 4-4h-3z" />
d="M16 2l5 5v14.008a.993.993 0 0 1-.993.992H3.993A1 1 0 0 1 3 21.008V2.992C3 2.444 3.445 2 3.993 2H16zm-3 10V8h-2v4H8l4 4 4-4h-3z" />
</svg>
Get torrent
</button>
@ -143,7 +138,7 @@
</div>
</section>
</section>
<section id="search" class="page hide-completely page-layout">
<section id="search" class="page hidden page-layout">
<div class="grid gap-1">
<h1 class="page__title">Search</h1>
<section class="search-container">
@ -161,51 +156,55 @@
<div class="flex align-center space-between">
<p id="result_for"></p>
<button id="filter_button" class="button" onclick="getRef('filter_popup').show()">
<svg class="icon button__icon button__icon--left" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0H24V24H0z"/><path d="M21 4L21 6 20 6 14 15 14 22 10 22 10 15 4 6 3 6 3 4z"/></svg>
<svg class="icon button__icon button__icon--left" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0H24V24H0z" />
<path d="M21 4L21 6 20 6 14 15 14 22 10 22 10 15 4 6 3 6 3 4z" />
</svg>
Filter
</button>
</div>
<section id="filters_bar" class="hide-completely">
<section id="filters_bar" class="hidden">
<div id="selected_filters_container"></div>
<button class="button" onclick="resetFilter()">
<svg class="icon button__icon button__icon--left" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-11.414L9.172 7.757 7.757 9.172 10.586 12l-2.829 2.828 1.415 1.415L12 13.414l2.828 2.829 1.415-1.415L13.414 12l2.829-2.828-1.415-1.415L12 10.586z"/></svg>
<svg class="icon button__icon button__icon--left" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-11.414L9.172 7.757 7.757 9.172 10.586 12l-2.829 2.828 1.415 1.415L12 13.414l2.828 2.829 1.415-1.415L13.414 12l2.829-2.828-1.415-1.415L12 10.586z" />
</svg>
Clear all
</button>
</section>
<section id="search_result" class="torrent-container"></section>
<strip-select id="search_page_selector"></strip-select>
<sm-chips id="search_page_selector"></sm-chips>
</section>
<section id="browse" class="hide-completely page page-layout">
<section id="browse" class="hidden page page-layout">
<section class="flex direction-column">
<h1 class="page__title">Browse</h1>
<strip-select id="browse_category_selector">
<strip-option value="movie" selected>Movie</strip-option>
<!-- <strip-option value="tv series">TV series</strip-option>
<strip-option value="video">Video</strip-option>
<strip-option value="music">Music</strip-option>
<strip-option value="software">Software</strip-option>
<strip-option value="game">Game</strip-option>
<strip-option value="image">Image</strip-option>
<strip-option value="audio">Audio</strip-option>
<strip-option value="misc">Misc</strip-option> -->
</strip-select>
<sm-chips id="browse_category_selector">
<sm-chip value="movie" selected>Movie</sm-chip>
</sm-chips>
</section>
<section id="browser_category_torrents" class="torrent-container"></section>
<strip-select id="page_selector"></strip-select>
<sm-chips id="page_selector"></sm-chips>
</section>
<section id="how_it_works" class="page hide-completely page-layout">
<section id="how_it_works" class="page hidden page-layout">
<h1 class="page__title">How it works?</h1>
<p>FLO Torrent stores torrent files directly on the FLO blockchain making it completely decentralized. But this approach came with some challenges.</p>
<p>FLO Torrent stores torrent files directly on the FLO blockchain making it completely decentralized. But
this approach came with some challenges.</p>
<section class="info-section">
<object class="info__image" data="assets/torrent-vs-flo-tx.svg" type="image/svg+xml"></object>
<div class="textual-info">
<h2 class="info__title h2">The problem</h2>
<p>
The maximum number of free characters that can be stored in a FLO blockchain transaction is 1040.
The maximum number of free characters that can be stored in a FLO blockchain transaction is
1040.
But torrents are typically 20,000 characters each.
</p>
<p>
So, we need to first design a way to split the torrent into smaller segments and find a way to link them inside the blockchain.
So, we need to first design a way to split the torrent into smaller segments and find a way to
link them inside the blockchain.
</p>
</div>
@ -215,7 +214,8 @@
<div class="textual-info">
<h2 class="info__title h2">The solution</h2>
<p>
Splitting can easily be achieved by reading only 900 characters from the torrent file one at a time and using the remaining characters for linking purposes.
Splitting can easily be achieved by reading only 900 characters from the torrent file one at a
time and using the remaining characters for linking purposes.
</p>
</div>
</section>
@ -231,28 +231,31 @@
This process continues until all segments are put on the blockchain.
</p>
</div>
</section>
</section>
<section class="info-section">
<object class="info__image" data="assets/chunk-linking.svg" type="image/svg+xml"></object>
<div class="textual-info">
<h2 class="info__title h2">Linking the chunks</h2>
<p>
The transaction ID of the last segment is the entry point to the full data stream and is published as a torrent ID.
The transaction ID of the last segment is the entry point to the full data stream and is
published as a torrent ID.
</p>
</div>
</section>
</section>
<section class="info-section">
<object class="info__image" data="assets/global.svg" type="image/svg+xml"></object>
<div class="textual-info">
<h2 class="info__title h2">Discoverability</h2>
<p>
Transactions from a global FLO Address will list all Torrent IDs, and other details like the name of the torrent, description, etc.
Transactions from a global FLO Address will list all Torrent IDs, and other details like the
name of the torrent, description, etc.
</p>
<p>
This global FLO Address will be a trusted address and will list only trusted and good quality torrents.
This global FLO Address will be a trusted address and will list only trusted and good quality
torrents.
</p>
</div>
</section>
</section>
<section class="info-section">
<object class="info__image" data="assets/client-side.svg" type="image/svg+xml"></object>
<div class="textual-info">
@ -261,43 +264,50 @@
FLO torrent will first read the global FLO Address, find all the torrent details and, list it.
</p>
<p>
When you select a torrent to download. Its entry transaction ID is retrieved, and the last segment of the torrent file is downloaded. The previous transaction ID is also retrieved, and data in that ID is downloaded.
When you select a torrent to download. Its entry transaction ID is retrieved, and the last
segment of the torrent file is downloaded. The previous transaction ID is also retrieved, and
data in that ID is downloaded.
</p>
<p>
Finally, all segments are downloaded until we reach the first segment which has no further linkages.
Finally, all segments are downloaded until we reach the first segment which has no further
linkages.
</p>
</div>
</section>
</section>
<section class="info-section">
<object class="info__image" data="assets/torrent-assembly.svg" type="image/svg+xml"></object>
<div class="textual-info">
<h2 class="info__title h2">Putting everything back</h2>
<p>
Thus, all segments of the torrent can be downloaded from the blockchain.
Thus, all segments of the torrent can be downloaded from the blockchain.
</p>
<p>
Now, all the browser has to do is reassemble them in the correct order, and we have our torrent file.
Now, all the browser has to do is reassemble them in the correct order, and we have our torrent
file.
</p>
</div>
</section>
</section>
<section class="info-section">
<object class="info__image" data="assets/missing-piece.svg" type="image/svg+xml"></object>
<div class="textual-info">
<h2 class="info__title h2">But why?</h2>
<p>
This solution decentralizes the storage of Torrent files which was the last missing piece in the decentralization of the torrent ecosystem. After this the entire chain of the torrent ecosystem is decentralized.
This solution decentralizes the storage of Torrent files which was the last missing piece in the
decentralization of the torrent ecosystem. After this the entire chain of the torrent ecosystem
is decentralized.
</p>
</div>
</section>
</section>
<section class="info-section">
<object class="info__image" data="assets/context-menu.svg" type="image/svg+xml"></object>
<div class="textual-info">
<h2 class="info__title h2">What if we get blocked?</h2>
<p>
No worries, this app is completely contained within a single HTML file so even if the site URL is blocked if you saved this webpage as an HTML file it will work seamlessly.
No worries, this app is completely contained within a single HTML file so even if the site URL
is blocked if you saved this webpage as an HTML file it will work seamlessly.
</p>
</div>
</section>
</section>
</section>
</main>
<footer id="main_footer" class="page-layout">
@ -671,11 +681,11 @@
`
})
if (fromTorrentPage) {
getRef('torrent_download_button').classList.add('hide-completely')
getRef('torrent_download_button').classList.add('hidden')
getRef('loader_container').append(progressIndicator)
}
else {
getRef(torrentId).querySelector('.torrent-card__download-button').classList.add('hide-completely')
getRef(torrentId).querySelector('.torrent-card__download-button').classList.add('hidden')
getRef(torrentId).append(progressIndicator)
}
getTorrentMetafromAPI(txid, 1, totalChunks, torrentId, fromTorrentPage).then(chunks => {
@ -685,10 +695,10 @@
setTimeout(() => {
progressIndicator.remove()
if (fromTorrentPage) {
getRef('torrent_download_button').classList.remove('hide-completely')
getRef('torrent_download_button').classList.remove('hidden')
}
else {
getRef(torrentId).querySelector('.torrent-card__download-button').classList.remove('hide-completely')
getRef(torrentId).querySelector('.torrent-card__download-button').classList.remove('hidden')
}
}, 300);
}).catch(error => {
@ -726,6 +736,21 @@
</script>
<script id="default_ui_library">
const domRefs = {};
const uiGlobals = {
connectionErrorNotifications: []
}
//Checks for internet connection status
if (!navigator.onLine)
uiGlobals.connectionErrorNotifications.push(notify('There seems to be a problem connecting to the internet, Please check you internet connection.', 'error'))
window.addEventListener('offline', () => {
uiGlobals.connectionErrorNotifications.push(notify('There seems to be a problem connecting to the internet, Please check you internet connection.', 'error'))
})
window.addEventListener('online', () => {
uiGlobals.connectionErrorNotifications.forEach(notificationId => getRef('notification_drawer').remove(notificationId))
uiGlobals.connectionErrorNotifications = []
notify('We are back online.', 'success')
})
function getRef(elementId) {
if (!domRefs.hasOwnProperty(elementId)) {
@ -757,69 +782,6 @@
return elem
}
//Checks for internet connection status
if (!navigator.onLine)
notify(
"There seems to be a problem connecting to the internet, Please check you internet connection.",
"error",
"",
true
);
window.addEventListener("offline", () => {
notify(
"There seems to be a problem connecting to the internet, Please check you internet connection.",
"error",
true,
true
);
});
window.addEventListener("online", () => {
getRef("notification_drawer").clearAll();
notify("We are back online.", "success");
});
if (getRef("theme_switcher")) {
if (localStorage.theme === "dark") {
nightlight();
getRef("theme_switcher").checked = true;
} else if (localStorage.theme === "light") {
daylight();
getRef("theme_switcher").checked = false;
}
else {
if (window.matchMedia(`(prefers-color-scheme: dark)`).matches) {
nightlight();
getRef("theme_switcher").checked = true;
} else {
daylight();
getRef("theme_switcher").checked = false;
}
}
function daylight() {
document.body.setAttribute("data-theme", "light");
}
function nightlight() {
document.body.setAttribute("data-theme", "dark");
}
getRef("theme_switcher").addEventListener("change", function (e) {
if (this.checked) {
nightlight();
localStorage.setItem("theme", "dark");
} else {
daylight();
localStorage.setItem("theme", "light");
}
});
}
function setAttributes(el, attrs) {
for (key in attrs) {
el.setAttribute(key, attrs[key]);
}
}
function randomHsl(saturation = 80, lightness = 80) {
let hue = Math.random() * 360;
let color = {
@ -851,13 +813,20 @@
}
//Function for displaying toast notifications. pass in error for mode param if you want to show an error.
function notify(message, mode, pinned, sound) {
if (mode === "error") console.error(message);
else console.log(message);
getRef("notification_drawer").push(message, mode, pinned);
if (navigator.onLine && sound) {
getRef("notification_sound").currentTime = 0;
getRef("notification_sound").play();
function notify(message, mode, options = {}) {
let icon
switch (mode) {
case 'success':
icon = `<svg class="icon icon--success" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M10 15.172l9.192-9.193 1.415 1.414L10 18l-6.364-6.364 1.414-1.414z"/></svg>`
break;
case 'error':
icon = `<svg class="icon icon--error" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-1-7v2h2v-2h-2zm0-8v6h2V7h-2z"/></svg>`
options.pinned = true
break;
}
getRef("notification_drawer").push(message, { icon, ...options });
if (mode === 'error') {
console.error(message)
}
}
@ -894,18 +863,10 @@
window.addEventListener("load", () => {
document.addEventListener("keyup", (e) => {
/* if (e.code === "Escape") {
if (isSiteMapOpen) {
hideSiteMap();
}
else if (isRoomOpen) {
hideRoom()
}
} */
});
document.addEventListener("pointerdown", (e) => {
if (e.target.closest("button, sm-button:not([disable]), .interact")) {
createRipple(e, e.target.closest("button, sm-button, .interact"));
if (e.target.closest("button, .interact")) {
createRipple(e, e.target.closest("button, .interact"));
}
});
});
@ -937,39 +898,17 @@
};
}
function debounce(func, wait, immediate) {
let timeout;
return function () {
let context = this,
args = arguments;
let later = function () {
timeout = null;
if (!immediate) func.apply(context, args);
};
let callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
// Use when a function needs to be executed after user finishes changes
const debounce = (callback, wait) => {
let timeoutId = null;
return (...args) => {
window.clearTimeout(timeoutId);
timeoutId = window.setTimeout(() => {
callback.apply(null, args);
}, wait);
};
}
let timerId;
function throttle(func, delay) {
// If setTimeout is already scheduled, no need to do anything
if (timerId) {
return;
}
// Schedule a setTimeout after delay seconds
timerId = setTimeout(function () {
func();
// Once setTimeout function execution is finished, timerId = undefined so that in
// the next scroll event function execution can be scheduled by the setTimeout
timerId = undefined;
}, delay);
}
</script>
<script>
const render = {
@ -977,14 +916,14 @@
const { id, name, description, tags, type = 'misc', size, uploader, startTx, filename, chunks } = obj
const card = getRef('torrent_card_template').content.cloneNode(true).firstElementChild
card.id = id
card.querySelector('.torrent-info').href = `?id=${id}#torrent`
card.querySelector('.torrent-info').href = `#/torrent/${id}`
card.querySelector('.torrent-card__title').textContent = name
card.querySelector('.torrent-card__uploader').textContent = `by ${uploader}`
card.querySelector('.torrent-card__icon').innerHTML = getIcon(type)
return card
},
filterOption(obj){
const {content, groupName, value} = obj
filterOption(obj) {
const { content, groupName, value } = obj
const option = create('label', {
className: 'filter-option interact'
})
@ -995,8 +934,8 @@
`
return option
},
selectedFilter(obj){
const {content, type} = obj
selectedFilter(obj) {
const { content, type } = obj
const option = create('div', {
className: 'selected-filter interact'
})
@ -1051,17 +990,6 @@
})
return torrentsFrag
}
function getSearchParams() {
const urlSearchParams = new URLSearchParams(window.location.search);
const params = Object.fromEntries(urlSearchParams.entries());
return params
}
function pushParams() {
history.pushState(null, null, `?query=${filteredSearch.query}#search`)
}
let filteredSearch = {
active: false,
query: '',
@ -1077,17 +1005,17 @@
filteredSearch['query'] = searchKey
let result
if (filteredSearch.isActive) {
if(filteredSearch.category && filteredSearch.tags.size){
if (filteredSearch.category && filteredSearch.tags.size) {
result = await getFilteredTorrents(['type'], filteredSearch.category)
result = await getFilteredTorrents(['tags'], [...filteredSearch.tags].join('/'), {torrents: result})
result = await getFilteredTorrents(['tags'], [...filteredSearch.tags].join('/'), { torrents: result })
}
else if(filteredSearch.category){
else if (filteredSearch.category) {
result = await getFilteredTorrents(['type'], filteredSearch.category)
}
else if(filteredSearch.tags.size){
else if (filteredSearch.tags.size) {
result = await getFilteredTorrents(['tags'], [...filteredSearch.tags].join('/'))
}
result = await getFilteredTorrents(['name', 'filename', 'tags'], searchKey, {torrents: result})
result = await getFilteredTorrents(['name', 'filename', 'tags'], searchKey, { torrents: result })
}
else {
result = await getFilteredTorrents(['name', 'filename', 'tags'], searchKey)
@ -1101,12 +1029,12 @@
getRef('search_result').innerHTML = ``
getRef('search_page_selector').innerHTML = ``
if(result.length > 20){
if (result.length > 20) {
const pages = Math.round(result.length / 20)
getRef('search_page_selector').append(createPageButtons(pages))
getRef('search_result').append(renderTorrents(result.slice(0, 20)))
}
else{
else {
getRef('search_result').append(renderTorrents(result))
}
@ -1114,10 +1042,10 @@
}
const categories = ['movie', 'tv series', 'video', 'music', 'software', 'game', 'image', 'audio', 'misc']
const allTags = ['action', 'adventure', ,'anime', 'comedy', 'crime', 'drama', 'family', 'fantacy', 'horror', 'korean', 'mystery', 'romance', 'sci-fi', 'sport', 'thriller', 'western']
const allTags = ['action', 'adventure', , 'anime', 'comedy', 'crime', 'drama', 'family', 'fantacy', 'horror', 'korean', 'mystery', 'romance', 'sci-fi', 'sport', 'thriller', 'western']
function renderOptions(list, options = {}){
const {groupName} = options
function renderOptions(list, options = {}) {
const { groupName } = options
const frag = document.createDocumentFragment()
list.forEach(item => {
const optionObj = {
@ -1130,25 +1058,61 @@
return frag
}
let currentpage = ''
const appState = {
params: {},
openedPages: new Set(),
}
const generalPages = ['sign_up', 'sign_in', 'loading', 'landing']
async function routeTo(targetPage, options = {}) {
const { firstLoad, hashChange } = options
let pageId
let params = {}
let searchParams
if (targetPage === '') {
pageId = 'home'
} else {
if (targetPage.includes('/')) {
if (targetPage.includes('?')) {
const splitAddress = targetPage.split('?')
searchParams = splitAddress.pop()
const pages = splitAddress.pop().split('/')
pageId = pages[1]
subPageId = pages[2]
} else {
const pages = targetPage.split('/')
pageId = pages[1]
subPageId = pages[2]
}
} else {
pageId = targetPage
}
}
appState.currentPage = pageId
async function showPage(target) {
let params = getSearchParams()
let page = target.includes('#') ? target.split('#')[1] : target
switch (page) {
case 'loading_page':
break;
if (searchParams) {
const urlSearchParams = new URLSearchParams('?' + searchParams);
params = Object.fromEntries(urlSearchParams.entries());
}
switch (pageId) {
case 'home':
history.replaceState(null, null, '#/home')
let allTorrents = await getDataFromIDB()
allTorrents = allTorrents.reverse().slice(0, 8)
getRef('torrent_container').innerHTML = ``
getRef('torrent_container').append(renderTorrents(allTorrents))
break;
case 'search':
if(currentpage !== page){
if (appState.lastPage !== pageId) {
getRef('category_selector').innerHTML = ''
getRef('category_selector').append(renderOptions(categories, {groupName: 'category-selector'}))
getRef('category_selector').append(renderOptions(categories, { groupName: 'category-selector' }))
getRef('tags_selector').innerHTML = ''
getRef('tags_selector').append(renderOptions(allTags))
}
renderSearchResult(params.query)
if (subPageId)
renderSearchResult(subPageId)
break;
case 'torrent':
currentTorrent = await getDataFromIDB(parseInt(params.id))
currentTorrent = await getDataFromIDB(parseInt(subPageId))
const { id, name, description, tags, type = 'misc', size, uploader, startTx, filename, chunks } = currentTorrent
getRef('torrent_type_icon').innerHTML = getIcon(type)
getRef('torrent_name').textContent = name
@ -1161,21 +1125,38 @@
const category = getRef('browse_category_selector').value
showCategoryTorrents(category)
break;
case 'how_it_works':
break
default:
page = 'homepage'
history.replaceState(null, null, ' ')
let allTorrents = await getDataFromIDB()
allTorrents = allTorrents.reverse().slice(0, 8)
getRef('torrent_container').innerHTML = ``
getRef('torrent_container').append(renderTorrents(allTorrents))
}
if(currentpage !== page){
document.querySelector('.page:not(.hide-completely)')?.classList.add('hide-completely')
getRef(page).classList.remove('hide-completely')
currentpage = page
if (appState.lastPage !== pageId) {
const animOptions = {
duration: 100,
fill: 'forwards',
easing: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)'
}
document.querySelectorAll('.page').forEach(page => page.classList.add('hidden'))
getRef(pageId).closest('.page').classList.remove('hidden')
// getRef('main_card').style.overflowY = "hidden";
getRef(pageId).animate([
{
opacity: 0,
transform: 'translateY(1rem)'
},
{
opacity: 1,
transform: 'translateY(0)'
},
],
{
duration: 300,
easing: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)'
}).onfinish = () => {
// getRef('main_card').style.overflowY = "";
}
appState.lastPage = pageId
}
if (params)
appState.params = params
appState.openedPages.add(pageId)
}
function alphaIncSort(arr, prop) {
@ -1185,9 +1166,9 @@
return arr.sort((a, b) => b[prop].localeCompare(a[prop]))
}
async function getFilteredTorrents(keys, searchKey, options = {}){
let {limit, torrents} = options
if(!torrents){
async function getFilteredTorrents(keys, searchKey, options = {}) {
let { limit, torrents } = options
if (!torrents) {
torrents = await getDataFromIDB()
}
const config = {
@ -1195,21 +1176,21 @@
threshold: 0.2,
}
const fuseSearch = new Fuse(torrents, config)
return fuseSearch.search(searchKey, {limit}).map(elem => elem.item)
return fuseSearch.search(searchKey, { limit }).map(elem => elem.item)
}
function createPageButtons(pages){
function createPageButtons(pages) {
const paginationFrag = document.createDocumentFragment()
for (let i = 0; i < pages; i++) {
const pageButton = create('strip-option', {
textContent: (i + 1)
})
pageButton.setAttribute('value', i)
if (i === 0) {
pageButton.setAttribute('selected', '')
}
paginationFrag.append(pageButton)
const pageButton = create('sm-chip', {
textContent: (i + 1)
})
pageButton.setAttribute('value', i)
if (i === 0) {
pageButton.setAttribute('selected', '')
}
paginationFrag.append(pageButton)
}
return paginationFrag
}
@ -1230,11 +1211,11 @@
}
async function renderSearchSuggestions(searchKey, advance = false) {
const result = await getFilteredTorrents(['name', 'filename'], searchKey, {limit: 6})
if(advance){
const result = await getFilteredTorrents(['name', 'filename'], searchKey, { limit: 6 })
if (advance) {
getRef('advance_search_suggestions').innerHTML = ``
}
else{
else {
getRef('search_suggestions').innerHTML = ``
}
if (result.length) {
@ -1248,28 +1229,25 @@
innerHTML: elem.name.replace(regEx, `<span>$&</span>`),
})
suggestion.append(pre)
suggestion.href = `?id=${elem.id}#torrent`
suggestion.href = `#/torrent/${elem.id}`
suggestionsFrag.append(suggestion)
})
if(advance){
if (advance) {
getRef('advance_search_suggestions').append(suggestionsFrag)
}
else{
else {
getRef('search_suggestions').append(suggestionsFrag)
}
}
}
function handleEnter(e){
function handleEnter(e) {
if (e.target.value.trim() !== '') {
switch (e.key) {
case 'Enter':
const searchKey = e.target.value.trim()
renderSearchResult(searchKey)
pushParams()
if(e.target.id === 'search_torrent'){
showPage('search')
}
location.hash = `#/search/${filteredSearch.query}`;
e.target.value = ''
break;
case 'ArrowDown':
@ -1279,8 +1257,8 @@
}
}
}
function handleKeyboardNav(e){
function handleKeyboardNav(e) {
switch (e.key) {
case 'ArrowUp':
e.preventDefault()
@ -1300,19 +1278,16 @@
// Event listeners
window.addEventListener('load', e => {
showPage('loading_page')
routeTo('loading_page')
getTorrents().then(allTorrents => {
showPage(window.location.hash)
})
.catch(err => {
console.log(err)
routeTo(window.location.hash)
})
.catch(err => {
console.log(err)
})
})
window.addEventListener('hashchange', e => {
showPage(window.location.hash)
})
window.addEventListener('popstate', e => {
showPage(window.location.hash)
routeTo(window.location.hash)
})
let currentTorrent
@ -1329,19 +1304,15 @@
getRef('advance_torrent_search').addEventListener('keydown', handleEnter)
getRef('search_suggestions').addEventListener('keydown', handleKeyboardNav)
getRef('advance_search_suggestions').addEventListener('keydown', handleKeyboardNav)
getRef('search_torrent').addEventListener('input', async e => {
throttle(() => {
const searchKey = getRef('search_torrent').value.trim()
renderSearchSuggestions(searchKey)
}, 100)
})
getRef('advance_torrent_search').addEventListener('input', async e => {
throttle(() => {
const searchKey = getRef('advance_torrent_search').value.trim()
renderSearchSuggestions(searchKey, true)
}, 100)
})
getRef('search_torrent').addEventListener('input', debounce((e) => {
const searchKey = e.target.value.trim()
renderSearchSuggestions(searchKey)
}, 100))
getRef('advance_torrent_search').addEventListener('input', debounce((e) => {
const searchKey = getRef('advance_torrent_search').value.trim()
renderSearchSuggestions(searchKey, true)
}, 100))
getRef('browse_category_selector').addEventListener('change', e => {
showCategoryTorrents(e.detail.value)
})
@ -1369,32 +1340,32 @@
getRef('search_result').append(renderTorrents(result.slice(startIndex, endIndex)))
}, 200);
})
function addFilter(){
function addFilter() {
const selectedCategory = getRef('category_selector').querySelector('input:checked')
if(selectedCategory){
if (selectedCategory) {
filteredSearch.category = selectedCategory.value
}
filteredSearch['tags'].clear()
getRef('tags_selector').querySelectorAll('input:checked').forEach(tag => {
filteredSearch['tags'].add(tag.value)
})
if(filteredSearch.category || filteredSearch.tags.size){
if (filteredSearch.category || filteredSearch.tags.size) {
filteredSearch.isActive = true
getRef('filters_bar').classList.remove('hide-completely')
getRef('filters_bar').classList.remove('hidden')
}
else{
getRef('filters_bar').classList.add('hide-completely')
else {
getRef('filters_bar').classList.add('hidden')
}
renderSelectedFilters()
renderSearchResult('', true);
getRef('filter_popup').hide()
}
function resetFilter(){
function resetFilter() {
filteredSearch.isActive = false
filteredSearch.category = undefined
getRef('filter_form').reset()
const selectedCategory = getRef('category_selector').querySelector('input:checked')
if(selectedCategory){
if (selectedCategory) {
selectedCategory.checked = false
}
filteredSearch['tags'].clear()
@ -1402,35 +1373,35 @@
tag.checked = false
})
getRef('selected_filters_container').innerHTML = ''
getRef('filters_bar').classList.add('hide-completely')
getRef('filters_bar').classList.add('hidden')
renderSearchResult('', true);
}
function renderSelectedFilters(){
function renderSelectedFilters() {
getRef('selected_filters_container').innerHTML = ''
const frag = document.createDocumentFragment()
if(filteredSearch.category){
frag.append(render.selectedFilter({content: filteredSearch.category, type: 'category'}))
if (filteredSearch.category) {
frag.append(render.selectedFilter({ content: filteredSearch.category, type: 'category' }))
}
filteredSearch.tags.forEach(tag => {
frag.append(render.selectedFilter({content: tag, type: 'tag'}))
frag.append(render.selectedFilter({ content: tag, type: 'tag' }))
})
getRef('selected_filters_container').append(frag)
}
getRef('selected_filters_container').addEventListener('click', e => {
if(e.target.closest('.selected-filter')){
if (e.target.closest('.selected-filter')) {
const target = e.target.closest('.selected-filter')
if(target.dataset.type === 'category'){
if (target.dataset.type === 'category') {
filteredSearch.category = undefined
getRef('category_selector').querySelector('input:checked').checked = false
}
else{
else {
filteredSearch.tags.delete(target.dataset.value)
getRef('tags_selector').querySelector(`input[value="${target.dataset.value}"]`).checked = false
}
target.remove()
if(!filteredSearch.category && !filteredSearch.tags.size){
if (!filteredSearch.category && !filteredSearch.tags.size) {
filteredSearch.isActive = false
getRef('filters_bar').classList.add('hide-completely')
getRef('filters_bar').classList.add('hidden')
}
}
renderSearchResult('', true);
@ -1438,4 +1409,4 @@
</script>
</body>
</html>
</html>

View File

@ -15,48 +15,39 @@
<body>
<sm-notifications id="notification_drawer"></sm-notifications>
<audio id="notification_sound">
<source src="https://rmservices.duckdns.org/files/notification-sound.mp3" type="audio/mpeg">
<source src="https://rmservices.duckdns.org/files/notification-sound.ogg" type="audio/ogg">
</audio>
<sm-popup id="confirmation_popup">
<h4 id="confirm_title"></h4>
<p id="confirm_message"></p>
<div class="flex align-center">
<sm-button variant="no-outline" class="cancel-btn">Cancel</sm-button>
<sm-button variant="no-outline" class="submit-btn">OK</button>
<div class="flex align-center gap-0-5 margin-left-auto">
<button class="button cancel-button">Cancel</button>
<button class="button button--primary confirm-button">OK</button>
</div>
</sm-popup>
<sm-popup id="prompt_popup">
<h4 id="prompt_title"></h4>
<p id="prompt_message"></p>
<sm-input id="prompt_input"></sm-input>
<div class="flex align-center">
<sm-button variant="no-outline" class="cancel-btn">Cancel</sm-button>
<sm-button variant="no-outline" class="submit-btn" type="submit">OK</button>
</div>
<sm-form>
<sm-input id="prompt_input"></sm-input>
<div class="flex align-center gap-0-5 margin-left-auto">
<button class="button cancel-button">Cancel</button>
<button class="button confirm-button button--primary" type="submit">OK</button>
</div>
</sm-form>
</sm-popup>
<sm-popup id="show_progress_popup">
<div id="overlay_content"></div>
</sm-popup>
<header id="main_header">
<a href="./index.html" class="flex align-center" title="FLO Torrent">
<svg id="main_header__logo" class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><path d="M54.69,14.28l8.83-8.83a1.62,1.62,0,0,0-1.14-2.77H21.15A1.63,1.63,0,0,0,19.53,4.3V59.7a1.62,1.62,0,0,0,2.78,1.13l8.83-9a1.62,1.62,0,0,0,.46-1.14V38.06a1.58,1.58,0,0,1,.48-1.14l8.81-8.83a1.62,1.62,0,0,0-1.15-2.77H33.22A1.62,1.62,0,0,1,31.6,23.7V16.37a1.63,1.63,0,0,1,1.62-1.62H53.55A1.63,1.63,0,0,0,54.69,14.28Z"/><path d="M1.62,14.75H12.36A1.62,1.62,0,0,0,14,13.13V4.3a1.63,1.63,0,0,0-1.62-1.62H7.47a1.6,1.6,0,0,0-1.35.73L.27,12.24A1.62,1.62,0,0,0,1.62,14.75Z"/></svg>
<svg id="main_header__logo" class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<path
d="M54.69,14.28l8.83-8.83a1.62,1.62,0,0,0-1.14-2.77H21.15A1.63,1.63,0,0,0,19.53,4.3V59.7a1.62,1.62,0,0,0,2.78,1.13l8.83-9a1.62,1.62,0,0,0,.46-1.14V38.06a1.58,1.58,0,0,1,.48-1.14l8.81-8.83a1.62,1.62,0,0,0-1.15-2.77H33.22A1.62,1.62,0,0,1,31.6,23.7V16.37a1.63,1.63,0,0,1,1.62-1.62H53.55A1.63,1.63,0,0,0,54.69,14.28Z" />
<path
d="M1.62,14.75H12.36A1.62,1.62,0,0,0,14,13.13V4.3a1.63,1.63,0,0,0-1.62-1.62H7.47a1.6,1.6,0,0,0-1.35.73L.27,12.24A1.62,1.62,0,0,0,1.62,14.75Z" />
</svg>
<!-- <a href="./index.html" class="header__company-name">FLO torrent uploader</a> -->
</a>
<label class="theme-switcher" title="Change theme">
<input id="theme_switcher" class="theme-switcher__checkbox" type="checkbox">
<svg class="icon moon-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M10 6a8 8 0 0 0 11.955 6.956C21.474 18.03 17.2 22 12 22 6.477 22 2 17.523 2 12c0-5.2 3.97-9.474 9.044-9.955A7.963 7.963 0 0 0 10 6zm-6 6a8 8 0 0 0 8 8 8.006 8.006 0 0 0 6.957-4.045c-.316.03-.636.045-.957.045-5.523 0-10-4.477-10-10 0-.321.015-.64.045-.957A8.006 8.006 0 0 0 4 12zm14.164-9.709L19 2.5v1l-.836.209a2 2 0 0 0-1.455 1.455L16.5 6h-1l-.209-.836a2 2 0 0 0-1.455-1.455L13 3.5v-1l.836-.209A2 2 0 0 0 15.29.836L15.5 0h1l.209.836a2 2 0 0 0 1.455 1.455zm5 5L24 7.5v1l-.836.209a2 2 0 0 0-1.455 1.455L21.5 11h-1l-.209-.836a2 2 0 0 0-1.455-1.455L18 8.5v-1l.836-.209a2 2 0 0 0 1.455-1.455L20.5 5h1l.209.836a2 2 0 0 0 1.455 1.455z" />
</svg>
<svg class="icon sun-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M12 18a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-2a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM11 1h2v3h-2V1zm0 19h2v3h-2v-3zM3.515 4.929l1.414-1.414L7.05 5.636 5.636 7.05 3.515 4.93zM16.95 18.364l1.414-1.414 2.121 2.121-1.414 1.414-2.121-2.121zm2.121-14.85l1.414 1.415-2.121 2.121-1.414-1.414 2.121-2.121zM5.636 16.95l1.414 1.414-2.121 2.121-1.414-1.414 2.121-2.121zM23 11v2h-3v-2h3zM4 11v2H1v-2h3z" />
</svg>
</label>
<theme-toggle></theme-toggle>
</header>
<section class="torrent-upload-section">
<sm-form id="torrent_form">
@ -66,7 +57,12 @@
<sm-input id="floID" placeholder="FLO ID" data-flo-id required></sm-input>
<sm-input id="name" placeholder="Torrent Name" required></sm-input>
<file-input id="torrentFile" required>
<svg class="icon button__icon--left" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M9 2.003V2h10.998C20.55 2 21 2.455 21 2.992v18.016a.993.993 0 0 1-.993.992H3.993A1 1 0 0 1 3 20.993V8l6-5.997zM5.83 8H9V4.83L5.83 8zM11 4v5a1 1 0 0 1-1 1H5v10h14V4h-8z"/></svg>
<svg class="icon button__icon--left" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M9 2.003V2h10.998C20.55 2 21 2.455 21 2.992v18.016a.993.993 0 0 1-.993.992H3.993A1 1 0 0 1 3 20.993V8l6-5.997zM5.83 8H9V4.83L5.83 8zM11 4v5a1 1 0 0 1-1 1H5v10h14V4h-8z" />
</svg>
Select torrent
</file-input>
</section>
@ -89,22 +85,33 @@
</div>
<tags-input id="tags" placeholder="Tags" limit="10"></tags-input>
</section>
<sm-button id="upload_torrent_button" variant="primary" disabled>
<svg class="icon button__icon--left" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M15 4H5v16h14V8h-4V4zM3 2.992C3 2.444 3.447 2 3.999 2H16l5 5v13.993A1 1 0 0 1 20.007 22H3.993A1 1 0 0 1 3 21.008V2.992zM13 12v4h-2v-4H8l4-4 4 4h-3z"/></svg>
<button id="upload_torrent_button" class="button button--primary" type="submit" disabled>
<svg class="icon button__icon--left" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24"
height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M15 4H5v16h14V8h-4V4zM3 2.992C3 2.444 3.447 2 3.999 2H16l5 5v13.993A1 1 0 0 1 20.007 22H3.993A1 1 0 0 1 3 21.008V2.992zM13 12v4h-2v-4H8l4-4 4 4h-3z" />
</svg>
Upload
</sm-button>
</button>
</sm-form>
<section class="grid gap-1">
<section class="grid gap-1 hidden">
<details>
<summary>More options</summary>
<sm-button onclick="refreshUTXOs()">Refresh UTXOs</sm-button>
<button class="button" onclick="refreshUTXOs()">Refresh UTXOs</button>
<div class="flex align-center">
<button onclick="decRefreshFee()" id="-">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M5 11h14v2H5z"/></svg>
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path d="M5 11h14v2H5z" />
</svg>
</button>
<span id="refreshFee"></span>
<button onclick="incRefreshFee()" id="+">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M11 11V5h2v6h6v2h-6v6h-2v-6H5v-2z"/></svg>
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path d="M11 11V5h2v6h6v2h-6v6h-2v-6H5v-2z" />
</svg>
</button>
</div>
</details>
@ -132,6 +139,21 @@
<script src="components.js"></script>
<script id="default_ui_library">
const domRefs = {};
const uiGlobals = {
connectionErrorNotifications: [],
}
//Checks for internet connection status
if (!navigator.onLine)
uiGlobals.connectionErrorNotifications.push(notify('There seems to be a problem connecting to the internet, Please check you internet connection.', 'error'))
window.addEventListener('offline', () => {
uiGlobals.connectionErrorNotifications.push(notify('There seems to be a problem connecting to the internet, Please check you internet connection.', 'error'))
})
window.addEventListener('online', () => {
uiGlobals.connectionErrorNotifications.forEach(notificationId => getRef('notification_drawer').remove(notificationId))
uiGlobals.connectionErrorNotifications = []
notify('We are back online.', 'success')
})
function getRef(elementId) {
if (!domRefs.hasOwnProperty(elementId)) {
@ -151,179 +173,87 @@
}
}
}
function create(tagName, obj) {
const { className, textContent, innerHTML } = obj
const elem = document.createElement(tagName)
if (className)
elem.className = className
elem.textContent = textContent
if (innerHTML)
elem.innerHTML = innerHTML
return elem
}
// Use when a function needs to be executed after user finishes changes
const debounce = (callback, wait) => {
let timeoutId = null;
return (...args) => {
window.clearTimeout(timeoutId);
timeoutId = window.setTimeout(() => {
callback.apply(null, args);
}, wait);
};
}
let timerId;
function throttle(func, delay) {
// If setTimeout is already scheduled, no need to do anything
if (timerId) {
return;
}
// Schedule a setTimeout after delay seconds
timerId = setTimeout(function () {
func();
// Once setTimeout function execution is finished, timerId = undefined so that in
// the next scroll event function execution can be scheduled by the setTimeout
timerId = undefined;
}, delay);
let timeoutId = null;
return (...args) => {
window.clearTimeout(timeoutId);
timeoutId = window.setTimeout(() => {
callback.apply(null, args);
}, wait);
};
}
//Checks for internet connection status
if (!navigator.onLine)
notify(
"There seems to be a problem connecting to the internet, Please check you internet connection.",
"error",
"",
true
);
window.addEventListener("offline", () => {
notify(
"There seems to be a problem connecting to the internet, Please check you internet connection.",
"error",
true,
true
);
});
window.addEventListener("online", () => {
getRef("notification_drawer").clearAll();
notify("We are back online.", "success");
});
if (getRef("theme_switcher")) {
if (localStorage.theme === "dark") {
nightlight();
getRef("theme_switcher").checked = true;
} else if (localStorage.theme === "light") {
daylight();
getRef("theme_switcher").checked = false;
}
else {
if (window.matchMedia(`(prefers-color-scheme: dark)`).matches) {
nightlight();
getRef("theme_switcher").checked = true;
} else {
daylight();
getRef("theme_switcher").checked = false;
}
}
function daylight() {
document.body.setAttribute("data-theme", "light");
}
function nightlight() {
document.body.setAttribute("data-theme", "dark");
}
getRef("theme_switcher").addEventListener("change", function (e) {
if (this.checked) {
nightlight();
localStorage.setItem("theme", "dark");
} else {
daylight();
localStorage.setItem("theme", "light");
}
});
}
let zIndex = 50
// function required for popups or modals to appear
class Stack {
constructor() {
this.items = [];
}
push(element) {
this.items.push(element);
}
pop() {
if (this.items.length == 0)
return "Underflow";
return this.items.pop();
}
peek() {
return this.items[this.items.length - 1];
}
}
let popupStack = new Stack(),
zIndex = 10;
async function showPopup(popup, pinned) {
function openPopup(popupId, pinned) {
zIndex++
getRef(popup).setAttribute('style', `z-index: ${zIndex}`)
popupStack = getRef(popup).show(pinned, popupStack)
return getRef(popup);
getRef(popupId).setAttribute('style', `z-index: ${zIndex}`)
return getRef(popupId).show({ pinned })
}
// hides the popup or modal
function hidePopup() {
// hides the popup or modal
function closePopup(options = {}) {
if (popupStack.peek() === undefined)
return;
popupStack.peek().popup.hide()
popupStack.peek().popup.hide(options)
}
// displays a popup for asking permission. Use this instead of JS confirm
let confirmation = (title, message, cancelText = 'Cancel', confirmText = 'OK') => {
const getConfirmation = (title, options = {}) => {
return new Promise(resolve => {
showPopup('confirmation_popup', true)
getRef('confirm_title').textContent = title;
getRef('confirm_message').textContent = message;
let cancelButton = getRef('confirmation_popup').children[2].children[0],
submitButton = getRef('confirmation_popup').children[2].children[1]
submitButton.textContent = confirmText
const { message = '', cancelText = 'Cancel', confirmText = 'OK', danger = false } = options
getRef('confirm_title').innerText = title;
getRef('confirm_message').innerText = message;
const cancelButton = getRef('confirmation_popup').querySelector('.cancel-button');
const confirmButton = getRef('confirmation_popup').querySelector('.confirm-button')
confirmButton.textContent = confirmText
cancelButton.textContent = cancelText
submitButton.onclick = () => {
hidePopup()
resolve(true);
if (danger)
confirmButton.classList.add('button--danger')
else
confirmButton.classList.remove('button--danger')
const { opened, closed } = openPopup('confirmation_popup')
confirmButton.onclick = () => {
closePopup({ payload: true })
}
cancelButton.onclick = () => {
hidePopup()
resolve(false);
closePopup()
}
closed.then((payload) => {
confirmButton.onclick = null
cancelButton.onclick = null
if (payload)
resolve(true)
else
resolve(false)
})
})
}
// displays a popup for asking user input. Use this instead of JS prompt
async function getPromptInput(title, message = '', showText = true, trueBtn = "Ok", falseBtn = "Cancel") {
showPopup('prompt_popup', true)
getRef('prompt_title').textContent = title;
let input = getRef('prompt_input');
input.setAttribute("placeholder", message)
let buttons = getRef('prompt_popup').querySelectorAll("sm-button");
if (showText)
input.setAttribute("type", "text")
else
input.setAttribute("type", "password")
input.focusIn()
buttons[0].textContent = falseBtn;
buttons[1].textContent = trueBtn;
function getPromptInput(title, message = '', options = {}) {
let { placeholder = '', isPassword = false, cancelText = 'Cancel', confirmText = 'OK' } = options
getRef('prompt_title').innerText = title;
getRef('prompt_message').innerText = message;
const cancelButton = getRef('prompt_popup').querySelector('.cancel-button');
const confirmButton = getRef('prompt_popup').querySelector('.confirm-button')
if (isPassword) {
placeholder = 'Password'
getRef('prompt_input').setAttribute("type", "password")
}
getRef('prompt_input').setAttribute("placeholder", placeholder)
getRef('prompt_input').focusIn()
cancelButton.textContent = cancelText;
confirmButton.textContent = confirmText;
openPopup('prompt_popup', true)
return new Promise((resolve, reject) => {
buttons[0].onclick = () => {
hidePopup()
return;
}
buttons[1].onclick = () => {
let value = input.value;
hidePopup()
resolve(value)
}
cancelButton.addEventListener('click', () => {
closePopup()
return null
}, { once: true })
confirmButton.addEventListener('click', () => {
closePopup()
resolve(getRef('prompt_input').value)
}, { once: true })
})
}
@ -369,31 +299,16 @@
}
}
const inputValidation = debounce((e) => {
if (e.target.closest('sm-input')) {
const input = e.target.closest('sm-input')
if (input.value === '')
input.setValidity('')
if (input.hasAttribute('data-flo-id')) {
if (validateAddr(input.value.trim()) || input.value.trim() === '')
input.setValidity('')
else
input.setValidity('Invalid FLO address.')
}
}
}, 100)
window.addEventListener("load", () => {
document.querySelectorAll('sm-input[data-flo-id]').forEach(input => input.customValidation = validateAddr)
document.addEventListener('input', inputValidation)
document.addEventListener('keyup', (e) => {
if (e.code === 'Escape') {
hidePopup()
closePopup()
}
})
document.addEventListener("pointerdown", (e) => {
if (e.target.closest("button, sm-button:not([disabled]), .interact")) {
createRipple(e, e.target.closest("button, sm-button, .interact"));
if (e.target.closest("button, .interact")) {
createRipple(e, e.target.closest("button, .interact"));
}
});
});
@ -455,31 +370,31 @@
}
</script>
<script>
function ajax(method, uri){
var request = new XMLHttpRequest();
var url = `${server}/${uri}`
console.log(url)
var result;
request.open(method,url , false);
request.onload = function () {
if (request.readyState == 4 && request.status == 200)
result = this.response;
else {
console.log('error');
result = false;
}
};
request.send();
console.log(result);
return result;
}
function ajax(method, uri) {
var request = new XMLHttpRequest();
var url = `${server}/${uri}`
console.log(url)
var result;
request.open(method, url, false);
request.onload = function () {
if (request.readyState == 4 && request.status == 200)
result = this.response;
else {
console.log('error');
result = false;
}
};
request.send();
console.log(result);
return result;
}
async function getJsonResult(uri) {
try{
try {
const url = `${server}/${uri}`
const response = await fetch(url)
return await response.json()
}
catch(err){
catch (err) {
console.error(err)
}
}
@ -534,19 +449,17 @@
notify("Selected File is not a torrent file!", 'error');
return;
}
showPopup('show_progress_popup', true)
openPopup('show_progress_popup', true)
getRef("overlay_content").innerHTML = "";
const overlaytext = document.createElement("div");
getRef("overlay_content").appendChild(overlaytext);
overlaytext.innerHTML = `
<svg viewBox="0 0 64 64" class="loader"><circle cx="32" cy="32" r="32" /></svg
<div>Checking Balance</div>`;
overlaytext.innerHTML = ` <svg viewBox="0 0 64 64" class="loader"><circle cx="32" cy="32" r="32" /></svg <div>Checking Balance</div>`;
const balance = await getJsonResult(`api/addr/${floID}/balance`); //get balance from API
if(balance){
if (balance) {
console.log(`Balance : ${balance}`);
}
else{
hidePopup()
else {
closePopup()
return
}
overlaytext.innerHTML = "Loading File...";
@ -563,7 +476,7 @@
var totalFee = splitfee + (chunks.length * fee) + (sendAmt + fee) // splitUTXOs => 1 fee, each chunk to self => (chunk.lenght*fee), one header to admin ID => (sendAmt+fee)
if (totalFee > balance) {
notify(`Your net balance is : ${balance}\nTotal fee for upload : ${totalFee}\nBalance is Insufficient to upload`, 'error');
hidePopup()
closePopup()
return;
}
var header = JSON.stringify({ FLO_Torrent: { name, filename: file.name, type, size: file.size, description, tags, chunks: chunks.length } });
@ -579,11 +492,11 @@
}).catch(function (error) {
notify(error, 'error');
}).finally(function () {
hidePopup()
closePopup()
});
}
else
hidePopup()
closePopup()
};
reader.readAsBinaryString(file);
}
@ -647,31 +560,31 @@
</script>
<script>
function splitUTXOs(sender,num,wif){
function splitUTXOs(sender, num, wif) {
var trx = bitjs.transaction();
var utxoAmt = 0.0;
var response = ajax("GET",`api/addr/${sender}/utxo`);
var response = ajax("GET", `api/addr/${sender}/utxo`);
var utxos = JSON.parse(response);
var splitAmt = sendAmt + fee;
var TotalAmt = splitAmt*(num+1);
for(var x = utxos.length-1; (x >= 0) && (utxoAmt < TotalAmt+splitfee); x--){
if(utxos[x].confirmations){
var TotalAmt = splitAmt * (num + 1);
for (var x = utxos.length - 1; (x >= 0) && (utxoAmt < TotalAmt + splitfee); x--) {
if (utxos[x].confirmations) {
trx.addinput(utxos[x].txid, utxos[x].vout, utxos[x].scriptPubKey)
utxoAmt += utxos[x].amount;
}
}
if(utxoAmt < TotalAmt+splitfee){
if (utxoAmt < TotalAmt + splitfee) {
notify("Insufficient balance!", 'error');
return false;
}
for(var i=0;i<=num;i++)
for (var i = 0; i <= num; i++)
trx.addoutput(sender, splitAmt);
var change = utxoAmt-TotalAmt-splitfee;
if(change>0)
var change = utxoAmt - TotalAmt - splitfee;
if (change > 0)
trx.addoutput(sender, change);
//trx.addflodata(data);
var signedTxHash = trx.sign(wif, 1);
return {scriptPubKey:utxos[0].scriptPubKey ,txid:broadcastTx(signedTxHash)};
return { scriptPubKey: utxos[0].scriptPubKey, txid: broadcastTx(signedTxHash) };
}
function sendTransaction(sender, receiver, utxo, vout, data, wif) {
var trx = bitjs.transaction();
@ -688,7 +601,7 @@
console.log(wif)
if (!verifyWIF(wif, floID)) {
notify("Invalid Private Key!", 'error');
hidePopup()
closePopup()
return;
}
getRef("overlay_content").innerHTML = "";
@ -698,7 +611,7 @@
<span>Refreshing UTXOs</span>
`;
getRef("overlay_content").appendChild(overlaytext);
showPopup("show_progress_popup");
openPopup("show_progress_popup");
var trx = bitjs.transaction();
var utxoAmt = 0.0;
@ -711,7 +624,7 @@
}
if (utxoAmt <= refreshfee) {
notify("Insufficient Balance!", 'error');
hidePopup()
closePopup()
return;
}
var refreshAmt = utxoAmt - refreshfee;
@ -722,7 +635,7 @@
notify(`Refresh Unsuccessful! Rise the refresh fee and Try again!`, 'error');
else
notify(`Refresh UTXO successful\ntxid:${txid}`, 'success');
hidePopup()
closePopup()
}
function broadcastTx(signedTxHash) {